From 5bf021226d9742a6fefbcb33e54f7ef044d8fbcc Mon Sep 17 00:00:00 2001
From: Amul Sul <amul.sul@enterprisedb.com>
Date: Thu, 30 Sep 2021 06:29:06 -0400
Subject: [PATCH v38 4/4] Remove dependencies on startup-process specifical
 variables.

To make XLogAcceptWrites(), need to dependency on few global and local
variable spcific to startup process.

Global variables are abortedRecPtr, missingContrecPtr,
ArchiveRecoveryRequested and LocalPromoteIsTriggered, whereas
LocalPromoteIsTriggered can be accessed in any other process using
existing PromoteIsTriggered().  ArchiveRecoveryRequested is made
accessible by copying into shared memory.  abortedRecPtr and
missingContrecPtr can get from the existing shared memory values but
for that, we need a flag indicating that broken records was found
previously and OVERWRITE_CONTRECORD message needs to be written when
WAL writes permitted, added a flag for the same.

XLogAcceptWrites() accepts two argument as EndOfLogTLI and EndOfLog
which are local to StartupXLOG(). Instead of passing as an argument
XLogCtl->replayEndTLI and XLogCtl->lastSegSwitchLSN from the shared
memory can be used as an replacement to EndOfLogTLI and EndOfLog
respectively.  XLogCtl->lastSegSwitchLSN is not going to change until
we use it. That changes only when the current WAL segment gets full
which never going to happen because of two reasons, first WAL writes
are disabled for other processes until XLogAcceptWrites() finishes and
other reasons before use of lastSegSwitchLSN, XLogAcceptWrites() is
writes fix size wal records as full-page write and record for either
recovery end or checkpoint which not going to fill up the 16MB wal
segment.

EndOfLogTLI in the StartupXLOG() is the timeline ID of the last record
that xlogreader reads, but this xlogreader was simply re-fetching the
last record which we have replied in redo loop if it was in recovery,
if not in recovery, we don't need to worry since this value is needed
only in case of ArchiveRecoveryRequested = true, which implicitly
forces redo and sets XLogCtl->replayEndTLI value.
---
 src/backend/access/transam/xlog.c | 90 ++++++++++++++++++++++++-------
 1 file changed, 72 insertions(+), 18 deletions(-)

diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c
index cdfec248081..b9596ca005c 100644
--- a/src/backend/access/transam/xlog.c
+++ b/src/backend/access/transam/xlog.c
@@ -668,6 +668,13 @@ typedef struct XLogCtlData
 	 */
 	bool		SharedPromoteIsTriggered;
 
+	/*
+	 * SharedArchiveRecoveryRequested exports the value of the
+	 * ArchiveRecoveryRequested flag to be share which is otherwise valid only
+	 * in the startup process.
+	 */
+	bool		SharedArchiveRecoveryRequested;
+
 	/*
 	 * WalWriterSleeping indicates whether the WAL writer is currently in
 	 * low-power mode (and hence should be nudged if an async commit occurs).
@@ -706,9 +713,10 @@ typedef struct XLogCtlData
 
 	/*
 	 * lastReplayedEndRecPtr points to end+1 of the last record successfully
-	 * replayed. When we're currently replaying a record, ie. in a redo
-	 * function, replayEndRecPtr points to the end+1 of the record being
-	 * replayed, otherwise it's equal to lastReplayedEndRecPtr.
+	 * replayed and that could be point where broken record starts (if exists).
+	 * When we're currently replaying a record, ie. in a redo function,
+	 * replayEndRecPtr points to the end+1 of the record being replayed,
+	 * otherwise it's equal to lastReplayedEndRecPtr.
 	 */
 	XLogRecPtr	lastReplayedEndRecPtr;
 	TimeLineID	lastReplayedTLI;
@@ -717,6 +725,12 @@ typedef struct XLogCtlData
 	/* timestamp of last COMMIT/ABORT record replayed (or being replayed) */
 	TimestampTz recoveryLastXTime;
 
+	/*
+	 * overwriteContrecord indicates if a record was found to be broken at the
+	 * end of recovery and OVERWRITE_CONTRECORD message needs to write.
+	 */
+	bool		overwriteContrecord;
+
 	/*
 	 * timestamp of when we started replaying the current chunk of WAL data,
 	 * only relevant for replication or archive recovery
@@ -889,8 +903,7 @@ static MemoryContext walDebugCxt = NULL;
 static void readRecoverySignalFile(void);
 static void validateRecoveryParameters(void);
 static void exitArchiveRecovery(TimeLineID endTLI, XLogRecPtr endOfLog);
-static void CleanupAfterArchiveRecovery(TimeLineID EndOfLogTLI,
-										XLogRecPtr EndOfLog);
+static void CleanupAfterArchiveRecovery(void);
 static bool recoveryStopsBefore(XLogReaderState *record);
 static bool recoveryStopsAfter(XLogReaderState *record);
 static char *getRecoveryStopReason(void);
@@ -939,7 +952,7 @@ static void UpdateMinRecoveryPoint(XLogRecPtr lsn, bool force);
 static XLogRecord *ReadRecord(XLogReaderState *xlogreader,
 							  int emode, bool fetching_ckpt);
 static void CheckRecoveryConsistency(void);
-static bool XLogAcceptWrites(TimeLineID EndOfLogTLI, XLogRecPtr EndOfLog);
+static bool XLogAcceptWrites(void);
 static bool PerformRecoveryXLogAction(void);
 static XLogRecord *ReadCheckpointRecord(XLogReaderState *xlogreader,
 										XLogRecPtr RecPtr, int whichChkpt, bool report);
@@ -5267,6 +5280,7 @@ XLOGShmemInit(void)
 	XLogCtl->SharedHotStandbyActive = false;
 	XLogCtl->InstallXLogFileSegmentActive = false;
 	XLogCtl->SharedPromoteIsTriggered = false;
+	XLogCtl->SharedArchiveRecoveryRequested = false;
 	XLogCtl->WalWriterSleeping = false;
 
 	SpinLockInit(&XLogCtl->Insert.insertpos_lck);
@@ -5548,6 +5562,11 @@ readRecoverySignalFile(void)
 		ereport(FATAL,
 				(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
 				 errmsg("standby mode is not supported by single-user servers")));
+
+	/*
+	 * Remember archive recovery request in shared memory state.
+	 */
+	XLogCtl->SharedArchiveRecoveryRequested = ArchiveRecoveryRequested;
 }
 
 static void
@@ -5739,8 +5758,10 @@ exitArchiveRecovery(TimeLineID endTLI, XLogRecPtr endOfLog)
  * Perform cleanup actions at the conclusion of archive recovery.
  */
 static void
-CleanupAfterArchiveRecovery(TimeLineID EndOfLogTLI, XLogRecPtr EndOfLog)
+CleanupAfterArchiveRecovery(void)
 {
+	XLogRecPtr	EndOfLog;
+
 	/*
 	 * Execute the recovery_end_command, if any.
 	 */
@@ -5757,6 +5778,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.
 	 */
+	(void) GetLastSegSwitchData(&EndOfLog);
 	RemoveNonParentXlogFiles(EndOfLog, ThisTimeLineID);
 
 	/*
@@ -5791,6 +5813,7 @@ CleanupAfterArchiveRecovery(TimeLineID EndOfLogTLI, XLogRecPtr EndOfLog)
 	{
 		char		origfname[MAXFNAMELEN];
 		XLogSegNo	endLogSegNo;
+		TimeLineID	EndOfLogTLI = XLogCtl->replayEndTLI;
 
 		XLByteToPrevSeg(EndOfLog, endLogSegNo, wal_segment_size);
 		XLogFileName(origfname, EndOfLogTLI, endLogSegNo, wal_segment_size);
@@ -7965,6 +7988,27 @@ StartupXLOG(void)
 	{
 		Assert(!XLogRecPtrIsInvalid(abortedRecPtr));
 		EndOfLog = missingContrecPtr;
+
+		/*
+		 * Set broken record found flag in shared memory. This process might
+		 * unable to write an OVERWRITE_CONTRECORD message because of WAL write
+		 * restriction.  Storing in shared memory helps that get written later
+		 * by another process when WAL writes enabled.
+		 */
+		XLogCtl->overwriteContrecord = true;
+
+		/*
+		 * While writing OVERWRITE_CONTRECORD message abortedRecPtr and
+		 * missingContrecPtr values need to be restored, and that can be fetched
+		 * from the shared memory as lastReplayedEndRecPtr is the abortedRecPtr
+		 * and missingContrecPtr is the EndOfLog which going to be stored at a
+		 * bunch of places in the shared memory (e.g. lastSegSwitchLSN which not
+		 * going to change before the point where the OVERWRITE_CONTRECORD
+		 * message gets written).
+		 */
+		Assert(!XLogRecPtrIsInvalid(abortedRecPtr == XLogCtl->lastReplayedEndRecPtr));
+		abortedRecPtr = InvalidXLogRecPtr;
+		missingContrecPtr = InvalidXLogRecPtr;
 	}
 
 	/*
@@ -8071,7 +8115,7 @@ StartupXLOG(void)
 	Insert->fullPageWrites = lastFullPageWrites;
 
 	/* Prepare to accept WAL writes. */
-	promoted = XLogAcceptWrites(EndOfLogTLI, EndOfLog);
+	promoted = XLogAcceptWrites();
 
 	/*
 	 * All done with end-of-recovery actions.
@@ -8131,19 +8175,29 @@ StartupXLOG(void)
  * Prepare to accept WAL writes.
  */
 static bool
-XLogAcceptWrites(TimeLineID EndOfLogTLI, XLogRecPtr EndOfLog)
+XLogAcceptWrites(void)
 {
 	bool		promoted = false;
 
 	LocalSetXLogInsertAllowed();
 
 	/* If necessary, write overwrite-contrecord before doing anything else */
-	if (!XLogRecPtrIsInvalid(abortedRecPtr))
+	if (!XLogRecPtrIsInvalid(XLogCtl->overwriteContrecord))
 	{
-		Assert(!XLogRecPtrIsInvalid(missingContrecPtr));
-		CreateOverwriteContrecordRecord(abortedRecPtr);
-		abortedRecPtr = InvalidXLogRecPtr;
-		missingContrecPtr = InvalidXLogRecPtr;
+		/*
+		 * Restore missingContrecPtr, needed to set
+		 * XLP_FIRST_IS_OVERWRITE_CONTRECORD flag on the page header where
+		 * overwrite-contrecord get written. See AdvanceXLInsertBuffer().
+		 */
+		GetLastSegSwitchData(&missingContrecPtr);
+
+		/*
+		 * Start writing overwrite-contrecord after the point where the last
+		 * valid replyed record ended.
+		 */
+		CreateOverwriteContrecordRecord(XLogCtl->lastReplayedEndRecPtr);
+
+		XLogCtl->overwriteContrecord = false;
 	}
 
 	/* Write an XLOG_FPW_CHANGE record */
@@ -8161,8 +8215,8 @@ XLogAcceptWrites(TimeLineID EndOfLogTLI, XLogRecPtr EndOfLog)
 		promoted = PerformRecoveryXLogAction();
 
 	/* If this is archive recovery, perform post-recovery cleanup actions. */
-	if (ArchiveRecoveryRequested)
-		CleanupAfterArchiveRecovery(EndOfLogTLI, EndOfLog);
+	if (XLogCtl->SharedArchiveRecoveryRequested)
+		CleanupAfterArchiveRecovery();
 
 	/*
 	 * If any of the critical GUCs have changed, log them before we allow
@@ -8304,8 +8358,8 @@ PerformRecoveryXLogAction(void)
 	 * a full checkpoint. A checkpoint is requested later, after we're fully out
 	 * of recovery mode and already accepting queries.
 	 */
-	if (ArchiveRecoveryRequested && IsUnderPostmaster &&
-		LocalPromoteIsTriggered)
+	if (XLogCtl->SharedArchiveRecoveryRequested && IsUnderPostmaster &&
+		PromoteIsTriggered())
 	{
 		promoted = true;
 
-- 
2.18.0

