1
0
mirror of https://github.com/postgrespro/pg_probackup.git synced 2024-11-28 09:33:54 +02:00

[pbckp-128] dry-run option for catchup (#477)

* Added dry-run option for catchup. Run catchup without affect on the files and WAL
This commit is contained in:
dlepikhova 2022-06-01 12:49:09 +05:00 committed by GitHub
parent 7be2e738a9
commit 884e8b09f3
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 219 additions and 31 deletions

View File

@ -2,7 +2,7 @@
* *
* catchup.c: sync DB cluster * catchup.c: sync DB cluster
* *
* Copyright (c) 2021, Postgres Professional * Copyright (c) 2022, Postgres Professional
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
@ -507,15 +507,19 @@ catchup_multithreaded_copy(int num_threads,
/* Run threads */ /* Run threads */
thread_interrupted = false; thread_interrupted = false;
threads = (pthread_t *) palloc(sizeof(pthread_t) * num_threads); threads = (pthread_t *) palloc(sizeof(pthread_t) * num_threads);
if (!dry_run)
{
for (i = 0; i < num_threads; i++) for (i = 0; i < num_threads; i++)
{ {
elog(VERBOSE, "Start thread num: %i", i); elog(VERBOSE, "Start thread num: %i", i);
pthread_create(&threads[i], NULL, &catchup_thread_runner, &(threads_args[i])); pthread_create(&threads[i], NULL, &catchup_thread_runner, &(threads_args[i]));
} }
}
/* Wait threads */ /* Wait threads */
for (i = 0; i < num_threads; i++) for (i = 0; i < num_threads; i++)
{ {
if (!dry_run)
pthread_join(threads[i], NULL); pthread_join(threads[i], NULL);
all_threads_successful &= threads_args[i].completed; all_threads_successful &= threads_args[i].completed;
transfered_bytes_result += threads_args[i].transfered_bytes; transfered_bytes_result += threads_args[i].transfered_bytes;
@ -706,9 +710,14 @@ do_catchup(const char *source_pgdata, const char *dest_pgdata, int num_threads,
/* Start stream replication */ /* Start stream replication */
join_path_components(dest_xlog_path, dest_pgdata, PG_XLOG_DIR); join_path_components(dest_xlog_path, dest_pgdata, PG_XLOG_DIR);
if (!dry_run)
{
fio_mkdir(dest_xlog_path, DIR_PERMISSION, FIO_LOCAL_HOST); fio_mkdir(dest_xlog_path, DIR_PERMISSION, FIO_LOCAL_HOST);
start_WAL_streaming(source_conn, dest_xlog_path, &instance_config.conn_opt, start_WAL_streaming(source_conn, dest_xlog_path, &instance_config.conn_opt,
current.start_lsn, current.tli, false); current.start_lsn, current.tli, false);
}
else
elog(INFO, "WAL streaming skipping with --dry-run option");
source_filelist = parray_new(); source_filelist = parray_new();
@ -820,8 +829,8 @@ do_catchup(const char *source_pgdata, const char *dest_pgdata, int num_threads,
char dirpath[MAXPGPATH]; char dirpath[MAXPGPATH];
join_path_components(dirpath, dest_pgdata, file->rel_path); join_path_components(dirpath, dest_pgdata, file->rel_path);
elog(VERBOSE, "Create directory '%s'", dirpath); elog(VERBOSE, "Create directory '%s'", dirpath);
if (!dry_run)
fio_mkdir(dirpath, DIR_PERMISSION, FIO_LOCAL_HOST); fio_mkdir(dirpath, DIR_PERMISSION, FIO_LOCAL_HOST);
} }
else else
@ -853,6 +862,8 @@ do_catchup(const char *source_pgdata, const char *dest_pgdata, int num_threads,
elog(VERBOSE, "Create directory \"%s\" and symbolic link \"%s\"", elog(VERBOSE, "Create directory \"%s\" and symbolic link \"%s\"",
linked_path, to_path); linked_path, to_path);
if (!dry_run)
{
/* create tablespace directory */ /* create tablespace directory */
if (fio_mkdir(linked_path, file->mode, FIO_LOCAL_HOST) != 0) if (fio_mkdir(linked_path, file->mode, FIO_LOCAL_HOST) != 0)
elog(ERROR, "Could not create tablespace directory \"%s\": %s", elog(ERROR, "Could not create tablespace directory \"%s\": %s",
@ -864,6 +875,7 @@ do_catchup(const char *source_pgdata, const char *dest_pgdata, int num_threads,
linked_path, to_path, strerror(errno)); linked_path, to_path, strerror(errno));
} }
} }
}
/* /*
* find pg_control file (in already sorted source_filelist) * find pg_control file (in already sorted source_filelist)
@ -930,7 +942,10 @@ do_catchup(const char *source_pgdata, const char *dest_pgdata, int num_threads,
char fullpath[MAXPGPATH]; char fullpath[MAXPGPATH];
join_path_components(fullpath, dest_pgdata, file->rel_path); join_path_components(fullpath, dest_pgdata, file->rel_path);
if (!dry_run)
{
fio_delete(file->mode, fullpath, FIO_LOCAL_HOST); fio_delete(file->mode, fullpath, FIO_LOCAL_HOST);
}
elog(VERBOSE, "Deleted file \"%s\"", fullpath); elog(VERBOSE, "Deleted file \"%s\"", fullpath);
/* shrink dest pgdata list */ /* shrink dest pgdata list */
@ -961,7 +976,7 @@ do_catchup(const char *source_pgdata, const char *dest_pgdata, int num_threads,
catchup_isok = transfered_datafiles_bytes != -1; catchup_isok = transfered_datafiles_bytes != -1;
/* at last copy control file */ /* at last copy control file */
if (catchup_isok) if (catchup_isok && !dry_run)
{ {
char from_fullpath[MAXPGPATH]; char from_fullpath[MAXPGPATH];
char to_fullpath[MAXPGPATH]; char to_fullpath[MAXPGPATH];
@ -972,7 +987,7 @@ do_catchup(const char *source_pgdata, const char *dest_pgdata, int num_threads,
transfered_datafiles_bytes += source_pg_control_file->size; transfered_datafiles_bytes += source_pg_control_file->size;
} }
if (!catchup_isok) if (!catchup_isok && !dry_run)
{ {
char pretty_time[20]; char pretty_time[20];
char pretty_transfered_data_bytes[20]; char pretty_transfered_data_bytes[20];
@ -1010,14 +1025,18 @@ do_catchup(const char *source_pgdata, const char *dest_pgdata, int num_threads,
pg_free(stop_backup_query_text); pg_free(stop_backup_query_text);
} }
if (!dry_run)
wait_wal_and_calculate_stop_lsn(dest_xlog_path, stop_backup_result.lsn, &current); wait_wal_and_calculate_stop_lsn(dest_xlog_path, stop_backup_result.lsn, &current);
#if PG_VERSION_NUM >= 90600 #if PG_VERSION_NUM >= 90600
/* Write backup_label */ /* Write backup_label */
Assert(stop_backup_result.backup_label_content != NULL); Assert(stop_backup_result.backup_label_content != NULL);
if (!dry_run)
{
pg_stop_backup_write_file_helper(dest_pgdata, PG_BACKUP_LABEL_FILE, "backup label", pg_stop_backup_write_file_helper(dest_pgdata, PG_BACKUP_LABEL_FILE, "backup label",
stop_backup_result.backup_label_content, stop_backup_result.backup_label_content_len, stop_backup_result.backup_label_content, stop_backup_result.backup_label_content_len,
NULL); NULL);
}
free(stop_backup_result.backup_label_content); free(stop_backup_result.backup_label_content);
stop_backup_result.backup_label_content = NULL; stop_backup_result.backup_label_content = NULL;
stop_backup_result.backup_label_content_len = 0; stop_backup_result.backup_label_content_len = 0;
@ -1040,6 +1059,7 @@ do_catchup(const char *source_pgdata, const char *dest_pgdata, int num_threads,
#endif #endif
/* wait for end of wal streaming and calculate wal size transfered */ /* wait for end of wal streaming and calculate wal size transfered */
if (!dry_run)
{ {
parray *wal_files_list = NULL; parray *wal_files_list = NULL;
wal_files_list = parray_new(); wal_files_list = parray_new();
@ -1091,17 +1111,17 @@ do_catchup(const char *source_pgdata, const char *dest_pgdata, int num_threads,
} }
/* Sync all copied files unless '--no-sync' flag is used */ /* Sync all copied files unless '--no-sync' flag is used */
if (sync_dest_files) if (sync_dest_files && !dry_run)
catchup_sync_destination_files(dest_pgdata, FIO_LOCAL_HOST, source_filelist, source_pg_control_file); catchup_sync_destination_files(dest_pgdata, FIO_LOCAL_HOST, source_filelist, source_pg_control_file);
else else
elog(WARNING, "Files are not synced to disk"); elog(WARNING, "Files are not synced to disk");
/* Cleanup */ /* Cleanup */
if (dest_filelist) if (dest_filelist && !dry_run)
{ {
parray_walk(dest_filelist, pgFileFree); parray_walk(dest_filelist, pgFileFree);
parray_free(dest_filelist);
} }
parray_free(dest_filelist);
parray_walk(source_filelist, pgFileFree); parray_walk(source_filelist, pgFileFree);
parray_free(source_filelist); parray_free(source_filelist);
pgFileFree(source_pg_control_file); pgFileFree(source_pg_control_file);

View File

@ -261,6 +261,7 @@ help_pg_probackup(void)
printf(_(" [--remote-proto] [--remote-host]\n")); printf(_(" [--remote-proto] [--remote-host]\n"));
printf(_(" [--remote-port] [--remote-path] [--remote-user]\n")); printf(_(" [--remote-port] [--remote-path] [--remote-user]\n"));
printf(_(" [--ssh-options]\n")); printf(_(" [--ssh-options]\n"));
printf(_(" [--dry-run]\n"));
printf(_(" [--help]\n")); printf(_(" [--help]\n"));
if ((PROGRAM_URL || PROGRAM_EMAIL)) if ((PROGRAM_URL || PROGRAM_EMAIL))
@ -1047,6 +1048,7 @@ help_catchup(void)
printf(_(" [--remote-proto] [--remote-host]\n")); printf(_(" [--remote-proto] [--remote-host]\n"));
printf(_(" [--remote-port] [--remote-path] [--remote-user]\n")); printf(_(" [--remote-port] [--remote-path] [--remote-user]\n"));
printf(_(" [--ssh-options]\n")); printf(_(" [--ssh-options]\n"));
printf(_(" [--dry-run]\n"));
printf(_(" [--help]\n\n")); printf(_(" [--help]\n\n"));
printf(_(" -b, --backup-mode=catchup-mode catchup mode=FULL|DELTA|PTRACK\n")); printf(_(" -b, --backup-mode=catchup-mode catchup mode=FULL|DELTA|PTRACK\n"));
@ -1081,4 +1083,6 @@ help_catchup(void)
printf(_(" --remote-user=username user name for ssh connection (default: current user)\n")); printf(_(" --remote-user=username user name for ssh connection (default: current user)\n"));
printf(_(" --ssh-options=ssh_options additional ssh options (default: none)\n")); printf(_(" --ssh-options=ssh_options additional ssh options (default: none)\n"));
printf(_(" (example: --ssh-options='-c cipher_spec -F configfile')\n\n")); printf(_(" (example: --ssh-options='-c cipher_spec -F configfile')\n\n"));
printf(_(" --dry-run perform a trial run without any changes\n\n"));
} }

View File

@ -1455,3 +1455,157 @@ class CatchupTest(ProbackupTest, unittest.TestCase):
dst_pg.stop() dst_pg.stop()
#self.assertEqual(1, 0, 'Stop test') #self.assertEqual(1, 0, 'Stop test')
self.del_test_dir(module_name, self.fname) self.del_test_dir(module_name, self.fname)
#########################################
# --dry-run
#########################################
def test_dry_run_catchup_full(self):
"""
Test dry-run option for full catchup
"""
# preparation 1: source
src_pg = self.make_simple_node(
base_dir = os.path.join(module_name, self.fname, 'src'),
set_replication = True
)
src_pg.slow_start()
# preparation 2: make clean shutdowned lagging behind replica
dst_pg = self.make_empty_node(os.path.join(module_name, self.fname, 'dst'))
src_pg.pgbench_init(scale = 10)
pgbench = src_pg.pgbench(options=['-T', '10', '--no-vacuum'])
pgbench.wait()
# save the condition before dry-run
content_before = self.pgdata_content(dst_pg.data_dir)
# do full catchup
self.catchup_node(
backup_mode = 'FULL',
source_pgdata = src_pg.data_dir,
destination_node = dst_pg,
options = ['-d', 'postgres', '-p', str(src_pg.port), '--stream', '--dry-run']
)
# compare data dirs before and after catchup
self.compare_pgdata(
content_before,
self.pgdata_content(dst_pg.data_dir)
)
# Cleanup
src_pg.stop()
self.del_test_dir(module_name, self.fname)
def test_dry_run_catchup_ptrack(self):
"""
Test dry-run option for catchup in incremental ptrack mode
"""
if not self.ptrack:
return unittest.skip('Skipped because ptrack support is disabled')
# preparation 1: source
src_pg = self.make_simple_node(
base_dir = os.path.join(module_name, self.fname, 'src'),
set_replication = True,
ptrack_enable = True,
initdb_params = ['--data-checksums']
)
src_pg.slow_start()
src_pg.safe_psql("postgres", "CREATE EXTENSION ptrack")
src_pg.pgbench_init(scale = 10)
pgbench = src_pg.pgbench(options=['-T', '10', '--no-vacuum'])
pgbench.wait()
# preparation 2: make clean shutdowned lagging behind replica
dst_pg = self.make_empty_node(os.path.join(module_name, self.fname, 'dst'))
self.catchup_node(
backup_mode = 'FULL',
source_pgdata = src_pg.data_dir,
destination_node = dst_pg,
options = ['-d', 'postgres', '-p', str(src_pg.port), '--stream']
)
self.set_replica(src_pg, dst_pg)
dst_options = {}
dst_options['port'] = str(dst_pg.port)
self.set_auto_conf(dst_pg, dst_options)
dst_pg.slow_start(replica = True)
dst_pg.stop()
# save the condition before dry-run
content_before = self.pgdata_content(dst_pg.data_dir)
# do incremental catchup
self.catchup_node(
backup_mode = 'PTRACK',
source_pgdata = src_pg.data_dir,
destination_node = dst_pg,
options = ['-d', 'postgres', '-p', str(src_pg.port), '--stream', '--dry-run']
)
# compare data dirs before and after cathup
self.compare_pgdata(
content_before,
self.pgdata_content(dst_pg.data_dir)
)
# Cleanup
src_pg.stop()
self.del_test_dir(module_name, self.fname)
def test_dry_run_catchup_delta(self):
"""
Test dry-run option for catchup in incremental delta mode
"""
# preparation 1: source
src_pg = self.make_simple_node(
base_dir = os.path.join(module_name, self.fname, 'src'),
set_replication = True,
initdb_params = ['--data-checksums'],
pg_options = { 'wal_log_hints': 'on' }
)
src_pg.slow_start()
src_pg.pgbench_init(scale = 10)
pgbench = src_pg.pgbench(options=['-T', '10', '--no-vacuum'])
pgbench.wait()
# preparation 2: make clean shutdowned lagging behind replica
dst_pg = self.make_empty_node(os.path.join(module_name, self.fname, 'dst'))
self.catchup_node(
backup_mode = 'FULL',
source_pgdata = src_pg.data_dir,
destination_node = dst_pg,
options = ['-d', 'postgres', '-p', str(src_pg.port), '--stream']
)
self.set_replica(src_pg, dst_pg)
dst_options = {}
dst_options['port'] = str(dst_pg.port)
self.set_auto_conf(dst_pg, dst_options)
dst_pg.slow_start(replica = True)
dst_pg.stop()
# save the condition before dry-run
content_before = self.pgdata_content(dst_pg.data_dir)
# do delta catchup
self.catchup_node(
backup_mode = 'DELTA',
source_pgdata = src_pg.data_dir,
destination_node = dst_pg,
options = ['-d', 'postgres', '-p', str(src_pg.port), '--stream', "--dry-run"]
)
# compare data dirs before and after cathup
self.compare_pgdata(
content_before,
self.pgdata_content(dst_pg.data_dir)
)
# Cleanup
src_pg.stop()
self.del_test_dir(module_name, self.fname)

View File

@ -178,6 +178,7 @@ pg_probackup - utility to manage backup/recovery of PostgreSQL database.
[--remote-proto] [--remote-host] [--remote-proto] [--remote-host]
[--remote-port] [--remote-path] [--remote-user] [--remote-port] [--remote-path] [--remote-user]
[--ssh-options] [--ssh-options]
[--dry-run]
[--help] [--help]
Read the website for details <https://github.com/postgrespro/pg_probackup>. Read the website for details <https://github.com/postgrespro/pg_probackup>.

View File

@ -100,11 +100,20 @@ source pyenv/bin/activate
pip3 install testgres pip3 install testgres
echo "############### Testing:" echo "############### Testing:"
echo PG_PROBACKUP_PARANOIA=${PG_PROBACKUP_PARANOIA}
echo ARCHIVE_COMPRESSION=${ARCHIVE_COMPRESSION}
echo PGPROBACKUPBIN_OLD=${PGPROBACKUPBIN_OLD}
echo PGPROBACKUPBIN=${PGPROBACKUPBIN}
echo PGPROBACKUP_SSH_REMOTE=${PGPROBACKUP_SSH_REMOTE}
echo PGPROBACKUP_GDB=${PGPROBACKUP_GDB}
echo PG_PROBACKUP_PTRACK=${PG_PROBACKUP_PTRACK}
if [ "$MODE" = "basic" ]; then if [ "$MODE" = "basic" ]; then
export PG_PROBACKUP_TEST_BASIC=ON export PG_PROBACKUP_TEST_BASIC=ON
echo PG_PROBACKUP_TEST_BASIC=${PG_PROBACKUP_TEST_BASIC}
python3 -m unittest -v tests python3 -m unittest -v tests
python3 -m unittest -v tests.init python3 -m unittest -v tests.init
else else
echo PG_PROBACKUP_TEST_BASIC=${PG_PROBACKUP_TEST_BASIC}
python3 -m unittest -v tests.$MODE python3 -m unittest -v tests.$MODE
fi fi