]> granicus.if.org Git - postgresql/commitdiff
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);
        }