1
0
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:
Victor Spirin
2023-12-06 20:32:35 +03:00
parent 1ee26f9912
commit d26df12019
4 changed files with 147 additions and 11 deletions

View File

@@ -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);

View File

@@ -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);

View File

@@ -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)

View File

@@ -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)