@@ -447,6 +447,7 @@ typedef struct XLogCtlData
447447
448448 /* end+1 of the last record replayed (or being replayed) */
449449 XLogRecPtr replayEndRecPtr ;
450+ TimeLineID replayEndTLI ;
450451 /* end+1 of the last record replayed */
451452 XLogRecPtr recoveryLastRecPtr ;
452453 /* timestamp of last COMMIT/ABORT record replayed (or being replayed) */
@@ -580,6 +581,7 @@ static TimeLineID lastSegmentTLI = 0;
580581
581582static XLogRecPtr minRecoveryPoint ; /* local copy of
582583 * ControlFile->minRecoveryPoint */
584+ static TimeLineID minRecoveryPointTLI ;
583585static bool updateMinRecoveryPoint = true;
584586
585587/*
@@ -1778,6 +1780,7 @@ UpdateMinRecoveryPoint(XLogRecPtr lsn, bool force)
17781780
17791781 /* update local copy */
17801782 minRecoveryPoint = ControlFile -> minRecoveryPoint ;
1783+ minRecoveryPointTLI = ControlFile -> minRecoveryPointTLI ;
17811784
17821785 /*
17831786 * An invalid minRecoveryPoint means that we need to recover all the WAL,
@@ -1791,6 +1794,7 @@ UpdateMinRecoveryPoint(XLogRecPtr lsn, bool force)
17911794 /* use volatile pointer to prevent code rearrangement */
17921795 volatile XLogCtlData * xlogctl = XLogCtl ;
17931796 XLogRecPtr newMinRecoveryPoint ;
1797+ TimeLineID newMinRecoveryPointTLI ;
17941798
17951799 /*
17961800 * To avoid having to update the control file too often, we update it
@@ -1807,6 +1811,7 @@ UpdateMinRecoveryPoint(XLogRecPtr lsn, bool force)
18071811 */
18081812 SpinLockAcquire (& xlogctl -> info_lck );
18091813 newMinRecoveryPoint = xlogctl -> replayEndRecPtr ;
1814+ newMinRecoveryPointTLI = xlogctl -> replayEndTLI ;
18101815 SpinLockRelease (& xlogctl -> info_lck );
18111816
18121817 if (!force && XLByteLT (newMinRecoveryPoint , lsn ))
@@ -1820,13 +1825,16 @@ UpdateMinRecoveryPoint(XLogRecPtr lsn, bool force)
18201825 if (XLByteLT (ControlFile -> minRecoveryPoint , newMinRecoveryPoint ))
18211826 {
18221827 ControlFile -> minRecoveryPoint = newMinRecoveryPoint ;
1828+ ControlFile -> minRecoveryPointTLI = newMinRecoveryPointTLI ;
18231829 UpdateControlFile ();
18241830 minRecoveryPoint = newMinRecoveryPoint ;
1831+ minRecoveryPointTLI = newMinRecoveryPointTLI ;
18251832
18261833 ereport (DEBUG2 ,
1827- (errmsg ("updated min recovery point to %X/%X" ,
1834+ (errmsg ("updated min recovery point to %X/%X on timeline %u " ,
18281835 (uint32 ) (minRecoveryPoint >> 32 ),
1829- (uint32 ) minRecoveryPoint )));
1836+ (uint32 ) minRecoveryPoint ,
1837+ newMinRecoveryPointTLI )));
18301838 }
18311839 }
18321840 LWLockRelease (ControlFileLock );
@@ -2132,6 +2140,7 @@ XLogNeedsFlush(XLogRecPtr record)
21322140 if (!LWLockConditionalAcquire (ControlFileLock , LW_SHARED ))
21332141 return true;
21342142 minRecoveryPoint = ControlFile -> minRecoveryPoint ;
2143+ minRecoveryPointTLI = ControlFile -> minRecoveryPointTLI ;
21352144 LWLockRelease (ControlFileLock );
21362145
21372146 /*
@@ -5305,6 +5314,19 @@ StartupXLOG(void)
53055314 recoveryTargetTLI ,
53065315 ControlFile -> checkPointCopy .ThisTimeLineID )));
53075316
5317+ /*
5318+ * The min recovery point should be part of the requested timeline's
5319+ * history, too.
5320+ */
5321+ if (!XLogRecPtrIsInvalid (ControlFile -> minRecoveryPoint ) &&
5322+ !list_member_int (expectedTLIs , ControlFile -> minRecoveryPointTLI ))
5323+ ereport (FATAL ,
5324+ (errmsg ("requested timeline %u does not contain minimum recovery point %X/%X on timeline %u" ,
5325+ recoveryTargetTLI ,
5326+ (uint32 ) (ControlFile -> minRecoveryPoint >> 32 ),
5327+ (uint32 ) ControlFile -> minRecoveryPoint ,
5328+ ControlFile -> minRecoveryPointTLI )));
5329+
53085330 /*
53095331 * Save the selected recovery target timeline ID and
53105332 * archive_cleanup_command in shared memory so that other processes can
@@ -5523,7 +5545,10 @@ StartupXLOG(void)
55235545 {
55245546 /* initialize minRecoveryPoint if not set yet */
55255547 if (XLByteLT (ControlFile -> minRecoveryPoint , checkPoint .redo ))
5548+ {
55265549 ControlFile -> minRecoveryPoint = checkPoint .redo ;
5550+ ControlFile -> minRecoveryPointTLI = checkPoint .ThisTimeLineID ;
5551+ }
55275552 }
55285553
55295554 /*
@@ -5556,6 +5581,7 @@ StartupXLOG(void)
55565581
55575582 /* initialize our local copy of minRecoveryPoint */
55585583 minRecoveryPoint = ControlFile -> minRecoveryPoint ;
5584+ minRecoveryPointTLI = ControlFile -> minRecoveryPointTLI ;
55595585
55605586 /*
55615587 * Reset pgstat data, because it may be invalid after recovery.
@@ -5681,6 +5707,7 @@ StartupXLOG(void)
56815707 */
56825708 SpinLockAcquire (& xlogctl -> info_lck );
56835709 xlogctl -> replayEndRecPtr = ReadRecPtr ;
5710+ xlogctl -> replayEndTLI = ThisTimeLineID ;
56845711 xlogctl -> recoveryLastRecPtr = EndRecPtr ;
56855712 xlogctl -> recoveryLastXTime = 0 ;
56865713 xlogctl -> currentChunkStartTime = 0 ;
@@ -7202,6 +7229,7 @@ CreateCheckPoint(int flags)
72027229 ControlFile -> time = (pg_time_t ) time (NULL );
72037230 /* crash recovery should always recover to the end of WAL */
72047231 MemSet (& ControlFile -> minRecoveryPoint , 0 , sizeof (XLogRecPtr ));
7232+ ControlFile -> minRecoveryPointTLI = 0 ;
72057233 UpdateControlFile ();
72067234 LWLockRelease (ControlFileLock );
72077235
@@ -7878,16 +7906,42 @@ xlog_redo(XLogRecPtr lsn, XLogRecord *record)
78787906 }
78797907
78807908 /*
7881- * TLI may change in a shutdown checkpoint, but it shouldn't decrease
7909+ * TLI may change in a shutdown checkpoint.
78827910 */
78837911 if (checkPoint .ThisTimeLineID != ThisTimeLineID )
78847912 {
7913+ /*
7914+ * The new timeline better be in the list of timelines we expect
7915+ * to see, according to the timeline history. It should also not
7916+ * decrease.
7917+ */
78857918 if (checkPoint .ThisTimeLineID < ThisTimeLineID ||
78867919 !list_member_int (expectedTLIs ,
78877920 (int ) checkPoint .ThisTimeLineID ))
78887921 ereport (PANIC ,
78897922 (errmsg ("unexpected timeline ID %u (after %u) in checkpoint record" ,
78907923 checkPoint .ThisTimeLineID , ThisTimeLineID )));
7924+
7925+ /*
7926+ * If we have not yet reached min recovery point, and we're about
7927+ * to switch to a timeline greater than the timeline of the min
7928+ * recovery point: trouble. After switching to the new timeline,
7929+ * we could not possibly visit the min recovery point on the
7930+ * correct timeline anymore. This can happen if there is a newer
7931+ * timeline in the archive that branched before the timeline the
7932+ * min recovery point is on, and you attempt to do PITR to the
7933+ * new timeline.
7934+ */
7935+ if (!XLogRecPtrIsInvalid (minRecoveryPoint ) &&
7936+ XLByteLT (lsn , minRecoveryPoint ) &&
7937+ checkPoint .ThisTimeLineID > minRecoveryPointTLI )
7938+ ereport (PANIC ,
7939+ (errmsg ("unexpected timeline ID %u in checkpoint record, before reaching minimum recovery point %X/%X on timeline %u" ,
7940+ checkPoint .ThisTimeLineID ,
7941+ (uint32 ) (minRecoveryPoint >> 32 ),
7942+ (uint32 ) minRecoveryPoint ,
7943+ minRecoveryPointTLI )));
7944+
78917945 /* Following WAL records should be run with new TLI */
78927946 ThisTimeLineID = checkPoint .ThisTimeLineID ;
78937947 }
@@ -7972,7 +8026,10 @@ xlog_redo(XLogRecPtr lsn, XLogRecord *record)
79728026 LWLockAcquire (ControlFileLock , LW_EXCLUSIVE );
79738027
79748028 if (XLByteLT (ControlFile -> minRecoveryPoint , lsn ))
8029+ {
79758030 ControlFile -> minRecoveryPoint = lsn ;
8031+ ControlFile -> minRecoveryPointTLI = ThisTimeLineID ;
8032+ }
79768033 MemSet (& ControlFile -> backupStartPoint , 0 , sizeof (XLogRecPtr ));
79778034 ControlFile -> backupEndRequired = false;
79788035 UpdateControlFile ();
@@ -8002,9 +8059,11 @@ xlog_redo(XLogRecPtr lsn, XLogRecord *record)
80028059 * decreasing max_* settings.
80038060 */
80048061 minRecoveryPoint = ControlFile -> minRecoveryPoint ;
8062+ minRecoveryPointTLI = ControlFile -> minRecoveryPointTLI ;
80058063 if (minRecoveryPoint != 0 && XLByteLT (minRecoveryPoint , lsn ))
80068064 {
80078065 ControlFile -> minRecoveryPoint = lsn ;
8066+ ControlFile -> minRecoveryPointTLI = ThisTimeLineID ;
80088067 }
80098068
80108069 UpdateControlFile ();
0 commit comments