From b4c99c9af379157a6224b0a4c01da22192633adf Mon Sep 17 00:00:00 2001 From: Heikki Linnakangas Date: Thu, 3 Jan 2013 19:50:46 +0200 Subject: 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. --- src/backend/access/transam/xlog.c | 27 ++++++++++++++++++++++----- 1 file changed, 22 insertions(+), 5 deletions(-) (limited to 'src/backend/access/transam/xlog.c') diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c index 6f352fd5be4..30d877b6fdb 100644 --- a/src/backend/access/transam/xlog.c +++ b/src/backend/access/transam/xlog.c @@ -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))); + } } /* -- cgit v1.2.3