]> granicus.if.org Git - postgresql/commitdiff
Include previous TLI in end-of-recovery and shutdown checkpoint records.
authorHeikki Linnakangas <heikki.linnakangas@iki.fi>
Mon, 11 Feb 2013 16:13:09 +0000 (18:13 +0200)
committerHeikki Linnakangas <heikki.linnakangas@iki.fi>
Mon, 11 Feb 2013 16:16:25 +0000 (18:16 +0200)
This isn't used for anything but a sanity check at the moment, but it could
be highly valuable for debugging purposes. It could also be used to recreate
timeline history by traversing WAL, which seems useful.

src/backend/access/rmgrdesc/xlogdesc.c
src/backend/access/transam/xlog.c
src/bin/pg_controldata/pg_controldata.c
src/bin/pg_resetxlog/pg_resetxlog.c
src/include/access/xlog_internal.h
src/include/catalog/pg_control.h

index 69012985161dcadd15cd6bd4d730cfc6e2fc9dd1..b22e66e57953f4991e35e9c426147ad922e25e62 100644 (file)
@@ -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
index f0df2977a12e62e390c83199b8ba79f3307e6131..3155238c58de5a12d252db9d0bc42f3f7747ae86 100644 (file)
@@ -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
index 67ebc88dcdd0b6db2c22b8e8c86e0cbba6d08907..33725154fd7aacf58bc821b6c591d4f0a413dd08 100644 (file)
@@ -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"),
index 8e7fe7eb72e011091c345eff5c89da998346b4e5..272813eaabffc35da14ce0f5b1f94115e02e2059 100644 (file)
@@ -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;
index 34c659314ceb40c949fbacfbe5b64b770322a912..16b53e37260b8abe2ec0ef4266bd71150a253f7a 100644 (file)
@@ -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;
 
 /*
index ec8cea7c86e749c3bde19063664a64bdd56f77ed..0c647e77ad76aa20e693991ecb1bd79e5c869517 100644 (file)
@@ -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 */