Skip to content

Commit 7244ee3

Browse files
committed
[Issue #138] always set "restore_command" in recovery.conf if "--restore-command" option is provided
1 parent e0871de commit 7244ee3

File tree

2 files changed

+85
-36
lines changed

2 files changed

+85
-36
lines changed

src/restore.c

Lines changed: 59 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -842,7 +842,10 @@ restore_files(void *arg)
842842
return NULL;
843843
}
844844

845-
/* Create recovery.conf with given recovery target parameters */
845+
/*
846+
* Create recovery.conf (probackup_recovery.conf in case of PG12)
847+
* with given recovery target parameters
848+
*/
846849
static void
847850
create_recovery_conf(time_t backup_id,
848851
pgRecoveryTarget *rt,
@@ -851,9 +854,17 @@ create_recovery_conf(time_t backup_id,
851854
{
852855
char path[MAXPGPATH];
853856
FILE *fp;
854-
bool need_restore_conf;
857+
bool pitr_requested;
855858
bool target_latest;
856859
bool target_immediate;
860+
bool restore_command_provided = false;
861+
char restore_command_guc[16384];
862+
863+
if (instance_config.restore_command &&
864+
(pg_strcasecmp(instance_config.restore_command, "none") != 0))
865+
{
866+
restore_command_provided = true;
867+
}
857868

858869
/* restore-target='latest' support */
859870
target_latest = rt->target_stop != NULL &&
@@ -862,16 +873,39 @@ create_recovery_conf(time_t backup_id,
862873
target_immediate = rt->target_stop != NULL &&
863874
strcmp(rt->target_stop, "immediate") == 0;
864875

865-
need_restore_conf = !backup->stream || rt->time_string ||
876+
/*
877+
* Note that setting restore_command alone interpreted
878+
* as PITR with target - "until all available WAL is replayed".
879+
* We do this because of the following case:
880+
* The user is restoring STREAM backup as replica but
881+
* also relies on WAL archive to catch-up with master.
882+
* If restore_command is provided, then it should be
883+
* added to recovery config.
884+
* In this scenario, "would be" replica will replay
885+
* all WAL segments available in WAL archive, after that
886+
* it will try to connect to master via repprotocol.
887+
*
888+
* The risk is obvious, what if masters current state is
889+
* in "the past" relatively to latest state in the archive?
890+
* We will get a replica that is "in the future" to the master.
891+
* We accept this risk because its probability is low.
892+
*/
893+
pitr_requested = !backup->stream || rt->time_string ||
866894
rt->xid_string || rt->lsn_string || rt->target_name ||
867-
target_immediate || target_latest;
895+
target_immediate || target_latest || restore_command_provided;
868896

869897
/* No need to generate recovery.conf at all. */
870-
if (!(need_restore_conf || params->restore_as_replica))
898+
if (!(pitr_requested || params->restore_as_replica))
871899
{
872900
/*
873901
* Restoring STREAM backup without PITR and not as replica,
874902
* recovery.signal and standby.signal for PG12 are not needed
903+
*
904+
* We do not add "include" option in this case because
905+
* here we are creating empty "probackup_recovery.conf"
906+
* to handle possible already existing "include"
907+
* directive pointing to "probackup_recovery.conf".
908+
* If don`t do that, recovery will fail.
875909
*/
876910
pg12_recovery_config(backup, false);
877911
return;
@@ -901,13 +935,10 @@ create_recovery_conf(time_t backup_id,
901935
#endif
902936

903937
/* construct restore_command */
904-
if (need_restore_conf)
938+
if (pitr_requested)
905939
{
906-
char restore_command_guc[16384];
907-
908-
/* If restore_command is provided, use it */
909-
if (instance_config.restore_command &&
910-
(pg_strcasecmp(instance_config.restore_command, "none") != 0))
940+
/* If restore_command is provided, use it. Otherwise construct it from scratch. */
941+
if (restore_command_provided)
911942
sprintf(restore_command_guc, "%s", instance_config.restore_command);
912943
else
913944
{
@@ -937,10 +968,6 @@ create_recovery_conf(time_t backup_id,
937968
}
938969
}
939970

940-
elog(LOG, "Setting restore_command to '%s'", restore_command_guc);
941-
fio_fprintf(fp, "restore_command = '%s'\n",
942-
restore_command_guc);
943-
944971
/*
945972
* We've already checked that only one of the four following mutually
946973
* exclusive options is specified, so the order of calls is insignificant.
@@ -996,13 +1023,29 @@ create_recovery_conf(time_t backup_id,
9961023
fio_fprintf(fp, "primary_conninfo = '%s'\n", backup->primary_conninfo);
9971024
}
9981025

1026+
if (pitr_requested)
1027+
{
1028+
elog(LOG, "Setting restore_command to '%s'", restore_command_guc);
1029+
fio_fprintf(fp, "restore_command = '%s'\n", restore_command_guc);
1030+
}
1031+
9991032
if (fio_fflush(fp) != 0 ||
10001033
fio_fclose(fp))
10011034
elog(ERROR, "cannot write file \"%s\": %s", path,
10021035
strerror(errno));
10031036

10041037
#if PG_VERSION_NUM >= 120000
1005-
if (need_restore_conf)
1038+
/*
1039+
* Create "recovery.signal" to mark this recovery as PITR for PostgreSQL.
1040+
* In older versions presense of recovery.conf alone was enough.
1041+
* To keep behaviour consistent with older versions,
1042+
* we are forced to create "recovery.signal"
1043+
* even when only restore_command is provided.
1044+
* Presense of "recovery.signal" by itself determine only
1045+
* one thing: do PostgreSQL must switch to a new timeline
1046+
* after successfull recovery or not?
1047+
*/
1048+
if (pitr_requested)
10061049
{
10071050
elog(LOG, "creating recovery.signal file");
10081051
snprintf(path, lengthof(path), "%s/recovery.signal", instance_config.pgdata);

tests/restore.py

Lines changed: 26 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -3247,8 +3247,8 @@ def test_missing_database_map(self):
32473247
# Clean after yourself
32483248
self.del_test_dir(module_name, fname)
32493249

3250-
@unittest.skip("skip")
3251-
def test_stream_restore_command_options(self):
3250+
# @unittest.skip("skip")
3251+
def test_stream_restore_command_option(self):
32523252
"""
32533253
correct handling of restore command options
32543254
when restoring STREAM backup
@@ -3291,40 +3291,46 @@ def test_stream_restore_command_options(self):
32913291

32923292
node.pgbench_init(scale=1)
32933293

3294+
node.safe_psql(
3295+
'postgres',
3296+
'CHECKPOINT')
3297+
3298+
node.safe_psql(
3299+
'postgres',
3300+
'create table t1()')
3301+
32943302
# restore backup
32953303
node.cleanup()
32963304
shutil.rmtree(os.path.join(node.logs_dir))
32973305

3298-
# self.restore_node(backup_dir, 'node', node)
32993306
restore_cmd = self.get_restore_command(backup_dir, 'node', node)
33003307

33013308
self.restore_node(
33023309
backup_dir, 'node', node,
33033310
options=[
3304-
'--restore-command={0}'.format(restore_cmd),
3305-
'--recovery-target-action=pause',
3306-
'--recovery-target=latest'])
3311+
'--restore-command={0}'.format(restore_cmd)])
33073312

33083313
self.assertTrue(
33093314
os.path.isfile(recovery_conf),
3310-
"File {0} do not exists".format(recovery_conf))
3315+
"File '{0}' do not exists".format(recovery_conf))
3316+
3317+
if self.get_version(node) >= self.version_to_num('12.0'):
3318+
recovery_signal = os.path.join(node.data_dir, 'recovery.signal')
3319+
self.assertTrue(
3320+
os.path.isfile(recovery_signal),
3321+
"File '{0}' do not exists".format(recovery_signal))
33113322

33123323
node.slow_start()
33133324

3314-
exit(1)
3325+
node.safe_psql(
3326+
'postgres',
3327+
'select * from t1')
3328+
3329+
timeline_id = node.safe_psql(
3330+
'postgres',
3331+
'select timeline_id from pg_control_checkpoint()').rstrip()
33153332

3316-
# self.set_config(
3317-
# backup_dir ,'node',
3318-
# options=['--restore-command="cp {0}/%f %p"'.format(wal_dir)])
3319-
#
3320-
# # restore delta backup
3321-
# node.cleanup()
3322-
# self.restore_node(
3323-
# backup_dir, 'node', node, options=['--recovery-target=immediate'])
3324-
#
3325-
# self.assertTrue(
3326-
# os.path.isfile(recovery_conf),
3327-
# "File {0} do not exists".format(recovery_conf))
3333+
self.assertEqual('2', timeline_id)
33283334

33293335
# Clean after yourself
33303336
self.del_test_dir(module_name, fname)

0 commit comments

Comments
 (0)