diff --git a/src/backend/access/rmgrdesc/xlogdesc.c b/src/backend/access/rmgrdesc/xlogdesc.c index 6901298516..b22e66e579 100644 --- a/src/backend/access/rmgrdesc/xlogdesc.c +++ b/src/backend/access/rmgrdesc/xlogdesc.c @@ -41,11 +41,12 @@ xlog_desc(StringInfo buf, uint8 xl_info, char *rec) CheckPoint *checkpoint = (CheckPoint *) rec; appendStringInfo(buf, "checkpoint: redo %X/%X; " - "tli %u; fpw %s; xid %u/%u; oid %u; multi %u; offset %u; " + "tli %u; prev tli %u; fpw %s; xid %u/%u; oid %u; multi %u; offset %u; " "oldest xid %u in DB %u; oldest multi %u in DB %u; " "oldest running xid %u; %s", (uint32) (checkpoint->redo >> 32), (uint32) checkpoint->redo, checkpoint->ThisTimeLineID, + checkpoint->PrevTimeLineID, checkpoint->fullPageWrites ? "true" : "false", checkpoint->nextXidEpoch, checkpoint->nextXid, checkpoint->nextOid, @@ -125,8 +126,8 @@ xlog_desc(StringInfo buf, uint8 xl_info, char *rec) xl_end_of_recovery xlrec; memcpy(&xlrec, rec, sizeof(xl_end_of_recovery)); - appendStringInfo(buf, "end_of_recovery: tli %u; time %s", - xlrec.ThisTimeLineID, + appendStringInfo(buf, "end_of_recovery: tli %u; prev tli %u; time %s", + xlrec.ThisTimeLineID, xlrec.PrevTimeLineID, timestamptz_to_str(xlrec.end_time)); } else diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c index f0df2977a1..3155238c58 100644 --- a/src/backend/access/transam/xlog.c +++ b/src/backend/access/transam/xlog.c @@ -408,7 +408,15 @@ typedef struct XLogCtlData char *pages; /* buffers for unwritten XLOG pages */ XLogRecPtr *xlblocks; /* 1st byte ptr-s + XLOG_BLCKSZ */ int XLogCacheBlck; /* highest allocated xlog buffer index */ + + /* + * Shared copy of ThisTimeLineID. Does not change after end-of-recovery. + * If we created a new timeline when the system was started up, + * PrevTimeLineID is the old timeline's ID that we forked off from. + * Otherwise it's equal to ThisTimeLineID. + */ TimeLineID ThisTimeLineID; + TimeLineID PrevTimeLineID; /* * archiveCleanupCommand is read from recovery.conf but needs to be in @@ -613,7 +621,8 @@ 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 checkTimeLineSwitch(XLogRecPtr lsn, TimeLineID newTLI, + TimeLineID prevTLI); static void LocalSetXLogInsertAllowed(void); static void CreateEndOfRecoveryRecord(void); static void CheckPointGuts(XLogRecPtr checkPointRedo, int flags); @@ -3896,6 +3905,7 @@ BootStrapXLOG(void) */ checkPoint.redo = XLogSegSize + SizeOfXLogLongPHD; checkPoint.ThisTimeLineID = ThisTimeLineID; + checkPoint.PrevTimeLineID = ThisTimeLineID; checkPoint.fullPageWrites = fullPageWrites; checkPoint.nextXidEpoch = 0; checkPoint.nextXid = FirstNormalTransactionId; @@ -4712,6 +4722,7 @@ StartupXLOG(void) checkPointLoc, EndOfLog; XLogSegNo endLogSegNo; + TimeLineID PrevTimeLineID; XLogRecord *record; uint32 freespace; TransactionId oldestActiveXID; @@ -5431,6 +5442,7 @@ StartupXLOG(void) if (record->xl_rmid == RM_XLOG_ID) { TimeLineID newTLI = ThisTimeLineID; + TimeLineID prevTLI = ThisTimeLineID; uint8 info = record->xl_info & ~XLR_INFO_MASK; if (info == XLOG_CHECKPOINT_SHUTDOWN) @@ -5439,6 +5451,7 @@ StartupXLOG(void) memcpy(&checkPoint, XLogRecGetData(record), sizeof(CheckPoint)); newTLI = checkPoint.ThisTimeLineID; + prevTLI = checkPoint.PrevTimeLineID; } else if (info == XLOG_END_OF_RECOVERY) { @@ -5446,12 +5459,13 @@ StartupXLOG(void) memcpy(&xlrec, XLogRecGetData(record), sizeof(xl_end_of_recovery)); newTLI = xlrec.ThisTimeLineID; + prevTLI = xlrec.PrevTimeLineID; } if (newTLI != ThisTimeLineID) { /* Check that it's OK to switch to this TLI */ - checkTimeLineSwitch(EndRecPtr, newTLI); + checkTimeLineSwitch(EndRecPtr, newTLI, prevTLI); /* Following WAL records should be run with new TLI */ ThisTimeLineID = newTLI; @@ -5620,6 +5634,7 @@ StartupXLOG(void) * * In a normal crash recovery, we can just extend the timeline we were in. */ + PrevTimeLineID = ThisTimeLineID; if (InArchiveRecovery) { char reason[200]; @@ -5655,6 +5670,7 @@ StartupXLOG(void) /* Save the selected TimeLineID in shared memory, too */ XLogCtl->ThisTimeLineID = ThisTimeLineID; + XLogCtl->PrevTimeLineID = PrevTimeLineID; /* * We are now done reading the old WAL. Turn off archive fetching if it @@ -6690,6 +6706,11 @@ CreateCheckPoint(int flags) LocalSetXLogInsertAllowed(); checkPoint.ThisTimeLineID = ThisTimeLineID; + if (flags & CHECKPOINT_END_OF_RECOVERY) + checkPoint.PrevTimeLineID = XLogCtl->PrevTimeLineID; + else + checkPoint.PrevTimeLineID = ThisTimeLineID; + checkPoint.fullPageWrites = Insert->fullPageWrites; /* @@ -6980,7 +7001,11 @@ CreateEndOfRecoveryRecord(void) elog(ERROR, "can only be used to end recovery"); xlrec.end_time = time(NULL); + + LWLockAcquire(WALInsertLock, LW_SHARED); xlrec.ThisTimeLineID = ThisTimeLineID; + xlrec.PrevTimeLineID = XLogCtl->PrevTimeLineID; + LWLockRelease(WALInsertLock); LocalSetXLogInsertAllowed(); @@ -7535,8 +7560,13 @@ UpdateFullPageWrites(void) * replay. (Currently, timeline can only change at a shutdown checkpoint). */ static void -checkTimeLineSwitch(XLogRecPtr lsn, TimeLineID newTLI) +checkTimeLineSwitch(XLogRecPtr lsn, TimeLineID newTLI, TimeLineID prevTLI) { + /* Check that the record agrees on what the current (old) timeline is */ + if (prevTLI != ThisTimeLineID) + ereport(PANIC, + (errmsg("unexpected prev timeline ID %u (current timeline ID %u) in checkpoint record", + prevTLI, ThisTimeLineID))); /* * The new timeline better be in the list of timelines we expect * to see, according to the timeline history. It should also not diff --git a/src/bin/pg_controldata/pg_controldata.c b/src/bin/pg_controldata/pg_controldata.c index 67ebc88dcd..33725154fd 100644 --- a/src/bin/pg_controldata/pg_controldata.c +++ b/src/bin/pg_controldata/pg_controldata.c @@ -215,6 +215,8 @@ main(int argc, char *argv[]) xlogfilename); printf(_("Latest checkpoint's TimeLineID: %u\n"), ControlFile.checkPointCopy.ThisTimeLineID); + printf(_("Latest checkpoint's PrevTimeLineID: %u\n"), + ControlFile.checkPointCopy.PrevTimeLineID); printf(_("Latest checkpoint's full_page_writes: %s\n"), ControlFile.checkPointCopy.fullPageWrites ? _("on") : _("off")); printf(_("Latest checkpoint's NextXID: %u/%u\n"), diff --git a/src/bin/pg_resetxlog/pg_resetxlog.c b/src/bin/pg_resetxlog/pg_resetxlog.c index 8e7fe7eb72..272813eaab 100644 --- a/src/bin/pg_resetxlog/pg_resetxlog.c +++ b/src/bin/pg_resetxlog/pg_resetxlog.c @@ -340,7 +340,10 @@ main(int argc, char *argv[]) ControlFile.checkPointCopy.nextMultiOffset = set_mxoff; if (minXlogTli > ControlFile.checkPointCopy.ThisTimeLineID) + { ControlFile.checkPointCopy.ThisTimeLineID = minXlogTli; + ControlFile.checkPointCopy.PrevTimeLineID = minXlogTli; + } if (minXlogSegNo > newXlogSegNo) newXlogSegNo = minXlogSegNo; @@ -490,6 +493,7 @@ GuessControlValues(void) ControlFile.checkPointCopy.redo = SizeOfXLogLongPHD; ControlFile.checkPointCopy.ThisTimeLineID = 1; + ControlFile.checkPointCopy.PrevTimeLineID = 1; ControlFile.checkPointCopy.fullPageWrites = false; ControlFile.checkPointCopy.nextXidEpoch = 0; ControlFile.checkPointCopy.nextXid = FirstNormalTransactionId; diff --git a/src/include/access/xlog_internal.h b/src/include/access/xlog_internal.h index 34c659314c..16b53e3726 100644 --- a/src/include/access/xlog_internal.h +++ b/src/include/access/xlog_internal.h @@ -221,7 +221,8 @@ typedef struct xl_restore_point typedef struct xl_end_of_recovery { TimestampTz end_time; - TimeLineID ThisTimeLineID; + TimeLineID ThisTimeLineID; /* new TLI */ + TimeLineID PrevTimeLineID; /* previous TLI we forked off from */ } xl_end_of_recovery; /* diff --git a/src/include/catalog/pg_control.h b/src/include/catalog/pg_control.h index ec8cea7c86..0c647e77ad 100644 --- a/src/include/catalog/pg_control.h +++ b/src/include/catalog/pg_control.h @@ -21,7 +21,7 @@ /* Version identifier for this pg_control format */ -#define PG_CONTROL_VERSION 933 +#define PG_CONTROL_VERSION 934 /* * Body of CheckPoint XLOG records. This is declared here because we keep @@ -33,6 +33,8 @@ typedef struct CheckPoint XLogRecPtr redo; /* next RecPtr available when we began to * create CheckPoint (i.e. REDO start point) */ TimeLineID ThisTimeLineID; /* current TLI */ + TimeLineID PrevTimeLineID; /* previous TLI, if this record begins a new + * timeline (equals ThisTimeLineID otherwise) */ bool fullPageWrites; /* current full_page_writes */ uint32 nextXidEpoch; /* higher-order bits of nextXid */ TransactionId nextXid; /* next free XID */