1
0
mirror of https://github.com/postgres/postgres.git synced 2025-06-29 10:41:53 +03:00

Tolerate timeline switches while "pg_basebackup -X fetch" is running.

If you take a base backup from a standby server with "pg_basebackup -X
fetch", and the timeline switches while the backup is being taken, the
backup used to fail with an error "requested WAL segment %s has already
been removed". This is because the server-side code that sends over the
required WAL files would not construct the WAL filename with the correct
timeline after a switch.

Fix that by using readdir() to scan pg_xlog for all the WAL segments in the
range, regardless of timeline.

Also, include all timeline history files in the backup, if taken with
"-X fetch". That fixes another related bug: If a timeline switch happened
just before the backup was initiated in a standby, the WAL segment
containing the initial checkpoint record contains WAL from the older
timeline too. Recovery will not accept that without a timeline history file
that lists the older timeline.

Backpatch to 9.2. Versions prior to that were not affected as you could not
take a base backup from a standby before 9.2.
This commit is contained in:
Heikki Linnakangas
2013-01-03 19:50:46 +02:00
parent faf1b1bd71
commit b4c99c9af3
4 changed files with 221 additions and 65 deletions

View File

@ -3456,19 +3456,36 @@ PreallocXlogFiles(XLogRecPtr endptr)
}
/*
* Get the log/seg of the latest removed or recycled WAL segment.
* Returns 0/0 if no WAL segments have been removed since startup.
* Throws an error if the given log segment has already been removed or
* recycled. The caller should only pass a segment that it knows to have
* existed while the server has been running, as this function always
* succeeds if no WAL segments have been removed since startup.
* 'tli' is only used in the error message.
*/
void
XLogGetLastRemoved(uint32 *log, uint32 *seg)
CheckXLogRemoved(uint32 log, uint32 seg, TimeLineID tli)
{
/* use volatile pointer to prevent code rearrangement */
volatile XLogCtlData *xlogctl = XLogCtl;
uint32 lastRemovedLog,
lastRemovedSeg;
SpinLockAcquire(&xlogctl->info_lck);
*log = xlogctl->lastRemovedLog;
*seg = xlogctl->lastRemovedSeg;
lastRemovedLog = xlogctl->lastRemovedLog;
lastRemovedSeg = xlogctl->lastRemovedSeg;
SpinLockRelease(&xlogctl->info_lck);
if (log < lastRemovedLog ||
(log == lastRemovedLog && seg <= lastRemovedSeg))
{
char filename[MAXFNAMELEN];
XLogFileName(filename, tli, log, seg);
ereport(ERROR,
(errcode_for_file_access(),
errmsg("requested WAL segment %s has already been removed",
filename)));
}
}
/*