1
0
mirror of https://github.com/postgres/postgres.git synced 2025-12-16 16:42:29 +03:00

Allow pg_rewind to use a standby server as the source system.

Using a hot standby server as the source has not been possible, because
pg_rewind creates a temporary table in the source system, to hold the
list of file ranges that need to be fetched. Refactor it to queue up the
file fetch requests in pg_rewind's memory, so that the temporary table
is no longer needed.

Also update the logic to compute 'minRecoveryPoint' correctly, when the
source is a standby server.

Reviewed-by: Kyotaro Horiguchi, Soumyadeep Chakraborty
Discussion: https://www.postgresql.org/message-id/0c5b3783-af52-3ee5-f8fa-6e794061f70d%40iki.fi
This commit is contained in:
Heikki Linnakangas
2020-11-12 14:52:24 +02:00
parent 1b2b19f758
commit 9c4f5192f6
5 changed files with 484 additions and 108 deletions

View File

@@ -50,6 +50,7 @@ static void disconnect_atexit(void);
static ControlFileData ControlFile_target;
static ControlFileData ControlFile_source;
static ControlFileData ControlFile_source_after;
const char *progname;
int WalSegSz;
@@ -486,6 +487,8 @@ perform_rewind(filemap_t *filemap, rewind_source *source,
XLogRecPtr endrec;
TimeLineID endtli;
ControlFileData ControlFile_new;
size_t size;
char *buffer;
/*
* Execute the actions in the file map, fetching data from the source
@@ -552,40 +555,104 @@ perform_rewind(filemap_t *filemap, rewind_source *source,
}
}
/*
* We've now copied the list of file ranges that we need to fetch to the
* temporary table. Now, actually fetch all of those ranges.
*/
/* Complete any remaining range-fetches that we queued up above. */
source->finish_fetch(source);
close_target_file();
progress_report(true);
/*
* Fetch the control file from the source last. This ensures that the
* minRecoveryPoint is up-to-date.
*/
buffer = source->fetch_file(source, "global/pg_control", &size);
digestControlFile(&ControlFile_source_after, buffer, size);
pg_free(buffer);
/*
* Sanity check: If the source is a local system, the control file should
* not have changed since we started.
*
* XXX: We assume it hasn't been modified, but actually, what could go
* wrong? The logic handles a libpq source that's modified concurrently,
* why not a local datadir?
*/
if (datadir_source &&
memcmp(&ControlFile_source, &ControlFile_source_after,
sizeof(ControlFileData)) != 0)
{
pg_fatal("source system was modified while pg_rewind was running");
}
if (showprogress)
pg_log_info("creating backup label and updating control file");
/*
* Create a backup label file, to tell the target where to begin the WAL
* replay. Normally, from the last common checkpoint between the source
* and the target. But if the source is a standby server, it's possible
* that the last common checkpoint is *after* the standby's restartpoint.
* That implies that the source server has applied the checkpoint record,
* but hasn't perfomed a corresponding restartpoint yet. Make sure we
* start at the restartpoint's redo point in that case.
*
* Use the old version of the source's control file for this. The server
* might have finished the restartpoint after we started copying files,
* but we must begin from the redo point at the time that started copying.
*/
if (ControlFile_source.checkPointCopy.redo < chkptredo)
{
chkptredo = ControlFile_source.checkPointCopy.redo;
chkpttli = ControlFile_source.checkPointCopy.ThisTimeLineID;
chkptrec = ControlFile_source.checkPoint;
}
createBackupLabel(chkptredo, chkpttli, chkptrec);
/*
* Update control file of target. Make it ready to perform archive
* recovery when restarting.
*
* Like in an online backup, it's important that we replay all the WAL
* that was generated while we copied the files over. To enforce that, set
* 'minRecoveryPoint' in the control file.
* Update control file of target, to tell the target how far it must
* replay the WAL (minRecoveryPoint).
*/
memcpy(&ControlFile_new, &ControlFile_source, sizeof(ControlFileData));
if (connstr_source)
{
endrec = source->get_current_wal_insert_lsn(source);
endtli = ControlFile_source.checkPointCopy.ThisTimeLineID;
/*
* The source is a live server. Like in an online backup, it's
* important that we recover all the WAL that was generated while we
* were copying files.
*/
if (ControlFile_source_after.state == DB_IN_ARCHIVE_RECOVERY)
{
/*
* Source is a standby server. We must replay to its
* minRecoveryPoint.
*/
endrec = ControlFile_source_after.minRecoveryPoint;
endtli = ControlFile_source_after.minRecoveryPointTLI;
}
else
{
/*
* Source is a production, non-standby, server. We must replay to
* the last WAL insert location.
*/
if (ControlFile_source_after.state != DB_IN_PRODUCTION)
pg_fatal("source system was in unexpected state at end of rewind");
endrec = source->get_current_wal_insert_lsn(source);
endtli = ControlFile_source_after.checkPointCopy.ThisTimeLineID;
}
}
else
{
endrec = ControlFile_source.checkPoint;
endtli = ControlFile_source.checkPointCopy.ThisTimeLineID;
/*
* Source is a local data directory. It should've shut down cleanly,
* and we must replay to the latest shutdown checkpoint.
*/
endrec = ControlFile_source_after.checkPoint;
endtli = ControlFile_source_after.checkPointCopy.ThisTimeLineID;
}
memcpy(&ControlFile_new, &ControlFile_source_after, sizeof(ControlFileData));
ControlFile_new.minRecoveryPoint = endrec;
ControlFile_new.minRecoveryPointTLI = endtli;
ControlFile_new.state = DB_IN_ARCHIVE_RECOVERY;