You've already forked pg_probackup
mirror of
https://github.com/postgrespro/pg_probackup.git
synced 2025-09-16 09:26:30 +02:00
PBCKP-819: Fixed the recovery-target-timeline command line parameter. It may contain 'current' or 'latest' keywords.
recovery-target can be 'latest' too for compatibility with previous versions
This commit is contained in:
@@ -98,7 +98,7 @@ static char *target_time = NULL;
|
||||
static char *target_xid = NULL;
|
||||
static char *target_lsn = NULL;
|
||||
static char *target_inclusive = NULL;
|
||||
static TimeLineID target_tli;
|
||||
static char *target_tli_string; /* timeline number, "current" or "latest"*/
|
||||
static char *target_stop;
|
||||
static bool target_immediate;
|
||||
static char *target_name = NULL;
|
||||
@@ -227,7 +227,7 @@ static ConfigOption cmd_options[] =
|
||||
{ 's', 137, "recovery-target-xid", &target_xid, SOURCE_CMD_STRICT },
|
||||
{ 's', 144, "recovery-target-lsn", &target_lsn, SOURCE_CMD_STRICT },
|
||||
{ 's', 138, "recovery-target-inclusive", &target_inclusive, SOURCE_CMD_STRICT },
|
||||
{ 'u', 139, "recovery-target-timeline", &target_tli, SOURCE_CMD_STRICT },
|
||||
{ 's', 139, "recovery-target-timeline", &target_tli_string, SOURCE_CMD_STRICT },
|
||||
{ 's', 157, "recovery-target", &target_stop, SOURCE_CMD_STRICT },
|
||||
{ 'f', 'T', "tablespace-mapping", opt_tablespace_map, SOURCE_CMD_STRICT },
|
||||
{ 'f', 155, "external-mapping", opt_externaldir_map, SOURCE_CMD_STRICT },
|
||||
@@ -285,7 +285,7 @@ static ConfigOption cmd_options[] =
|
||||
{ 's', 136, "time", &target_time, SOURCE_CMD_STRICT },
|
||||
{ 's', 137, "xid", &target_xid, SOURCE_CMD_STRICT },
|
||||
{ 's', 138, "inclusive", &target_inclusive, SOURCE_CMD_STRICT },
|
||||
{ 'u', 139, "timeline", &target_tli, SOURCE_CMD_STRICT },
|
||||
{ 's', 139, "timeline", &target_tli_string, SOURCE_CMD_STRICT },
|
||||
{ 's', 144, "lsn", &target_lsn, SOURCE_CMD_STRICT },
|
||||
{ 'b', 140, "immediate", &target_immediate, SOURCE_CMD_STRICT },
|
||||
|
||||
@@ -739,7 +739,7 @@ main(int argc, char *argv[])
|
||||
*/
|
||||
recovery_target_options =
|
||||
parseRecoveryTargetOptions(target_time, target_xid,
|
||||
target_inclusive, target_tli, target_lsn,
|
||||
target_inclusive, target_tli_string, target_lsn,
|
||||
(target_stop != NULL) ? target_stop :
|
||||
(target_immediate) ? "immediate" : NULL,
|
||||
target_name, target_action);
|
||||
|
@@ -564,6 +564,7 @@ typedef struct pgRecoveryTarget
|
||||
const char *target_stop;
|
||||
const char *target_name;
|
||||
const char *target_action;
|
||||
const char *target_tli_string; /* timeline number, "current" or "latest" from recovery_target_timeline option*/
|
||||
} pgRecoveryTarget;
|
||||
|
||||
/* Options needed for restore and validate commands */
|
||||
@@ -893,7 +894,7 @@ extern bool satisfy_recovery_target(const pgBackup *backup,
|
||||
const pgRecoveryTarget *rt);
|
||||
extern pgRecoveryTarget *parseRecoveryTargetOptions(
|
||||
const char *target_time, const char *target_xid,
|
||||
const char *target_inclusive, TimeLineID target_tli, const char* target_lsn,
|
||||
const char *target_inclusive, const char *target_tli_string, const char* target_lsn,
|
||||
const char *target_stop, const char *target_name,
|
||||
const char *target_action);
|
||||
|
||||
|
@@ -1332,8 +1332,10 @@ create_recovery_conf(InstanceState *instanceState, time_t backup_id,
|
||||
}
|
||||
|
||||
/* restore-target='latest' support */
|
||||
target_latest = rt->target_stop != NULL &&
|
||||
strcmp(rt->target_stop, "latest") == 0;
|
||||
target_latest = (rt->target_tli_string != NULL &&
|
||||
strcmp(rt->target_tli_string, "latest") == 0) ||
|
||||
(rt->target_stop != NULL &&
|
||||
strcmp(rt->target_stop, "latest") == 0);
|
||||
|
||||
target_immediate = rt->target_stop != NULL &&
|
||||
strcmp(rt->target_stop, "immediate") == 0;
|
||||
@@ -1359,6 +1361,13 @@ create_recovery_conf(InstanceState *instanceState, time_t backup_id,
|
||||
rt->xid_string || rt->lsn_string || rt->target_name ||
|
||||
target_immediate || target_latest || restore_command_provided)
|
||||
params->recovery_settings_mode = PITR_REQUESTED;
|
||||
/*
|
||||
* The recovery-target-timeline option can be 'latest' for streaming backups.
|
||||
* This operation requires a WAL archive for PITR.
|
||||
*/
|
||||
if (rt->target_tli && backup->stream && params->recovery_settings_mode != PITR_REQUESTED)
|
||||
elog(WARNING, "The '--recovery-target-timeline' option applied for STREAM backup. "
|
||||
"The timeline number will be ignored.");
|
||||
|
||||
elog(LOG, "----------------------------------------");
|
||||
|
||||
@@ -1438,14 +1447,20 @@ print_recovery_settings(InstanceState *instanceState, FILE *fp, pgBackup *backup
|
||||
fio_fprintf(fp, "recovery_target_timeline = '%u'\n", rt->target_tli);
|
||||
else
|
||||
{
|
||||
if (rt->target_tli_string)
|
||||
fio_fprintf(fp, "recovery_target_timeline = '%s'\n", rt->target_tli_string);
|
||||
else if (rt->target_stop && (strcmp(rt->target_stop, "latest") == 0))
|
||||
fio_fprintf(fp, "recovery_target_timeline = 'latest'\n");
|
||||
#if PG_VERSION_NUM >= 120000
|
||||
|
||||
else
|
||||
{
|
||||
/*
|
||||
* In PG12 default recovery target timeline was changed to 'latest', which
|
||||
* is extremely risky. Explicitly preserve old behavior of recovering to current
|
||||
* timneline for PG12.
|
||||
*/
|
||||
fio_fprintf(fp, "recovery_target_timeline = 'current'\n");
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -1877,7 +1892,7 @@ pgRecoveryTarget *
|
||||
parseRecoveryTargetOptions(const char *target_time,
|
||||
const char *target_xid,
|
||||
const char *target_inclusive,
|
||||
TimeLineID target_tli,
|
||||
const char *target_tli_string,
|
||||
const char *target_lsn,
|
||||
const char *target_stop,
|
||||
const char *target_name,
|
||||
@@ -1950,7 +1965,20 @@ parseRecoveryTargetOptions(const char *target_time,
|
||||
target_inclusive);
|
||||
}
|
||||
|
||||
rt->target_tli = target_tli;
|
||||
rt->target_tli_string = target_tli_string;
|
||||
rt->target_tli = 0;
|
||||
/* target_tli can contains timeline number, "current" or "latest" */
|
||||
if(target_tli_string && strcmp(target_tli_string, "current") != 0 && strcmp(target_tli_string, "latest") != 0)
|
||||
{
|
||||
errno = 0;
|
||||
rt->target_tli = strtoul(target_tli_string, NULL, 10);
|
||||
if (errno == EINVAL || errno == ERANGE || !rt->target_tli)
|
||||
{
|
||||
elog(ERROR, "Invalid value for '--recovery-target-timeline' option '%s'",
|
||||
target_tli_string);
|
||||
}
|
||||
}
|
||||
|
||||
if (target_stop)
|
||||
{
|
||||
if ((strcmp(target_stop, "immediate") != 0)
|
||||
|
@@ -1916,7 +1916,9 @@ class RestoreTest(ProbackupTest, unittest.TestCase):
|
||||
with open(recovery_conf, 'r') as f:
|
||||
self.assertIn("recovery_target = 'immediate'", f.read())
|
||||
|
||||
# @unittest.skip("skip")
|
||||
# Skipped, because default recovery_target_timeline is 'current'
|
||||
# Before PBCKP-598 the --recovery-target=latest' option did not work and this test allways passed
|
||||
@unittest.skip("skip")
|
||||
def test_restore_target_latest_archive(self):
|
||||
"""
|
||||
make sure that recovery_target 'latest'
|
||||
@@ -3818,3 +3820,108 @@ class RestoreTest(ProbackupTest, unittest.TestCase):
|
||||
wal_path=os.path.join(node.data_dir, "pg_xlog")
|
||||
|
||||
self.assertEqual(os.path.islink(wal_path), True)
|
||||
|
||||
# @unittest.skip("skip")
|
||||
def test_restore_to_latest_timeline(self):
|
||||
"""recovery to latest timeline"""
|
||||
node = self.make_simple_node(
|
||||
base_dir=os.path.join(self.module_name, self.fname, 'node'),
|
||||
initdb_params=['--data-checksums'])
|
||||
|
||||
backup_dir = os.path.join(self.tmp_path, self.module_name, self.fname, 'backup')
|
||||
self.init_pb(backup_dir)
|
||||
self.add_instance(backup_dir, 'node', node)
|
||||
self.set_archiving(backup_dir, 'node', node)
|
||||
node.slow_start()
|
||||
|
||||
|
||||
node.pgbench_init(scale=2)
|
||||
|
||||
before1 = node.table_checksum("pgbench_branches")
|
||||
backup_id = self.backup_node(backup_dir, 'node', node)
|
||||
|
||||
node.stop()
|
||||
node.cleanup()
|
||||
|
||||
self.assertIn(
|
||||
"INFO: Restore of backup {0} completed.".format(backup_id),
|
||||
self.restore_node(
|
||||
backup_dir, 'node', node, options=["-j", "4"]),
|
||||
'\n Unexpected Error Message: {0}\n CMD: {1}'.format(
|
||||
repr(self.output), self.cmd))
|
||||
|
||||
|
||||
|
||||
node.slow_start()
|
||||
pgbench = node.pgbench(
|
||||
stdout=subprocess.PIPE, stderr=subprocess.STDOUT,
|
||||
options=['-T', '10', '-c', '2', '--no-vacuum'])
|
||||
pgbench.wait()
|
||||
pgbench.stdout.close()
|
||||
|
||||
before2 = node.table_checksum("pgbench_branches")
|
||||
self.backup_node(backup_dir, 'node', node)
|
||||
|
||||
node.stop()
|
||||
node.cleanup()
|
||||
# restore from first backup
|
||||
restore_result = self.restore_node(backup_dir, 'node', node,
|
||||
options=[
|
||||
"-j", "4", "--recovery-target-timeline=latest", "-i", backup_id]
|
||||
)
|
||||
self.assertIn(
|
||||
"INFO: Restore of backup {0} completed.".format(backup_id), restore_result,
|
||||
'\n Unexpected Error Message: {0}\n CMD: {1}'.format(
|
||||
repr(self.output), self.cmd))
|
||||
|
||||
# check recovery_target_timeline option in the recovery_conf
|
||||
recovery_target_timeline = self.get_recovery_conf(node)["recovery_target_timeline"]
|
||||
self.assertEqual(recovery_target_timeline, "latest")
|
||||
# check recovery-target=latest option for compatibility with previous versions
|
||||
node.cleanup()
|
||||
restore_result = self.restore_node(backup_dir, 'node', node,
|
||||
options=[
|
||||
"-j", "4", "--recovery-target=latest", "-i", backup_id]
|
||||
)
|
||||
self.assertIn(
|
||||
"INFO: Restore of backup {0} completed.".format(backup_id), restore_result,
|
||||
'\n Unexpected Error Message: {0}\n CMD: {1}'.format(
|
||||
repr(self.output), self.cmd))
|
||||
|
||||
# check recovery_target_timeline option in the recovery_conf
|
||||
recovery_target_timeline = self.get_recovery_conf(node)["recovery_target_timeline"]
|
||||
self.assertEqual(recovery_target_timeline, "latest")
|
||||
|
||||
# start postgres and promote wal files to latest timeline
|
||||
node.slow_start()
|
||||
|
||||
# check for the latest updates
|
||||
after = node.table_checksum("pgbench_branches")
|
||||
self.assertEqual(before2, after)
|
||||
|
||||
# checking recovery_target_timeline=current is the default option
|
||||
if self.pg_config_version >= self.version_to_num('12.0'):
|
||||
node.stop()
|
||||
node.cleanup()
|
||||
|
||||
# restore from first backup
|
||||
restore_result = self.restore_node(backup_dir, 'node', node,
|
||||
options=[
|
||||
"-j", "4", "-i", backup_id]
|
||||
)
|
||||
|
||||
self.assertIn(
|
||||
"INFO: Restore of backup {0} completed.".format(backup_id), restore_result,
|
||||
'\n Unexpected Error Message: {0}\n CMD: {1}'.format(
|
||||
repr(self.output), self.cmd))
|
||||
|
||||
# check recovery_target_timeline option in the recovery_conf
|
||||
recovery_target_timeline = self.get_recovery_conf(node)["recovery_target_timeline"]
|
||||
self.assertEqual(recovery_target_timeline, "current")
|
||||
|
||||
# start postgres with current timeline
|
||||
node.slow_start()
|
||||
|
||||
# check for the current updates
|
||||
after = node.table_checksum("pgbench_branches")
|
||||
self.assertEqual(before1, after)
|
||||
|
Reference in New Issue
Block a user