From c95e8c57a6e3a187a5b5c538e2fdc1e9d4d7cede Mon Sep 17 00:00:00 2001
From: Robert Haas <rhaas@postgresql.org>
Date: Mon, 18 Oct 2021 16:28:18 -0400
Subject: [PATCH 1/2] Test code to see whether we have always properly
 initialized ThisTimeLineID.

---
 src/backend/access/transam/twophase.c         |   4 +-
 src/backend/access/transam/xlog.c             | 108 ++++++++++--------
 src/backend/access/transam/xlogarchive.c      |   2 +-
 src/backend/access/transam/xlogfuncs.c        |   4 +-
 src/backend/access/transam/xlogutils.c        |  13 ++-
 src/backend/replication/basebackup.c          |  12 +-
 .../replication/logical/logicalfuncs.c        |   4 +
 src/backend/replication/slotfuncs.c           |   4 +
 src/backend/replication/walreceiver.c         |   5 +-
 src/backend/replication/walsender.c           |  19 +--
 src/include/access/xlog.h                     |   4 +
 11 files changed, 105 insertions(+), 74 deletions(-)

diff --git a/src/backend/access/transam/twophase.c b/src/backend/access/transam/twophase.c
index 2156de187c..43cab819da 100644
--- a/src/backend/access/transam/twophase.c
+++ b/src/backend/access/transam/twophase.c
@@ -1328,7 +1328,8 @@ XlogReadTwoPhaseData(XLogRecPtr lsn, char **buf, int *len)
 	XLogRecord *record;
 	XLogReaderState *xlogreader;
 	char	   *errormsg;
-	TimeLineID	save_currtli = ThisTimeLineID;
+	TimeLineID	save_currtli = ThisTimeLineID;  // XXX ThisTimeLineIDChecked fails assertion, ThisTimeLineID = 0!!!
+	bool		save_currtli_valid = ThisTimeLineIDValid;
 
 	xlogreader = XLogReaderAllocate(wal_segment_size, NULL,
 									XL_ROUTINE(.page_read = &read_local_xlog_page,
@@ -1350,6 +1351,7 @@ XlogReadTwoPhaseData(XLogRecPtr lsn, char **buf, int *len)
 	 * while recovery was finishing or if the timeline has jumped in-between.
 	 */
 	ThisTimeLineID = save_currtli;
+	ThisTimeLineIDValid = save_currtli_valid;
 
 	if (record == NULL)
 		ereport(ERROR,
diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c
index 62862255fc..5f1e6d360f 100644
--- a/src/backend/access/transam/xlog.c
+++ b/src/backend/access/transam/xlog.c
@@ -192,6 +192,7 @@ CheckpointStatsData CheckpointStats;
  * WAL timeline for the database system.
  */
 TimeLineID	ThisTimeLineID = 0;
+bool		ThisTimeLineIDValid = false;
 
 static XLogRecPtr LastRec;
 
@@ -2240,7 +2241,7 @@ AdvanceXLInsertBuffer(XLogRecPtr upto, bool opportunistic)
 		NewPage->xlp_magic = XLOG_PAGE_MAGIC;
 
 		/* NewPage->xlp_info = 0; */	/* done by memset */
-		NewPage->xlp_tli = ThisTimeLineID;
+		NewPage->xlp_tli = ThisTimeLineIDChecked;
 		NewPage->xlp_pageaddr = NewPageBeginPtr;
 
 		/* NewPage->xlp_rem_len = 0; */	/* done by memset */
@@ -2590,7 +2591,7 @@ XLogWrite(XLogwrtRqst WriteRqst, bool flexible)
 						continue;
 
 					save_errno = errno;
-					XLogFileName(xlogfname, ThisTimeLineID, openLogSegNo,
+					XLogFileName(xlogfname, ThisTimeLineIDChecked, openLogSegNo,
 								 wal_segment_size);
 					errno = save_errno;
 					ereport(PANIC,
@@ -3293,7 +3294,7 @@ XLogFileInitInternal(XLogSegNo logsegno, bool *added, char *path)
 	int			fd;
 	int			save_errno;
 
-	XLogFilePath(path, ThisTimeLineID, logsegno, wal_segment_size);
+	XLogFilePath(path, ThisTimeLineIDChecked, logsegno, wal_segment_size);
 
 	/*
 	 * Try to use existent file (checkpoint maker may have created it already)
@@ -3652,7 +3653,7 @@ InstallXLogFileSegment(XLogSegNo *segno, char *tmppath,
 	char		path[MAXPGPATH];
 	struct stat stat_buf;
 
-	XLogFilePath(path, ThisTimeLineID, *segno, wal_segment_size);
+	XLogFilePath(path, ThisTimeLineIDChecked, *segno, wal_segment_size);
 
 	LWLockAcquire(ControlFileLock, LW_EXCLUSIVE);
 	if (!XLogCtl->InstallXLogFileSegmentActive)
@@ -3678,7 +3679,7 @@ InstallXLogFileSegment(XLogSegNo *segno, char *tmppath,
 				return false;
 			}
 			(*segno)++;
-			XLogFilePath(path, ThisTimeLineID, *segno, wal_segment_size);
+			XLogFilePath(path, ThisTimeLineIDChecked, *segno, wal_segment_size);
 		}
 	}
 
@@ -3707,7 +3708,7 @@ XLogFileOpen(XLogSegNo segno)
 	char		path[MAXPGPATH];
 	int			fd;
 
-	XLogFilePath(path, ThisTimeLineID, segno, wal_segment_size);
+	XLogFilePath(path, ThisTimeLineIDChecked, segno, wal_segment_size);
 
 	fd = BasicOpenFile(path, O_RDWR | PG_BINARY | get_sync_bit(sync_method));
 	if (fd < 0)
@@ -3928,7 +3929,7 @@ XLogFileClose(void)
 		char		xlogfname[MAXFNAMELEN];
 		int			save_errno = errno;
 
-		XLogFileName(xlogfname, ThisTimeLineID, openLogSegNo, wal_segment_size);
+		XLogFileName(xlogfname, ThisTimeLineIDChecked, openLogSegNo, wal_segment_size);
 		errno = save_errno;
 		ereport(PANIC,
 				(errcode_for_file_access(),
@@ -4510,7 +4511,7 @@ ReadRecord(XLogReaderState *xlogreader, int emode,
 				if (ControlFile->minRecoveryPoint < EndRecPtr)
 				{
 					ControlFile->minRecoveryPoint = EndRecPtr;
-					ControlFile->minRecoveryPointTLI = ThisTimeLineID;
+					ControlFile->minRecoveryPointTLI = ThisTimeLineIDChecked;
 				}
 				/* update local copy */
 				minRecoveryPoint = ControlFile->minRecoveryPoint;
@@ -4606,7 +4607,7 @@ rescanLatestTimeLine(void)
 		ereport(LOG,
 				(errmsg("new timeline %u is not a child of database system timeline %u",
 						newtarget,
-						ThisTimeLineID)));
+						ThisTimeLineIDChecked)));
 		return false;
 	}
 
@@ -4620,7 +4621,7 @@ rescanLatestTimeLine(void)
 		ereport(LOG,
 				(errmsg("new timeline %u forked off current database system timeline %u before current recovery point %X/%X",
 						newtarget,
-						ThisTimeLineID,
+						ThisTimeLineIDChecked,
 						LSN_FORMAT_ARGS(EndRecPtr))));
 		return false;
 	}
@@ -5316,6 +5317,7 @@ BootStrapXLOG(void)
 
 	/* First timeline ID is always 1 */
 	ThisTimeLineID = 1;
+	ThisTimeLineIDValid = true;
 
 	/* page buffer must be aligned suitably for O_DIRECT */
 	buffer = (char *) palloc(XLOG_BLCKSZ + XLOG_BLCKSZ);
@@ -5330,8 +5332,8 @@ BootStrapXLOG(void)
 	 * used, so that we can use 0/0 to mean "before any valid WAL segment".
 	 */
 	checkPoint.redo = wal_segment_size + SizeOfXLogLongPHD;
-	checkPoint.ThisTimeLineID = ThisTimeLineID;
-	checkPoint.PrevTimeLineID = ThisTimeLineID;
+	checkPoint.ThisTimeLineID = ThisTimeLineIDChecked;
+	checkPoint.PrevTimeLineID = ThisTimeLineIDChecked;
 	checkPoint.fullPageWrites = fullPageWrites;
 	checkPoint.nextXid =
 		FullTransactionIdFromEpochAndXid(0, FirstNormalTransactionId);
@@ -5359,7 +5361,7 @@ BootStrapXLOG(void)
 	/* Set up the XLOG page header */
 	page->xlp_magic = XLOG_PAGE_MAGIC;
 	page->xlp_info = XLP_LONG_HEADER;
-	page->xlp_tli = ThisTimeLineID;
+	page->xlp_tli = ThisTimeLineIDChecked;
 	page->xlp_pageaddr = wal_segment_size;
 	longpage = (XLogLongPageHeader) page;
 	longpage->xlp_sysid = sysidentifier;
@@ -5640,7 +5642,7 @@ exitArchiveRecovery(TimeLineID endTLI, XLogRecPtr endOfLog)
 	XLogSegNo	startLogSegNo;
 
 	/* we always switch to a new timeline after archive recovery */
-	Assert(endTLI != ThisTimeLineID);
+	Assert(endTLI != ThisTimeLineIDChecked);
 
 	/*
 	 * We are no longer in archive recovery state.
@@ -5704,7 +5706,7 @@ exitArchiveRecovery(TimeLineID endTLI, XLogRecPtr endOfLog)
 			char		xlogfname[MAXFNAMELEN];
 			int			save_errno = errno;
 
-			XLogFileName(xlogfname, ThisTimeLineID, startLogSegNo,
+			XLogFileName(xlogfname, ThisTimeLineIDChecked, startLogSegNo,
 						 wal_segment_size);
 			errno = save_errno;
 			ereport(ERROR,
@@ -5717,7 +5719,7 @@ exitArchiveRecovery(TimeLineID endTLI, XLogRecPtr endOfLog)
 	 * Let's just make real sure there are not .ready or .done flags posted
 	 * for the new segment.
 	 */
-	XLogFileName(xlogfname, ThisTimeLineID, startLogSegNo, wal_segment_size);
+	XLogFileName(xlogfname, ThisTimeLineIDChecked, startLogSegNo, wal_segment_size);
 	XLogArchiveCleanup(xlogfname);
 
 	/*
@@ -5756,7 +5758,7 @@ CleanupAfterArchiveRecovery(TimeLineID EndOfLogTLI, XLogRecPtr EndOfLog)
 	 * files containing garbage. In any case, they are not part of the new
 	 * timeline's history so we don't need them.
 	 */
-	RemoveNonParentXlogFiles(EndOfLog, ThisTimeLineID);
+	RemoveNonParentXlogFiles(EndOfLog, ThisTimeLineIDChecked);
 
 	/*
 	 * If the switch happened in the middle of a segment, what to do with the
@@ -7137,6 +7139,7 @@ StartupXLOG(void)
 	 * also xlog_redo()).
 	 */
 	ThisTimeLineID = checkPoint.ThisTimeLineID;
+	ThisTimeLineIDValid = true;
 
 	/*
 	 * Copy any missing timeline history files between 'now' and the recovery
@@ -7150,7 +7153,7 @@ StartupXLOG(void)
 	 * are small, so it's better to copy them unnecessarily than not copy them
 	 * and regret later.
 	 */
-	restoreTimeLineHistoryFiles(ThisTimeLineID, recoveryTargetTLI);
+	restoreTimeLineHistoryFiles(ThisTimeLineIDChecked, recoveryTargetTLI);
 
 	/*
 	 * Before running in recovery, scan pg_twophase and fill in its status to
@@ -7434,7 +7437,7 @@ StartupXLOG(void)
 			XLogCtl->replayEndRecPtr = checkPoint.redo;
 		else
 			XLogCtl->replayEndRecPtr = EndRecPtr;
-		XLogCtl->replayEndTLI = ThisTimeLineID;
+		XLogCtl->replayEndTLI = ThisTimeLineIDChecked;
 		XLogCtl->lastReplayedEndRecPtr = XLogCtl->replayEndRecPtr;
 		XLogCtl->lastReplayedTLI = XLogCtl->replayEndTLI;
 		XLogCtl->recoveryLastXTime = 0;
@@ -7586,8 +7589,8 @@ StartupXLOG(void)
 				 */
 				if (record->xl_rmid == RM_XLOG_ID)
 				{
-					TimeLineID	newTLI = ThisTimeLineID;
-					TimeLineID	prevTLI = ThisTimeLineID;
+					TimeLineID	newTLI = ThisTimeLineIDChecked;
+					TimeLineID	prevTLI = ThisTimeLineIDChecked;
 					uint8		info = record->xl_info & ~XLR_INFO_MASK;
 
 					if (info == XLOG_CHECKPOINT_SHUTDOWN)
@@ -7607,13 +7610,14 @@ StartupXLOG(void)
 						prevTLI = xlrec.PrevTimeLineID;
 					}
 
-					if (newTLI != ThisTimeLineID)
+					if (newTLI != ThisTimeLineIDChecked)
 					{
 						/* Check that it's OK to switch to this TLI */
 						checkTimeLineSwitch(EndRecPtr, newTLI, prevTLI);
 
 						/* Following WAL records should be run with new TLI */
 						ThisTimeLineID = newTLI;
+						ThisTimeLineIDValid = true;
 						switchedTLI = true;
 					}
 				}
@@ -7624,7 +7628,7 @@ StartupXLOG(void)
 				 */
 				SpinLockAcquire(&XLogCtl->info_lck);
 				XLogCtl->replayEndRecPtr = EndRecPtr;
-				XLogCtl->replayEndTLI = ThisTimeLineID;
+				XLogCtl->replayEndTLI = ThisTimeLineIDChecked;
 				SpinLockRelease(&XLogCtl->info_lck);
 
 				/*
@@ -7656,7 +7660,7 @@ StartupXLOG(void)
 				 */
 				SpinLockAcquire(&XLogCtl->info_lck);
 				XLogCtl->lastReplayedEndRecPtr = EndRecPtr;
-				XLogCtl->lastReplayedTLI = ThisTimeLineID;
+				XLogCtl->lastReplayedTLI = ThisTimeLineIDChecked;
 				SpinLockRelease(&XLogCtl->info_lck);
 
 				/*
@@ -7684,7 +7688,7 @@ StartupXLOG(void)
 					 * (possibly bogus) future WAL segments on the old
 					 * timeline.
 					 */
-					RemoveNonParentXlogFiles(EndRecPtr, ThisTimeLineID);
+					RemoveNonParentXlogFiles(EndRecPtr, ThisTimeLineIDChecked);
 
 					/*
 					 * Wake up any walsenders to notice that we are on a new
@@ -7903,7 +7907,7 @@ StartupXLOG(void)
 	 *
 	 * In a normal crash recovery, we can just extend the timeline we were in.
 	 */
-	PrevTimeLineID = ThisTimeLineID;
+	PrevTimeLineID = ThisTimeLineIDChecked;
 	if (ArchiveRecoveryRequested)
 	{
 		char	   *reason;
@@ -7912,8 +7916,9 @@ StartupXLOG(void)
 		Assert(InArchiveRecovery);
 
 		ThisTimeLineID = findNewestTimeLine(recoveryTargetTLI) + 1;
+		ThisTimeLineIDValid = true;
 		ereport(LOG,
-				(errmsg("selected new timeline ID: %u", ThisTimeLineID)));
+				(errmsg("selected new timeline ID: %u", ThisTimeLineIDChecked)));
 
 		reason = getRecoveryStopReason();
 
@@ -7935,7 +7940,7 @@ StartupXLOG(void)
 		 * To minimize the window for that, try to do as little as possible
 		 * between here and writing the end-of-recovery record.
 		 */
-		writeTimeLineHistory(ThisTimeLineID, recoveryTargetTLI,
+		writeTimeLineHistory(ThisTimeLineIDChecked, recoveryTargetTLI,
 							 EndRecPtr, reason);
 
 		/*
@@ -7951,7 +7956,7 @@ StartupXLOG(void)
 	}
 
 	/* Save the selected TimeLineID in shared memory, too */
-	XLogCtl->ThisTimeLineID = ThisTimeLineID;
+	XLogCtl->ThisTimeLineID = ThisTimeLineIDChecked;
 	XLogCtl->PrevTimeLineID = PrevTimeLineID;
 
 	/*
@@ -8597,6 +8602,7 @@ InitXLOGAccess(void)
 
 	/* ThisTimeLineID doesn't change so we need no lock to copy it */
 	ThisTimeLineID = XLogCtl->ThisTimeLineID;
+	ThisTimeLineIDValid = true;
 	Assert(ThisTimeLineID != 0 || IsBootstrapProcessingMode());
 
 	/* set wal_segment_size */
@@ -9129,11 +9135,11 @@ CreateCheckPoint(int flags)
 	if (flags & CHECKPOINT_END_OF_RECOVERY)
 		LocalSetXLogInsertAllowed();
 
-	checkPoint.ThisTimeLineID = ThisTimeLineID;
+	checkPoint.ThisTimeLineID = ThisTimeLineIDChecked;
 	if (flags & CHECKPOINT_END_OF_RECOVERY)
 		checkPoint.PrevTimeLineID = XLogCtl->PrevTimeLineID;
 	else
-		checkPoint.PrevTimeLineID = ThisTimeLineID;
+		checkPoint.PrevTimeLineID = ThisTimeLineIDChecked;
 
 	checkPoint.fullPageWrites = Insert->fullPageWrites;
 
@@ -9443,7 +9449,7 @@ CreateEndOfRecoveryRecord(void)
 	xlrec.end_time = GetCurrentTimestamp();
 
 	WALInsertLockAcquireExclusive();
-	xlrec.ThisTimeLineID = ThisTimeLineID;
+	xlrec.ThisTimeLineID = ThisTimeLineIDChecked;
 	xlrec.PrevTimeLineID = XLogCtl->PrevTimeLineID;
 	WALInsertLockRelease();
 
@@ -9464,7 +9470,7 @@ CreateEndOfRecoveryRecord(void)
 	LWLockAcquire(ControlFileLock, LW_EXCLUSIVE);
 	ControlFile->time = (pg_time_t) time(NULL);
 	ControlFile->minRecoveryPoint = recptr;
-	ControlFile->minRecoveryPointTLI = ThisTimeLineID;
+	ControlFile->minRecoveryPointTLI = ThisTimeLineIDChecked;
 	UpdateControlFile();
 	LWLockRelease(ControlFileLock);
 
@@ -9800,7 +9806,10 @@ CreateRestartPoint(int flags)
 	 * with that.
 	 */
 	if (RecoveryInProgress())
+	{
 		ThisTimeLineID = replayTLI;
+		ThisTimeLineIDValid = true;
+	}
 
 	RemoveOldXlogFiles(_logSegNo, RedoRecPtr, endptr);
 
@@ -9817,7 +9826,10 @@ CreateRestartPoint(int flags)
 	 * to restore the normal state of affairs for debugging purposes.
 	 */
 	if (RecoveryInProgress())
+	{
 		ThisTimeLineID = 0;
+		ThisTimeLineIDValid = false;
+	}
 
 	/*
 	 * Truncate pg_subtrans if possible.  We can throw away all data before
@@ -10228,19 +10240,19 @@ static void
 checkTimeLineSwitch(XLogRecPtr lsn, TimeLineID newTLI, TimeLineID prevTLI)
 {
 	/* Check that the record agrees on what the current (old) timeline is */
-	if (prevTLI != ThisTimeLineID)
+	if (prevTLI != ThisTimeLineIDChecked)
 		ereport(PANIC,
 				(errmsg("unexpected previous timeline ID %u (current timeline ID %u) in checkpoint record",
-						prevTLI, ThisTimeLineID)));
+						prevTLI, ThisTimeLineIDChecked)));
 
 	/*
 	 * 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))
+	if (newTLI < ThisTimeLineIDChecked || !tliInHistory(newTLI, expectedTLEs))
 		ereport(PANIC,
 				(errmsg("unexpected timeline ID %u (after %u) in checkpoint record",
-						newTLI, ThisTimeLineID)));
+						newTLI, ThisTimeLineIDChecked)));
 
 	/*
 	 * If we have not yet reached min recovery point, and we're about to
@@ -10387,10 +10399,10 @@ xlog_redo(XLogReaderState *record)
 		 * We should've already switched to the new TLI before replaying this
 		 * record.
 		 */
-		if (checkPoint.ThisTimeLineID != ThisTimeLineID)
+		if (checkPoint.ThisTimeLineID != ThisTimeLineIDChecked)
 			ereport(PANIC,
 					(errmsg("unexpected timeline ID %u (should be %u) in checkpoint record",
-							checkPoint.ThisTimeLineID, ThisTimeLineID)));
+							checkPoint.ThisTimeLineID, ThisTimeLineIDChecked)));
 
 		RecoveryRestartPoint(&checkPoint);
 	}
@@ -10443,10 +10455,10 @@ xlog_redo(XLogReaderState *record)
 		SpinLockRelease(&XLogCtl->info_lck);
 
 		/* TLI should not change in an on-line checkpoint */
-		if (checkPoint.ThisTimeLineID != ThisTimeLineID)
+		if (checkPoint.ThisTimeLineID != ThisTimeLineIDChecked)
 			ereport(PANIC,
 					(errmsg("unexpected timeline ID %u (should be %u) in checkpoint record",
-							checkPoint.ThisTimeLineID, ThisTimeLineID)));
+							checkPoint.ThisTimeLineID, ThisTimeLineIDChecked)));
 
 		RecoveryRestartPoint(&checkPoint);
 	}
@@ -10473,10 +10485,10 @@ xlog_redo(XLogReaderState *record)
 		 * We should've already switched to the new TLI before replaying this
 		 * record.
 		 */
-		if (xlrec.ThisTimeLineID != ThisTimeLineID)
+		if (xlrec.ThisTimeLineID != ThisTimeLineIDChecked)
 			ereport(PANIC,
 					(errmsg("unexpected timeline ID %u (should be %u) in checkpoint record",
-							xlrec.ThisTimeLineID, ThisTimeLineID)));
+							xlrec.ThisTimeLineID, ThisTimeLineIDChecked)));
 	}
 	else if (info == XLOG_NOOP)
 	{
@@ -10546,7 +10558,7 @@ xlog_redo(XLogReaderState *record)
 			if (ControlFile->minRecoveryPoint < lsn)
 			{
 				ControlFile->minRecoveryPoint = lsn;
-				ControlFile->minRecoveryPointTLI = ThisTimeLineID;
+				ControlFile->minRecoveryPointTLI = ThisTimeLineIDChecked;
 			}
 			ControlFile->backupStartPoint = InvalidXLogRecPtr;
 			ControlFile->backupEndRequired = false;
@@ -10587,7 +10599,7 @@ xlog_redo(XLogReaderState *record)
 		if (minRecoveryPoint != InvalidXLogRecPtr && minRecoveryPoint < lsn)
 		{
 			ControlFile->minRecoveryPoint = lsn;
-			ControlFile->minRecoveryPointTLI = ThisTimeLineID;
+			ControlFile->minRecoveryPointTLI = ThisTimeLineIDChecked;
 		}
 
 		CommitTsParameterChange(xlrec.track_commit_timestamp,
@@ -10800,7 +10812,7 @@ assign_xlog_sync_method(int new_sync_method, void *extra)
 				int			save_errno;
 
 				save_errno = errno;
-				XLogFileName(xlogfname, ThisTimeLineID, openLogSegNo,
+				XLogFileName(xlogfname, ThisTimeLineIDChecked, openLogSegNo,
 							 wal_segment_size);
 				errno = save_errno;
 				ereport(PANIC,
@@ -10876,7 +10888,7 @@ issue_xlog_fsync(int fd, XLogSegNo segno)
 		char		xlogfname[MAXFNAMELEN];
 		int			save_errno = errno;
 
-		XLogFileName(xlogfname, ThisTimeLineID, segno,
+		XLogFileName(xlogfname, ThisTimeLineIDChecked, segno,
 					 wal_segment_size);
 		errno = save_errno;
 		ereport(PANIC,
@@ -11721,7 +11733,7 @@ do_pg_stop_backup(char *labelfile, bool waitforarchive, TimeLineID *stoptli_p)
 		XLogBeginInsert();
 		XLogRegisterData((char *) (&startpoint), sizeof(startpoint));
 		stoppoint = XLogInsert(RM_XLOG_ID, XLOG_BACKUP_END);
-		stoptli = ThisTimeLineID;
+		stoptli = ThisTimeLineIDChecked;
 
 		/*
 		 * Force a switch to a new xlog segment file, so that the backup is
diff --git a/src/backend/access/transam/xlogarchive.c b/src/backend/access/transam/xlogarchive.c
index 26b023e754..24fe509fdd 100644
--- a/src/backend/access/transam/xlogarchive.c
+++ b/src/backend/access/transam/xlogarchive.c
@@ -502,7 +502,7 @@ XLogArchiveNotifySeg(XLogSegNo segno)
 {
 	char		xlog[MAXFNAMELEN];
 
-	XLogFileName(xlog, ThisTimeLineID, segno, wal_segment_size);
+	XLogFileName(xlog, ThisTimeLineIDChecked, segno, wal_segment_size);
 	XLogArchiveNotify(xlog);
 }
 
diff --git a/src/backend/access/transam/xlogfuncs.c b/src/backend/access/transam/xlogfuncs.c
index b98deb72ec..dada5675e5 100644
--- a/src/backend/access/transam/xlogfuncs.c
+++ b/src/backend/access/transam/xlogfuncs.c
@@ -469,7 +469,7 @@ pg_walfile_name_offset(PG_FUNCTION_ARGS)
 	 * xlogfilename
 	 */
 	XLByteToPrevSeg(locationpoint, xlogsegno, wal_segment_size);
-	XLogFileName(xlogfilename, ThisTimeLineID, xlogsegno, wal_segment_size);
+	XLogFileName(xlogfilename, ThisTimeLineIDChecked, xlogsegno, wal_segment_size);
 
 	values[0] = CStringGetTextDatum(xlogfilename);
 	isnull[0] = false;
@@ -511,7 +511,7 @@ pg_walfile_name(PG_FUNCTION_ARGS)
 						 "pg_walfile_name()")));
 
 	XLByteToPrevSeg(locationpoint, xlogsegno, wal_segment_size);
-	XLogFileName(xlogfilename, ThisTimeLineID, xlogsegno, wal_segment_size);
+	XLogFileName(xlogfilename, ThisTimeLineIDChecked, xlogsegno, wal_segment_size);
 
 	PG_RETURN_TEXT_P(cstring_to_text(xlogfilename));
 }
diff --git a/src/backend/access/transam/xlogutils.c b/src/backend/access/transam/xlogutils.c
index 88a1bfd939..802517c881 100644
--- a/src/backend/access/transam/xlogutils.c
+++ b/src/backend/access/transam/xlogutils.c
@@ -737,7 +737,7 @@ XLogReadDetermineTimeline(XLogReaderState *state, XLogRecPtr wantPage, uint32 wa
 	 * it looked up the timeline. There's nothing we can do about it if
 	 * StartupXLOG() renames it to .partial concurrently.
 	 */
-	if (state->currTLI == ThisTimeLineID && wantPage >= lastReadPage)
+	if (state->currTLI == ThisTimeLineIDChecked && wantPage >= lastReadPage)
 	{
 		Assert(state->currTLIValidUntil == InvalidXLogRecPtr);
 		return;
@@ -749,7 +749,7 @@ XLogReadDetermineTimeline(XLogReaderState *state, XLogRecPtr wantPage, uint32 wa
 	 * the current segment we can just keep reading.
 	 */
 	if (state->currTLIValidUntil != InvalidXLogRecPtr &&
-		state->currTLI != ThisTimeLineID &&
+		state->currTLI != ThisTimeLineIDChecked &&
 		state->currTLI != 0 &&
 		((wantPage + wantLength) / state->segcxt.ws_segsize) <
 		(state->currTLIValidUntil / state->segcxt.ws_segsize))
@@ -772,7 +772,7 @@ XLogReadDetermineTimeline(XLogReaderState *state, XLogRecPtr wantPage, uint32 wa
 		 * We need to re-read the timeline history in case it's been changed
 		 * by a promotion or replay from a cascaded replica.
 		 */
-		List	   *timelineHistory = readTimeLineHistory(ThisTimeLineID);
+		List	   *timelineHistory = readTimeLineHistory(ThisTimeLineIDChecked);
 		XLogRecPtr	endOfSegment;
 
 		endOfSegment = ((wantPage / state->segcxt.ws_segsize) + 1) *
@@ -870,8 +870,11 @@ read_local_xlog_page(XLogReaderState *state, XLogRecPtr targetPagePtr,
 		if (!RecoveryInProgress())
 			read_upto = GetFlushRecPtr();
 		else
+		{
 			read_upto = GetXLogReplayRecPtr(&ThisTimeLineID);
-		tli = ThisTimeLineID;
+			ThisTimeLineIDValid = true;
+		}
+		tli = ThisTimeLineIDChecked;
 
 		/*
 		 * Check which timeline to get the record from.
@@ -899,7 +902,7 @@ read_local_xlog_page(XLogReaderState *state, XLogRecPtr targetPagePtr,
 		 */
 		XLogReadDetermineTimeline(state, targetPagePtr, reqLen);
 
-		if (state->currTLI == ThisTimeLineID)
+		if (state->currTLI == ThisTimeLineIDChecked)
 		{
 
 			if (loc <= read_upto)
diff --git a/src/backend/replication/basebackup.c b/src/backend/replication/basebackup.c
index b31c36d918..7d6370d399 100644
--- a/src/backend/replication/basebackup.c
+++ b/src/backend/replication/basebackup.c
@@ -502,9 +502,9 @@ perform_base_backup(basebackup_options *opt)
 		 * including them.
 		 */
 		XLByteToSeg(startptr, startsegno, wal_segment_size);
-		XLogFileName(firstoff, ThisTimeLineID, startsegno, wal_segment_size);
+		XLogFileName(firstoff, ThisTimeLineIDChecked, startsegno, wal_segment_size);
 		XLByteToPrevSeg(endptr, endsegno, wal_segment_size);
-		XLogFileName(lastoff, ThisTimeLineID, endsegno, wal_segment_size);
+		XLogFileName(lastoff, ThisTimeLineIDChecked, endsegno, wal_segment_size);
 
 		dir = AllocateDir("pg_wal");
 		while ((de = ReadDir(dir, "pg_wal")) != NULL)
@@ -528,7 +528,7 @@ perform_base_backup(basebackup_options *opt)
 		 * Before we go any further, check that none of the WAL segments we
 		 * need were removed.
 		 */
-		CheckXLogRemoved(startsegno, ThisTimeLineID);
+		CheckXLogRemoved(startsegno, ThisTimeLineIDChecked);
 
 		/*
 		 * Sort the WAL filenames.  We want to send the files in order from
@@ -555,7 +555,7 @@ perform_base_backup(basebackup_options *opt)
 		{
 			char		startfname[MAXFNAMELEN];
 
-			XLogFileName(startfname, ThisTimeLineID, startsegno,
+			XLogFileName(startfname, ThisTimeLineIDChecked, startsegno,
 						 wal_segment_size);
 			ereport(ERROR,
 					(errmsg("could not find WAL file \"%s\"", startfname)));
@@ -571,7 +571,7 @@ perform_base_backup(basebackup_options *opt)
 			{
 				char		nextfname[MAXFNAMELEN];
 
-				XLogFileName(nextfname, ThisTimeLineID, nextsegno,
+				XLogFileName(nextfname, ThisTimeLineIDChecked, nextsegno,
 							 wal_segment_size);
 				ereport(ERROR,
 						(errmsg("could not find WAL file \"%s\"", nextfname)));
@@ -581,7 +581,7 @@ perform_base_backup(basebackup_options *opt)
 		{
 			char		endfname[MAXFNAMELEN];
 
-			XLogFileName(endfname, ThisTimeLineID, endsegno, wal_segment_size);
+			XLogFileName(endfname, ThisTimeLineIDChecked, endsegno, wal_segment_size);
 			ereport(ERROR,
 					(errmsg("could not find WAL file \"%s\"", endfname)));
 		}
diff --git a/src/backend/replication/logical/logicalfuncs.c b/src/backend/replication/logical/logicalfuncs.c
index e59939aad1..379dc0c1d9 100644
--- a/src/backend/replication/logical/logicalfuncs.c
+++ b/src/backend/replication/logical/logicalfuncs.c
@@ -214,7 +214,11 @@ pg_logical_slot_get_changes_guts(FunctionCallInfo fcinfo, bool confirm, bool bin
 	if (!RecoveryInProgress())
 		end_of_wal = GetFlushRecPtr();
 	else
+	{
+		Assert(ThisTimeLineIDValid);
 		end_of_wal = GetXLogReplayRecPtr(&ThisTimeLineID);
+		ThisTimeLineIDValid = true;
+	}
 
 	ReplicationSlotAcquire(NameStr(*name), true);
 
diff --git a/src/backend/replication/slotfuncs.c b/src/backend/replication/slotfuncs.c
index 17df99c2ac..a3f8c35aec 100644
--- a/src/backend/replication/slotfuncs.c
+++ b/src/backend/replication/slotfuncs.c
@@ -627,7 +627,11 @@ pg_replication_slot_advance(PG_FUNCTION_ARGS)
 	if (!RecoveryInProgress())
 		moveto = Min(moveto, GetFlushRecPtr());
 	else
+	{
+		Assert(ThisTimeLineIDValid);
 		moveto = Min(moveto, GetXLogReplayRecPtr(&ThisTimeLineID));
+		ThisTimeLineIDValid = true;
+	}
 
 	/* Acquire the slot so we "own" it */
 	ReplicationSlotAcquire(NameStr(*slotname), true);
diff --git a/src/backend/replication/walreceiver.c b/src/backend/replication/walreceiver.c
index b90e5ca98e..0ac4e29fd6 100644
--- a/src/backend/replication/walreceiver.c
+++ b/src/backend/replication/walreceiver.c
@@ -395,6 +395,7 @@ WalReceiverMain(void)
 		options.slotname = slotname[0] != '\0' ? slotname : NULL;
 		options.proto.physical.startpointTLI = startpointTLI;
 		ThisTimeLineID = startpointTLI;
+		ThisTimeLineIDValid = true;
 		if (walrcv_startstreaming(wrconn, &options))
 		{
 			if (first_stream)
@@ -893,7 +894,7 @@ XLogWalRcvWrite(char *buf, Size nbytes, XLogRecPtr recptr)
 			/* Create/use new log file */
 			XLByteToSeg(recptr, recvSegNo, wal_segment_size);
 			recvFile = XLogFileInit(recvSegNo);
-			recvFileTLI = ThisTimeLineID;
+			recvFileTLI = ThisTimeLineIDChecked;
 		}
 
 		/* Calculate the start offset of the received logs */
@@ -972,7 +973,7 @@ XLogWalRcvFlush(bool dying)
 		{
 			walrcv->latestChunkStart = walrcv->flushedUpto;
 			walrcv->flushedUpto = LogstreamResult.Flush;
-			walrcv->receivedTLI = ThisTimeLineID;
+			walrcv->receivedTLI = ThisTimeLineIDChecked;
 		}
 		SpinLockRelease(&walrcv->mutex);
 
diff --git a/src/backend/replication/walsender.c b/src/backend/replication/walsender.c
index b811a5c0ef..73a6e06e6b 100644
--- a/src/backend/replication/walsender.c
+++ b/src/backend/replication/walsender.c
@@ -440,7 +440,7 @@ IdentifySystem(void)
 	values[0] = CStringGetTextDatum(sysid);
 
 	/* column 2: timeline */
-	values[1] = Int32GetDatum(ThisTimeLineID);
+	values[1] = Int32GetDatum(ThisTimeLineIDChecked);
 
 	/* column 3: wal location */
 	values[2] = CStringGetTextDatum(xloc);
@@ -628,7 +628,7 @@ StartReplication(StartReplicationCmd *cmd)
 		XLogRecPtr	switchpoint;
 
 		sendTimeLine = cmd->timeline;
-		if (sendTimeLine == ThisTimeLineID)
+		if (sendTimeLine == ThisTimeLineIDChecked)
 		{
 			sendTimeLineIsHistoric = false;
 			sendTimeLineValidUpto = InvalidXLogRecPtr;
@@ -643,7 +643,7 @@ StartReplication(StartReplicationCmd *cmd)
 			 * Check that the timeline the client requested exists, and the
 			 * requested start location is on that timeline.
 			 */
-			timeLineHistory = readTimeLineHistory(ThisTimeLineID);
+			timeLineHistory = readTimeLineHistory(ThisTimeLineIDChecked);
 			switchpoint = tliSwitchPoint(cmd->timeline, timeLineHistory,
 										 &sendTimeLineNextTLI);
 			list_free_deep(timeLineHistory);
@@ -682,7 +682,7 @@ StartReplication(StartReplicationCmd *cmd)
 	}
 	else
 	{
-		sendTimeLine = ThisTimeLineID;
+		sendTimeLine = ThisTimeLineIDChecked;
 		sendTimeLineValidUpto = InvalidXLogRecPtr;
 		sendTimeLineIsHistoric = false;
 	}
@@ -812,7 +812,7 @@ logical_read_xlog_page(XLogReaderState *state, XLogRecPtr targetPagePtr, int req
 	XLogSegNo	segno;
 
 	XLogReadDetermineTimeline(state, targetPagePtr, reqLen);
-	sendTimeLineIsHistoric = (state->currTLI != ThisTimeLineID);
+	sendTimeLineIsHistoric = (state->currTLI != ThisTimeLineIDChecked);
 	sendTimeLine = state->currTLI;
 	sendTimeLineValidUpto = state->currTLIValidUntil;
 	sendTimeLineNextTLI = state->nextTLI;
@@ -945,7 +945,7 @@ CreateReplicationSlot(CreateReplicationSlotCmd *cmd)
 
 	/* setup state for WalSndSegmentOpen */
 	sendTimeLineIsHistoric = false;
-	sendTimeLine = ThisTimeLineID;
+	sendTimeLine = ThisTimeLineID; // XXX ThisTimeLineIDChecked fails assertion, ThisTimeLineID = 0!!!
 
 	if (cmd->kind == REPLICATION_KIND_PHYSICAL)
 	{
@@ -2618,7 +2618,7 @@ XLogSendPhysical(void)
 			 * still the one recovery is recovering from? ThisTimeLineID was
 			 * updated by the GetStandbyFlushRecPtr() call above.
 			 */
-			if (sendTimeLine != ThisTimeLineID)
+			if (sendTimeLine != ThisTimeLineIDChecked)
 				becameHistoric = true;
 		}
 
@@ -2631,7 +2631,7 @@ XLogSendPhysical(void)
 			 */
 			List	   *history;
 
-			history = readTimeLineHistory(ThisTimeLineID);
+			history = readTimeLineHistory(ThisTimeLineIDChecked);
 			sendTimeLineValidUpto = tliSwitchPoint(sendTimeLine, history, &sendTimeLineNextTLI);
 
 			Assert(sendTimeLine < sendTimeLineNextTLI);
@@ -2989,9 +2989,10 @@ GetStandbyFlushRecPtr(void)
 	replayPtr = GetXLogReplayRecPtr(&replayTLI);
 
 	ThisTimeLineID = replayTLI;
+	ThisTimeLineIDValid = true;
 
 	result = replayPtr;
-	if (receiveTLI == ThisTimeLineID && receivePtr > replayPtr)
+	if (receiveTLI == ThisTimeLineIDChecked && receivePtr > replayPtr)
 		result = receivePtr;
 
 	return result;
diff --git a/src/include/access/xlog.h b/src/include/access/xlog.h
index 5e2c94a05f..317e110488 100644
--- a/src/include/access/xlog.h
+++ b/src/include/access/xlog.h
@@ -29,8 +29,12 @@
 #define SYNC_METHOD_OPEN_DSYNC	4	/* for O_DSYNC */
 extern int	sync_method;
 
+extern PGDLLIMPORT bool ThisTimeLineIDValid;
 extern PGDLLIMPORT TimeLineID ThisTimeLineID;	/* current TLI */
 
+#define ThisTimeLineIDChecked \
+	(AssertMacro(ThisTimeLineIDValid), ThisTimeLineID)
+
 /*
  * Recovery target type.
  * Only set during a Point in Time recovery, not when in standby mode.
-- 
2.24.3 (Apple Git-128)

