Fix the tracking of min recovery point timeline.
authorHeikki Linnakangas <heikki.linnakangas@iki.fi>
Fri, 7 Dec 2012 14:29:39 +0000 (16:29 +0200)
committerHeikki Linnakangas <heikki.linnakangas@iki.fi>
Mon, 10 Dec 2012 14:04:26 +0000 (16:04 +0200)
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.

src/backend/access/transam/xlog.c

index 2618c8d3d383b00b2fe3eb1e436301c3ab5744c4..9bd7f03b8673f61fe83919736f25b542f462c053 100644 (file)
@@ -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);
    }