From: Heikki Linnakangas Date: Fri, 7 Dec 2012 14:29:39 +0000 (+0200) Subject: Fix the tracking of min recovery point timeline. X-Git-Tag: REL9_3_BETA1~610 X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=6be799664aa89a473c15af7a015f4c2b2794da2e;p=postgresql Fix the tracking of min recovery point timeline. Forgot to update it at the right place. Also, consider checkpoint record that switches to new timelne to be on the new timeline. This fixes erroneous "requested timeline 2 does not contain minimum recovery point" errors, pointed out by Amit Kapila while testing another patch. --- diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c index 2618c8d3d3..9bd7f03b86 100644 --- a/src/backend/access/transam/xlog.c +++ b/src/backend/access/transam/xlog.c @@ -605,6 +605,7 @@ static void SetLatestXTime(TimestampTz xtime); static void SetCurrentChunkStartTime(TimestampTz xtime); static void CheckRequiredParameterValues(void); static void XLogReportParameters(void); +static void checkTimeLineSwitch(XLogRecPtr lsn, TimeLineID newTLI); static void LocalSetXLogInsertAllowed(void); static void CheckPointGuts(XLogRecPtr checkPointRedo, int flags); static void KeepLogSeg(XLogRecPtr recptr, XLogSegNo *logSegNo); @@ -5909,12 +5910,41 @@ StartupXLOG(void) LWLockRelease(XidGenLock); } + /* + * Before replaying this record, check if it is a shutdown + * checkpoint record that causes the current timeline to + * change. The checkpoint record is already considered to be + * part of the new timeline, so we update ThisTimeLineID + * before replaying it. That's important so that replayEndTLI, + * which is recorded as the minimum recovery point's TLI if + * recovery stops after this record, is set correctly. + */ + if (record->xl_rmid == RM_XLOG_ID && + (record->xl_info & ~XLR_INFO_MASK) == XLOG_CHECKPOINT_SHUTDOWN) + { + CheckPoint checkPoint; + TimeLineID newTLI; + + memcpy(&checkPoint, XLogRecGetData(record), sizeof(CheckPoint)); + newTLI = checkPoint.ThisTimeLineID; + + if (newTLI != ThisTimeLineID) + { + /* Check that it's OK to switch to this TLI */ + checkTimeLineSwitch(EndRecPtr, newTLI); + + /* Following WAL records should be run with new TLI */ + ThisTimeLineID = newTLI; + } + } + /* * Update shared replayEndRecPtr before replaying this record, * so that XLogFlush will update minRecoveryPoint correctly. */ SpinLockAcquire(&xlogctl->info_lck); xlogctl->replayEndRecPtr = EndRecPtr; + xlogctl->replayEndTLI = ThisTimeLineID; SpinLockRelease(&xlogctl->info_lck); /* @@ -7858,6 +7888,48 @@ UpdateFullPageWrites(void) END_CRIT_SECTION(); } +/* + * Check that it's OK to switch to new timeline during recovery. + * + * 'lsn' is the address of the shutdown checkpoint record we're about to + * replay. (Currently, timeline can only change at a shutdown checkpoint). + */ +static void +checkTimeLineSwitch(XLogRecPtr lsn, TimeLineID newTLI) +{ + /* + * The new timeline better be in the list of timelines we expect + * to see, according to the timeline history. It should also not + * decrease. + */ + if (newTLI < ThisTimeLineID || !tliInHistory(newTLI, expectedTLEs)) + ereport(PANIC, + (errmsg("unexpected timeline ID %u (after %u) in checkpoint record", + newTLI, ThisTimeLineID))); + + /* + * If we have not yet reached min recovery point, and we're about + * to switch to a timeline greater than the timeline of the min + * recovery point: trouble. After switching to the new timeline, + * we could not possibly visit the min recovery point on the + * correct timeline anymore. This can happen if there is a newer + * timeline in the archive that branched before the timeline the + * min recovery point is on, and you attempt to do PITR to the + * new timeline. + */ + if (!XLogRecPtrIsInvalid(minRecoveryPoint) && + XLByteLT(lsn, minRecoveryPoint) && + newTLI > minRecoveryPointTLI) + ereport(PANIC, + (errmsg("unexpected timeline ID %u in checkpoint record, before reaching minimum recovery point %X/%X on timeline %u", + newTLI, + (uint32) (minRecoveryPoint >> 32), + (uint32) minRecoveryPoint, + minRecoveryPointTLI))); + + /* Looks good */ +} + /* * XLOG resource manager's routines * @@ -7971,44 +8043,13 @@ xlog_redo(XLogRecPtr lsn, XLogRecord *record) } /* - * TLI may change in a shutdown checkpoint. + * We should've already switched to the new TLI before replaying this + * record. */ if (checkPoint.ThisTimeLineID != ThisTimeLineID) - { - /* - * The new timeline better be in the list of timelines we expect - * to see, according to the timeline history. It should also not - * decrease. - */ - if (checkPoint.ThisTimeLineID < ThisTimeLineID || - !tliInHistory(checkPoint.ThisTimeLineID, expectedTLEs)) - ereport(PANIC, - (errmsg("unexpected timeline ID %u (after %u) in checkpoint record", - checkPoint.ThisTimeLineID, ThisTimeLineID))); - - /* - * If we have not yet reached min recovery point, and we're about - * to switch to a timeline greater than the timeline of the min - * recovery point: trouble. After switching to the new timeline, - * we could not possibly visit the min recovery point on the - * correct timeline anymore. This can happen if there is a newer - * timeline in the archive that branched before the timeline the - * min recovery point is on, and you attempt to do PITR to the - * new timeline. - */ - if (!XLogRecPtrIsInvalid(minRecoveryPoint) && - XLByteLT(lsn, minRecoveryPoint) && - checkPoint.ThisTimeLineID > minRecoveryPointTLI) - ereport(PANIC, - (errmsg("unexpected timeline ID %u in checkpoint record, before reaching minimum recovery point %X/%X on timeline %u", - checkPoint.ThisTimeLineID, - (uint32) (minRecoveryPoint >> 32), - (uint32) minRecoveryPoint, - minRecoveryPointTLI))); - - /* Following WAL records should be run with new TLI */ - ThisTimeLineID = checkPoint.ThisTimeLineID; - } + ereport(PANIC, + (errmsg("unexpected timeline ID %u (should be %u) in checkpoint record", + checkPoint.ThisTimeLineID, ThisTimeLineID))); RecoveryRestartPoint(&checkPoint); }