From eaf661c022f82217d33cfdb3776c69675f82ac67 Mon Sep 17 00:00:00 2001
From: Mikhail Litsarev <m.litsarev@postgrespro.ru>
Date: Fri, 10 Jan 2025 21:23:02 +0300
Subject: [PATCH] Replace recovery boolean flags with a bits32 set.

Move local booleans ArchiveRecoveryRequested, InArchiveRecovery,
StandbyModeRequested, StandbyMode, LocalHotStandbyActive,
LocalPromoteIsTriggered into localRecoveryFlags bitset.

Move SharedHotStandbyActive, SharedPromoteIsTriggered members of
XLogRecoveryCtlData into sharedRecoveryFlags bitset.

Refactor code according to the changes.
---
 src/backend/access/transam/timeline.c      |   6 +-
 src/backend/access/transam/xlog.c          |  26 +--
 src/backend/access/transam/xlogarchive.c   |   4 +-
 src/backend/access/transam/xlogrecovery.c  | 221 ++++++++++-----------
 src/backend/replication/logical/slotsync.c |   2 +-
 src/include/access/xlog_internal.h         |   7 +-
 src/include/access/xlogrecovery.h          |  34 +++-
 7 files changed, 161 insertions(+), 139 deletions(-)

diff --git a/src/backend/access/transam/timeline.c b/src/backend/access/transam/timeline.c
index a27f27cc037..c9f53c4b667 100644
--- a/src/backend/access/transam/timeline.c
+++ b/src/backend/access/transam/timeline.c
@@ -93,7 +93,7 @@ readTimeLineHistory(TimeLineID targetTLI)
 		return list_make1(entry);
 	}
 
-	if (ArchiveRecoveryRequested)
+	if (ArchiveRecoveryRequested())
 	{
 		TLHistoryFileName(histfname, targetTLI);
 		fromArchive =
@@ -229,7 +229,7 @@ existsTimeLineHistory(TimeLineID probeTLI)
 	if (probeTLI == 1)
 		return false;
 
-	if (ArchiveRecoveryRequested)
+	if (ArchiveRecoveryRequested())
 	{
 		TLHistoryFileName(histfname, probeTLI);
 		RestoreArchivedFile(path, histfname, "RECOVERYHISTORY", 0, false);
@@ -331,7 +331,7 @@ writeTimeLineHistory(TimeLineID newTLI, TimeLineID parentTLI,
 	/*
 	 * If a history file exists for the parent, copy it verbatim
 	 */
-	if (ArchiveRecoveryRequested)
+	if (ArchiveRecoveryRequested())
 	{
 		TLHistoryFileName(histfname, parentTLI);
 		RestoreArchivedFile(path, histfname, "RECOVERYHISTORY", 0, false);
diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c
index bf3dbda901d..2d6615d94d0 100644
--- a/src/backend/access/transam/xlog.c
+++ b/src/backend/access/transam/xlog.c
@@ -5388,7 +5388,7 @@ CheckRequiredParameterValues(void)
 	 * For archive recovery, the WAL must be generated with at least 'replica'
 	 * wal_level.
 	 */
-	if (ArchiveRecoveryRequested && ControlFile->wal_level == WAL_LEVEL_MINIMAL)
+	if (ArchiveRecoveryRequested() && ControlFile->wal_level == WAL_LEVEL_MINIMAL)
 	{
 		ereport(FATAL,
 				(errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
@@ -5401,7 +5401,7 @@ CheckRequiredParameterValues(void)
 	 * For Hot Standby, the WAL must be generated with 'replica' mode, and we
 	 * must have at least as many backend slots as the primary.
 	 */
-	if (ArchiveRecoveryRequested && EnableHotStandby)
+	if (ArchiveRecoveryRequested() && EnableHotStandby)
 	{
 		/* We ignore autovacuum_worker_slots when we make this test. */
 		RecoveryRequiresIntParameter("max_connections",
@@ -5561,8 +5561,8 @@ StartupXLOG(void)
 	 *
 	 * InitWalRecovery analyzes the control file and the backup label file, if
 	 * any.  It updates the in-memory ControlFile buffer according to the
-	 * starting checkpoint, and sets InRecovery and ArchiveRecoveryRequested.
-	 * It also applies the tablespace map file, if any.
+	 * starting checkpoint, and sets SX_ARCHIVE_RECOVERY_REQUESTED and
+	 * InRecovery. It also applies the tablespace map file, if any.
 	 */
 	InitWalRecovery(ControlFile, &wasShutdown,
 					&haveBackupLabel, &haveTblspcMap);
@@ -5694,7 +5694,7 @@ StartupXLOG(void)
 	{
 		/* Initialize state for RecoveryInProgress() */
 		SpinLockAcquire(&XLogCtl->info_lck);
-		if (InArchiveRecovery)
+		if (InArchiveRecovery())
 			XLogCtl->SharedRecoveryState = RECOVERY_STATE_ARCHIVE;
 		else
 			XLogCtl->SharedRecoveryState = RECOVERY_STATE_CRASH;
@@ -5747,7 +5747,7 @@ StartupXLOG(void)
 		 * startup process to think that there are still invalid page
 		 * references when checking for data consistency.
 		 */
-		if (InArchiveRecovery)
+		if (InArchiveRecovery())
 		{
 			LocalMinRecoveryPoint = ControlFile->minRecoveryPoint;
 			LocalMinRecoveryPointTLI = ControlFile->minRecoveryPointTLI;
@@ -5781,7 +5781,7 @@ StartupXLOG(void)
 		 * control file and we've established a recovery snapshot from a
 		 * running-xacts WAL record.
 		 */
-		if (ArchiveRecoveryRequested && EnableHotStandby)
+		if (ArchiveRecoveryRequested() && EnableHotStandby)
 		{
 			TransactionId *xids;
 			int			nxids;
@@ -5894,7 +5894,7 @@ StartupXLOG(void)
 		 * recover from an online backup but never called pg_backup_stop(), or
 		 * you didn't archive all the WAL needed.
 		 */
-		if (ArchiveRecoveryRequested || ControlFile->backupEndRequired)
+		if (ArchiveRecoveryRequested() || ControlFile->backupEndRequired)
 		{
 			if (!XLogRecPtrIsInvalid(ControlFile->backupStartPoint) || ControlFile->backupEndRequired)
 				ereport(FATAL,
@@ -5946,7 +5946,7 @@ StartupXLOG(void)
 	 * In a normal crash recovery, we can just extend the timeline we were in.
 	 */
 	newTLI = endOfRecoveryInfo->lastRecTLI;
-	if (ArchiveRecoveryRequested)
+	if (ArchiveRecoveryRequested())
 	{
 		newTLI = findNewestTimeLine(recoveryTargetTLI) + 1;
 		ereport(LOG,
@@ -6139,7 +6139,7 @@ StartupXLOG(void)
 	XLogReportParameters();
 
 	/* If this is archive recovery, perform post-recovery cleanup actions. */
-	if (ArchiveRecoveryRequested)
+	if (ArchiveRecoveryRequested())
 		CleanupAfterArchiveRecovery(EndOfLogTLI, EndOfLog, newTLI);
 
 	/*
@@ -6298,7 +6298,7 @@ PerformRecoveryXLogAction(void)
 	 * of a full checkpoint. A checkpoint is requested later, after we're
 	 * fully out of recovery mode and already accepting queries.
 	 */
-	if (ArchiveRecoveryRequested && IsUnderPostmaster &&
+	if (ArchiveRecoveryRequested() && IsUnderPostmaster &&
 		PromoteIsTriggered())
 	{
 		promoted = true;
@@ -8292,7 +8292,7 @@ xlog_redo(XLogReaderState *record)
 		 * record, the backup was canceled and the end-of-backup record will
 		 * never arrive.
 		 */
-		if (ArchiveRecoveryRequested &&
+		if (ArchiveRecoveryRequested() &&
 			!XLogRecPtrIsInvalid(ControlFile->backupStartPoint) &&
 			XLogRecPtrIsInvalid(ControlFile->backupEndPoint))
 			ereport(PANIC,
@@ -8533,7 +8533,7 @@ xlog_redo(XLogReaderState *record)
 		 * local copies cannot be updated as long as crash recovery is
 		 * happening and we expect all the WAL to be replayed.
 		 */
-		if (InArchiveRecovery)
+		if (InArchiveRecovery())
 		{
 			LocalMinRecoveryPoint = ControlFile->minRecoveryPoint;
 			LocalMinRecoveryPointTLI = ControlFile->minRecoveryPointTLI;
diff --git a/src/backend/access/transam/xlogarchive.c b/src/backend/access/transam/xlogarchive.c
index 1ef1713c91a..ad379acc30a 100644
--- a/src/backend/access/transam/xlogarchive.c
+++ b/src/backend/access/transam/xlogarchive.c
@@ -68,7 +68,7 @@ RestoreArchivedFile(char *path, const char *xlogfname,
 	 * Ignore restore_command when not in archive recovery (meaning we are in
 	 * crash recovery).
 	 */
-	if (!ArchiveRecoveryRequested)
+	if (!ArchiveRecoveryRequested())
 		goto not_available;
 
 	/* In standby mode, restore_command might not be supplied */
@@ -205,7 +205,7 @@ RestoreArchivedFile(char *path, const char *xlogfname,
 				 * incorrectly conclude we've reached the end of WAL and we're
 				 * done recovering ...
 				 */
-				if (StandbyMode && stat_buf.st_size < expectedSize)
+				if (InStandbyMode() && stat_buf.st_size < expectedSize)
 					elevel = DEBUG1;
 				else
 					elevel = FATAL;
diff --git a/src/backend/access/transam/xlogrecovery.c b/src/backend/access/transam/xlogrecovery.c
index 0bbe2eea206..e75c7891fd5 100644
--- a/src/backend/access/transam/xlogrecovery.c
+++ b/src/backend/access/transam/xlogrecovery.c
@@ -123,30 +123,7 @@ TimeLineID	recoveryTargetTLI = 0;
 static List *expectedTLEs;
 static TimeLineID curFileTLI;
 
-/*
- * When ArchiveRecoveryRequested is set, archive recovery was requested,
- * ie. signal files were present.  When InArchiveRecovery is set, we are
- * currently recovering using offline XLOG archives.  These variables are only
- * valid in the startup process.
- *
- * When ArchiveRecoveryRequested is true, but InArchiveRecovery is false, we're
- * currently performing crash recovery using only XLOG files in pg_wal, but
- * will switch to using offline XLOG archives as soon as we reach the end of
- * WAL in pg_wal.
- */
-bool		ArchiveRecoveryRequested = false;
-bool		InArchiveRecovery = false;
-
-/*
- * When StandbyModeRequested is set, standby mode was requested, i.e.
- * standby.signal file was present.  When StandbyMode is set, we are currently
- * in standby mode.  These variables are only valid in the startup process.
- * They work similarly to ArchiveRecoveryRequested and InArchiveRecovery.
- */
-static bool StandbyModeRequested = false;
-bool		StandbyMode = false;
-
-/* was a signal file present at startup? */
+/* Was a signal file present at startup? */
 static bool standby_signal_file_found = false;
 static bool recovery_signal_file_found = false;
 
@@ -170,16 +147,19 @@ static XLogRecPtr RedoStartLSN = InvalidXLogRecPtr;
 static TimeLineID RedoStartTLI = 0;
 
 /*
- * Local copy of SharedHotStandbyActive variable. False actually means "not
- * known, need to check the shared state".
- */
-static bool LocalHotStandbyActive = false;
-
-/*
- * Local copy of SharedPromoteIsTriggered variable. False actually means "not
- * known, need to check the shared state".
+ * Local flags:
+ * SX_ARCHIVE_RECOVERY_REQUESTED
+ * SX_IN_ARCHIVE_RECOVERY
+ * SX_STANDBY_MODE_REQUESTED
+ * SX_IN_STANDBY_MODE
+ *
+ * and local copies of sharedRecoveryFlags:
+ * SX_HOT_STANDBY_ACTIVE,
+ * SX_PROMOTE_IS_TRIGGERED.
+ * If some flag is not set, that actually means "not known, need to check
+ * the shared state".
  */
-static bool LocalPromoteIsTriggered = false;
+static bits32 localRecoveryFlags = 0;
 
 /* Has the recovery code requested a walreceiver wakeup? */
 static bool doRequestWalReceiverReply;
@@ -297,23 +277,16 @@ bool		reachedConsistency = false;
 static char *replay_image_masked = NULL;
 static char *primary_image_masked = NULL;
 
-
 /*
  * Shared-memory state for WAL recovery.
  */
 typedef struct XLogRecoveryCtlData
 {
 	/*
-	 * SharedHotStandbyActive indicates if we allow hot standby queries to be
-	 * run.  Protected by info_lck.
+	 * The bit array stores the following states
+	 * SX_HOT_STANDBY_ACTIVE, SX_PROMOTE_IS_TRIGGERED. Protected by info_lck.
 	 */
-	bool		SharedHotStandbyActive;
-
-	/*
-	 * SharedPromoteIsTriggered indicates if a standby promotion has been
-	 * triggered.  Protected by info_lck.
-	 */
-	bool		SharedPromoteIsTriggered;
+	bits32		sharedRecoveryFlags;
 
 	/*
 	 * recoveryWakeupLatch is used to wake up the startup process to continue
@@ -440,6 +413,7 @@ static bool HotStandbyActiveInReplay(void);
 static void SetCurrentChunkStartTime(TimestampTz xtime);
 static void SetLatestXTime(TimestampTz xtime);
 
+static bool StandbyModeRequested(void);
 /*
  * Initialization of shared memory for WAL recovery
  */
@@ -477,7 +451,7 @@ XLogRecoveryShmemInit(void)
 static void
 EnableStandbyMode(void)
 {
-	StandbyMode = true;
+	localRecoveryFlags |= SX_IN_STANDBY_MODE;
 
 	/*
 	 * To avoid server log bloat, we don't report recovery progress in a
@@ -505,8 +479,8 @@ EnableStandbyMode(void)
  * disk does after initializing other subsystems, but before calling
  * PerformWalRecovery().
  *
- * This initializes some global variables like ArchiveRecoveryRequested, and
- * StandbyModeRequested and InRecovery.
+ * This initializes some flags like SX_ARCHIVE_RECOVERY_REQUESTED and
+ * SX_STABDBY_MODE_REQUESTED and global variable InRecovery.
  */
 void
 InitWalRecovery(ControlFileData *ControlFile, bool *wasShutdown_ptr,
@@ -544,7 +518,7 @@ InitWalRecovery(ControlFileData *ControlFile, bool *wasShutdown_ptr,
 	 * Take ownership of the wakeup latch if we're going to sleep during
 	 * recovery, if required.
 	 */
-	if (ArchiveRecoveryRequested)
+	if (ArchiveRecoveryRequested())
 		OwnLatch(&XLogRecoveryCtl->recoveryWakeupLatch);
 
 	/*
@@ -599,8 +573,8 @@ InitWalRecovery(ControlFileData *ControlFile, bool *wasShutdown_ptr,
 		 * file, we know how far we need to replay to reach consistency. Enter
 		 * archive recovery directly.
 		 */
-		InArchiveRecovery = true;
-		if (StandbyModeRequested)
+		localRecoveryFlags |= SX_IN_ARCHIVE_RECOVERY;
+		if (StandbyModeRequested())
 			EnableStandbyMode();
 
 		/*
@@ -749,14 +723,14 @@ InitWalRecovery(ControlFileData *ControlFile, bool *wasShutdown_ptr,
 		 * to minRecoveryPoint, up to backupEndPoint, or until we see an
 		 * end-of-backup record), and we can enter archive recovery directly.
 		 */
-		if (ArchiveRecoveryRequested &&
+		if (ArchiveRecoveryRequested() &&
 			(ControlFile->minRecoveryPoint != InvalidXLogRecPtr ||
 			 ControlFile->backupEndRequired ||
 			 ControlFile->backupEndPoint != InvalidXLogRecPtr ||
 			 ControlFile->state == DB_SHUTDOWNED))
 		{
-			InArchiveRecovery = true;
-			if (StandbyModeRequested)
+			localRecoveryFlags |= SX_IN_ARCHIVE_RECOVERY;
+			if (StandbyModeRequested())
 				EnableStandbyMode();
 		}
 
@@ -799,9 +773,9 @@ InitWalRecovery(ControlFileData *ControlFile, bool *wasShutdown_ptr,
 		wasShutdown = ((record->xl_info & ~XLR_INFO_MASK) == XLOG_CHECKPOINT_SHUTDOWN);
 	}
 
-	if (ArchiveRecoveryRequested)
+	if (ArchiveRecoveryRequested())
 	{
-		if (StandbyModeRequested)
+		if (StandbyModeRequested())
 			ereport(LOG,
 					(errmsg("entering standby mode")));
 		else if (recoveryTarget == RECOVERY_TARGET_XID)
@@ -911,7 +885,7 @@ InitWalRecovery(ControlFileData *ControlFile, bool *wasShutdown_ptr,
 	}
 	else if (ControlFile->state != DB_SHUTDOWNED)
 		InRecovery = true;
-	else if (ArchiveRecoveryRequested)
+	else if (ArchiveRecoveryRequested())
 	{
 		/* force recovery due to presence of recovery signal file */
 		InRecovery = true;
@@ -928,7 +902,7 @@ InitWalRecovery(ControlFileData *ControlFile, bool *wasShutdown_ptr,
 	 */
 	if (InRecovery)
 	{
-		if (InArchiveRecovery)
+		if (InArchiveRecovery())
 		{
 			ControlFile->state = DB_IN_ARCHIVE_RECOVERY;
 		}
@@ -947,7 +921,7 @@ InitWalRecovery(ControlFileData *ControlFile, bool *wasShutdown_ptr,
 		}
 		ControlFile->checkPoint = CheckPointLoc;
 		ControlFile->checkPointCopy = checkPoint;
-		if (InArchiveRecovery)
+		if (InArchiveRecovery())
 		{
 			/* initialize minRecoveryPoint if not set yet */
 			if (ControlFile->minRecoveryPoint < checkPoint.redo)
@@ -994,7 +968,7 @@ InitWalRecovery(ControlFileData *ControlFile, bool *wasShutdown_ptr,
 	backupStartPoint = ControlFile->backupStartPoint;
 	backupEndRequired = ControlFile->backupEndRequired;
 	backupEndPoint = ControlFile->backupEndPoint;
-	if (InArchiveRecovery)
+	if (InArchiveRecovery())
 	{
 		minRecoveryPoint = ControlFile->minRecoveryPoint;
 		minRecoveryPointTLI = ControlFile->minRecoveryPointTLI;
@@ -1080,17 +1054,16 @@ readRecoverySignalFile(void)
 		recovery_signal_file_found = true;
 	}
 
-	StandbyModeRequested = false;
-	ArchiveRecoveryRequested = false;
+	localRecoveryFlags &= ~SX_STANDBY_MODE_REQUESTED;
+	localRecoveryFlags &= ~SX_ARCHIVE_RECOVERY_REQUESTED;
 	if (standby_signal_file_found)
 	{
-		StandbyModeRequested = true;
-		ArchiveRecoveryRequested = true;
+		localRecoveryFlags |= SX_STANDBY_MODE_REQUESTED;
+		localRecoveryFlags |= SX_ARCHIVE_RECOVERY_REQUESTED;
 	}
 	else if (recovery_signal_file_found)
 	{
-		StandbyModeRequested = false;
-		ArchiveRecoveryRequested = true;
+		localRecoveryFlags |= SX_ARCHIVE_RECOVERY_REQUESTED;
 	}
 	else
 		return;
@@ -1099,7 +1072,7 @@ readRecoverySignalFile(void)
 	 * We don't support standby mode in standalone backends; that requires
 	 * other processes such as the WAL receiver to be alive.
 	 */
-	if (StandbyModeRequested && !IsUnderPostmaster)
+	if (StandbyModeRequested() && !IsUnderPostmaster)
 		ereport(FATAL,
 				(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
 				 errmsg("standby mode is not supported by single-user servers")));
@@ -1108,13 +1081,13 @@ readRecoverySignalFile(void)
 static void
 validateRecoveryParameters(void)
 {
-	if (!ArchiveRecoveryRequested)
+	if (!ArchiveRecoveryRequested())
 		return;
 
 	/*
 	 * Check for compulsory parameters
 	 */
-	if (StandbyModeRequested)
+	if (StandbyModeRequested())
 	{
 		if ((PrimaryConnInfo == NULL || strcmp(PrimaryConnInfo, "") == 0) &&
 			(recoveryRestoreCommand == NULL || strcmp(recoveryRestoreCommand, "") == 0))
@@ -1155,8 +1128,8 @@ validateRecoveryParameters(void)
 	/*
 	 * If user specified recovery_target_timeline, validate it or compute the
 	 * "latest" value.  We can't do this until after we've gotten the restore
-	 * command and set InArchiveRecovery, because we need to fetch timeline
-	 * history files from the archive.
+	 * command and set SX_IN_ARCHIVE_RECOVERY, because we need to fetch
+	 * timeline history files from the archive.
 	 */
 	if (recoveryTargetTimeLineGoal == RECOVERY_TARGET_TIMELINE_NUMERIC)
 	{
@@ -1494,7 +1467,7 @@ FinishWalRecovery(void)
 	 * i.e., calling XLogShutdownWalRcv().
 	 */
 	Assert(!WalRcvStreaming());
-	StandbyMode = false;
+	localRecoveryFlags &= ~SX_IN_STANDBY_MODE;
 
 	/*
 	 * Determine where to start writing WAL next.
@@ -1532,7 +1505,7 @@ FinishWalRecovery(void)
 	 */
 	result->endOfLogTLI = xlogreader->seg.ws_tli;
 
-	if (ArchiveRecoveryRequested)
+	if (ArchiveRecoveryRequested())
 	{
 		/*
 		 * We are no longer in archive recovery state.
@@ -1540,8 +1513,8 @@ FinishWalRecovery(void)
 		 * We are now done reading the old WAL.  Turn off archive fetching if
 		 * it was active.
 		 */
-		Assert(InArchiveRecovery);
-		InArchiveRecovery = false;
+		Assert(InArchiveRecovery());
+		localRecoveryFlags &= ~SX_IN_ARCHIVE_RECOVERY;
 
 		/*
 		 * If the ending log segment is still open, close it (to avoid
@@ -1621,7 +1594,7 @@ ShutdownWalRecovery(void)
 	XLogReaderFree(xlogreader);
 	XLogPrefetcherFree(xlogprefetcher);
 
-	if (ArchiveRecoveryRequested)
+	if (ArchiveRecoveryRequested())
 	{
 		/*
 		 * Since there might be a partial WAL segment named RECOVERYXLOG, get
@@ -1639,7 +1612,7 @@ ShutdownWalRecovery(void)
 	 * We don't need the latch anymore. It's not strictly necessary to disown
 	 * it, but let's do it for the sake of tidiness.
 	 */
-	if (ArchiveRecoveryRequested)
+	if (ArchiveRecoveryRequested())
 		DisownLatch(&XLogRecoveryCtl->recoveryWakeupLatch);
 }
 
@@ -1741,7 +1714,7 @@ PerformWalRecovery(void)
 						LSN_FORMAT_ARGS(xlogreader->ReadRecPtr))));
 
 		/* Prepare to report progress of the redo phase. */
-		if (!StandbyMode)
+		if (!InStandbyMode())
 			begin_startup_progress_phase();
 
 		/*
@@ -1749,7 +1722,7 @@ PerformWalRecovery(void)
 		 */
 		do
 		{
-			if (!StandbyMode)
+			if (!InStandbyMode())
 				ereport_startup_progress("redo in progress, elapsed time: %ld.%02d s, current LSN: %X/%X",
 										 LSN_FORMAT_ARGS(xlogreader->ReadRecPtr));
 
@@ -1894,7 +1867,7 @@ PerformWalRecovery(void)
 	 * This check is intentionally after the above log messages that indicate
 	 * how far recovery went.
 	 */
-	if (ArchiveRecoveryRequested &&
+	if (ArchiveRecoveryRequested() &&
 		recoveryTarget != RECOVERY_TARGET_UNSET &&
 		!reachedRecoveryTarget)
 		ereport(FATAL,
@@ -2186,7 +2159,7 @@ CheckRecoveryConsistency(void)
 	if (XLogRecPtrIsInvalid(minRecoveryPoint))
 		return;
 
-	Assert(InArchiveRecovery);
+	Assert(InArchiveRecovery());
 
 	/*
 	 * assume that we are called in the startup process, and hence don't need
@@ -2256,15 +2229,15 @@ CheckRecoveryConsistency(void)
 	 * enabling connections.
 	 */
 	if (standbyState == STANDBY_SNAPSHOT_READY &&
-		!LocalHotStandbyActive &&
+		!(localRecoveryFlags & SX_HOT_STANDBY_ACTIVE) &&
 		reachedConsistency &&
 		IsUnderPostmaster)
 	{
 		SpinLockAcquire(&XLogRecoveryCtl->info_lck);
-		XLogRecoveryCtl->SharedHotStandbyActive = true;
+		XLogRecoveryCtl->sharedRecoveryFlags |= SX_HOT_STANDBY_ACTIVE;
 		SpinLockRelease(&XLogRecoveryCtl->info_lck);
 
-		LocalHotStandbyActive = true;
+		localRecoveryFlags |= SX_HOT_STANDBY_ACTIVE;
 
 		SendPostmasterSignal(PMSIGNAL_BEGIN_HOT_STANDBY);
 	}
@@ -2584,7 +2557,7 @@ recoveryStopsBefore(XLogReaderState *record)
 	 * Ignore recovery target settings when not in archive recovery (meaning
 	 * we are in crash recovery).
 	 */
-	if (!ArchiveRecoveryRequested)
+	if (!ArchiveRecoveryRequested())
 		return false;
 
 	/* Check if we should stop as soon as reaching consistency */
@@ -2736,7 +2709,7 @@ recoveryStopsAfter(XLogReaderState *record)
 	 * Ignore recovery target settings when not in archive recovery (meaning
 	 * we are in crash recovery).
 	 */
-	if (!ArchiveRecoveryRequested)
+	if (!ArchiveRecoveryRequested())
 		return false;
 
 	info = XLogRecGetInfo(record) & ~XLR_INFO_MASK;
@@ -2927,11 +2900,11 @@ static void
 recoveryPausesHere(bool endOfRecovery)
 {
 	/* Don't pause unless users can connect! */
-	if (!LocalHotStandbyActive)
+	if (!(localRecoveryFlags & SX_HOT_STANDBY_ACTIVE))
 		return;
 
 	/* Don't pause after standby promotion has been triggered */
-	if (LocalPromoteIsTriggered)
+	if (localRecoveryFlags & SX_PROMOTE_IS_TRIGGERED)
 		return;
 
 	if (endOfRecovery)
@@ -2997,7 +2970,7 @@ recoveryApplyDelay(XLogReaderState *record)
 		return false;
 
 	/* nothing to do if crash recovery is requested */
-	if (!ArchiveRecoveryRequested)
+	if (!ArchiveRecoveryRequested())
 		return false;
 
 	/*
@@ -3159,13 +3132,13 @@ ReadRecord(XLogPrefetcher *xlogprefetcher, int emode,
 			 * to indicate to downstream WAL readers that that portion is to
 			 * be ignored.
 			 *
-			 * However, when ArchiveRecoveryRequested = true, we're going to
+			 * However, when ArchiveRecoveryRequested() = true, we're going to
 			 * switch to a new timeline at the end of recovery. We will only
 			 * copy WAL over to the new timeline up to the end of the last
 			 * complete record, so if we did this, we would later create an
 			 * overwrite contrecord in the wrong place, breaking everything.
 			 */
-			if (!ArchiveRecoveryRequested &&
+			if (!ArchiveRecoveryRequested() &&
 				!XLogRecPtrIsInvalid(xlogreader->abortedRecPtr))
 			{
 				abortedRecPtr = xlogreader->abortedRecPtr;
@@ -3234,13 +3207,13 @@ ReadRecord(XLogPrefetcher *xlogprefetcher, int emode,
 			 * we'd have no idea how far we'd have to replay to reach
 			 * consistency.  So err on the safe side and give up.
 			 */
-			if (!InArchiveRecovery && ArchiveRecoveryRequested &&
+			if (!InArchiveRecovery() && ArchiveRecoveryRequested() &&
 				!fetching_ckpt)
 			{
 				ereport(DEBUG1,
 						(errmsg_internal("reached end of WAL in pg_wal, entering archive recovery")));
-				InArchiveRecovery = true;
-				if (StandbyModeRequested)
+				localRecoveryFlags |= SX_IN_ARCHIVE_RECOVERY;
+				if (StandbyModeRequested())
 					EnableStandbyMode();
 
 				SwitchIntoArchiveRecovery(xlogreader->EndRecPtr, replayTLI);
@@ -3260,7 +3233,7 @@ ReadRecord(XLogPrefetcher *xlogprefetcher, int emode,
 			}
 
 			/* In standby mode, loop back to retry. Otherwise, give up. */
-			if (StandbyMode && !CheckForStandbyTrigger())
+			if (InStandbyMode() && !CheckForStandbyTrigger())
 				continue;
 			else
 				return NULL;
@@ -3321,7 +3294,7 @@ XLogPageRead(XLogReaderState *xlogreader, XLogRecPtr targetPagePtr, int reqLen,
 		 * Request a restartpoint if we've replayed too much xlog since the
 		 * last one.
 		 */
-		if (ArchiveRecoveryRequested && IsUnderPostmaster)
+		if (ArchiveRecoveryRequested() && IsUnderPostmaster)
 		{
 			if (XLogCheckpointNeeded(readSegNo))
 			{
@@ -3464,7 +3437,7 @@ retry:
 	 * page header here for the retry. Instead, ReadPageInternal() is
 	 * responsible for the validation.
 	 */
-	if (StandbyMode &&
+	if (InStandbyMode() &&
 		!XLogReaderValidatePageHeader(xlogreader, targetPagePtr, readBuf))
 	{
 		/*
@@ -3500,7 +3473,7 @@ next_record_is_invalid:
 	readSource = XLOG_FROM_ANY;
 
 	/* In standby-mode, keep trying */
-	if (StandbyMode)
+	if (InStandbyMode())
 		goto retry;
 	else
 		return XLREAD_FAIL;
@@ -3575,10 +3548,10 @@ WaitForWALToBecomeAvailable(XLogRecPtr RecPtr, bool randAccess,
 	 * the end of recovery.
 	 *-------
 	 */
-	if (!InArchiveRecovery)
+	if (!InArchiveRecovery())
 		currentSource = XLOG_FROM_PG_WAL;
 	else if (currentSource == XLOG_FROM_ANY ||
-			 (!StandbyMode && currentSource == XLOG_FROM_STREAM))
+			 (!InStandbyMode() && currentSource == XLOG_FROM_STREAM))
 	{
 		lastSourceFailed = false;
 		currentSource = XLOG_FROM_ARCHIVE;
@@ -3616,7 +3589,7 @@ WaitForWALToBecomeAvailable(XLogRecPtr RecPtr, bool randAccess,
 					 * finish replaying as much as we can from archive and
 					 * pg_wal before failover.
 					 */
-					if (StandbyMode && CheckForStandbyTrigger())
+					if (InStandbyMode() && CheckForStandbyTrigger())
 					{
 						XLogShutdownWalRcv();
 						return XLREAD_FAIL;
@@ -3626,7 +3599,7 @@ WaitForWALToBecomeAvailable(XLogRecPtr RecPtr, bool randAccess,
 					 * Not in standby mode, and we've now tried the archive
 					 * and pg_wal.
 					 */
-					if (!StandbyMode)
+					if (!InStandbyMode())
 						return XLREAD_FAIL;
 
 					/*
@@ -3658,7 +3631,7 @@ WaitForWALToBecomeAvailable(XLogRecPtr RecPtr, bool randAccess,
 					 * We should be able to move to XLOG_FROM_STREAM only in
 					 * standby mode.
 					 */
-					Assert(StandbyMode);
+					Assert(InStandbyMode());
 
 					/*
 					 * Before we leave XLOG_FROM_STREAM state, make sure that
@@ -3729,7 +3702,7 @@ WaitForWALToBecomeAvailable(XLogRecPtr RecPtr, bool randAccess,
 			 * the archive over ones in pg_wal, so try the next file again
 			 * from the archive first.
 			 */
-			if (InArchiveRecovery)
+			if (InArchiveRecovery())
 				currentSource = XLOG_FROM_ARCHIVE;
 		}
 
@@ -3789,7 +3762,7 @@ WaitForWALToBecomeAvailable(XLogRecPtr RecPtr, bool randAccess,
 					 * We should be able to move to XLOG_FROM_STREAM only in
 					 * standby mode.
 					 */
-					Assert(StandbyMode);
+					Assert(InStandbyMode());
 
 					/*
 					 * First, shutdown walreceiver if its restart has been
@@ -4397,21 +4370,22 @@ PromoteIsTriggered(void)
 	 * triggered. We can't trigger a promotion again, so there's no need to
 	 * keep checking after the shared variable has once been seen true.
 	 */
-	if (LocalPromoteIsTriggered)
+	if (localRecoveryFlags & SX_PROMOTE_IS_TRIGGERED)
 		return true;
 
 	SpinLockAcquire(&XLogRecoveryCtl->info_lck);
-	LocalPromoteIsTriggered = XLogRecoveryCtl->SharedPromoteIsTriggered;
+	localRecoveryFlags |=
+		(XLogRecoveryCtl->sharedRecoveryFlags & SX_PROMOTE_IS_TRIGGERED);
 	SpinLockRelease(&XLogRecoveryCtl->info_lck);
 
-	return LocalPromoteIsTriggered;
+	return localRecoveryFlags & SX_PROMOTE_IS_TRIGGERED;
 }
 
 static void
 SetPromoteIsTriggered(void)
 {
 	SpinLockAcquire(&XLogRecoveryCtl->info_lck);
-	XLogRecoveryCtl->SharedPromoteIsTriggered = true;
+	XLogRecoveryCtl->sharedRecoveryFlags |= SX_PROMOTE_IS_TRIGGERED;
 	SpinLockRelease(&XLogRecoveryCtl->info_lck);
 
 	/*
@@ -4422,7 +4396,7 @@ SetPromoteIsTriggered(void)
 	 */
 	SetRecoveryPause(false);
 
-	LocalPromoteIsTriggered = true;
+	localRecoveryFlags |= SX_PROMOTE_IS_TRIGGERED;
 }
 
 /*
@@ -4431,7 +4405,7 @@ SetPromoteIsTriggered(void)
 static bool
 CheckForStandbyTrigger(void)
 {
-	if (LocalPromoteIsTriggered)
+	if (localRecoveryFlags & SX_PROMOTE_IS_TRIGGERED)
 		return true;
 
 	if (IsPromoteSignaled() && CheckPromoteSignal())
@@ -4505,16 +4479,17 @@ HotStandbyActive(void)
 	 * can't de-activate Hot Standby, so there's no need to keep checking
 	 * after the shared variable has once been seen true.
 	 */
-	if (LocalHotStandbyActive)
+	if (localRecoveryFlags & SX_HOT_STANDBY_ACTIVE)
 		return true;
 	else
 	{
 		/* spinlock is essential on machines with weak memory ordering! */
 		SpinLockAcquire(&XLogRecoveryCtl->info_lck);
-		LocalHotStandbyActive = XLogRecoveryCtl->SharedHotStandbyActive;
+		localRecoveryFlags |=
+			(XLogRecoveryCtl->sharedRecoveryFlags & SX_HOT_STANDBY_ACTIVE);
 		SpinLockRelease(&XLogRecoveryCtl->info_lck);
 
-		return LocalHotStandbyActive;
+		return localRecoveryFlags & SX_HOT_STANDBY_ACTIVE;
 	}
 }
 
@@ -4526,7 +4501,7 @@ static bool
 HotStandbyActiveInReplay(void)
 {
 	Assert(AmStartupProcess() || !IsPostmasterEnvironment);
-	return LocalHotStandbyActive;
+	return localRecoveryFlags & SX_HOT_STANDBY_ACTIVE;
 }
 
 /*
@@ -5044,3 +5019,23 @@ assign_recovery_target_xid(const char *newval, void *extra)
 	else
 		recoveryTarget = RECOVERY_TARGET_UNSET;
 }
+
+bool StandbyModeRequested(void)
+{
+	return localRecoveryFlags & SX_STANDBY_MODE_REQUESTED;
+}
+
+bool ArchiveRecoveryRequested(void)
+{
+	return localRecoveryFlags & SX_ARCHIVE_RECOVERY_REQUESTED;
+}
+
+bool InArchiveRecovery(void)
+{
+	return localRecoveryFlags & SX_IN_ARCHIVE_RECOVERY;
+}
+
+bool InStandbyMode(void)
+{
+	return localRecoveryFlags & SX_IN_STANDBY_MODE;
+}
diff --git a/src/backend/replication/logical/slotsync.c b/src/backend/replication/logical/slotsync.c
index f6945af1d43..b8ac4c6c5df 100644
--- a/src/backend/replication/logical/slotsync.c
+++ b/src/backend/replication/logical/slotsync.c
@@ -1517,7 +1517,7 @@ update_synced_slots_inactive_since(void)
 	 * long time after promotion if they haven't been synchronized recently.
 	 * Whoever acquires the slot, i.e., makes the slot active, will reset it.
 	 */
-	if (!StandbyMode)
+	if (!InStandbyMode())
 		return;
 
 	/* The slot sync worker or SQL function mustn't be running by now */
diff --git a/src/include/access/xlog_internal.h b/src/include/access/xlog_internal.h
index 0b7c56332b5..ff5e4fce305 100644
--- a/src/include/access/xlog_internal.h
+++ b/src/include/access/xlog_internal.h
@@ -392,14 +392,13 @@ extern void GetOldestRestartPoint(XLogRecPtr *oldrecptr, TimeLineID *oldtli);
 extern void XLogRecGetBlockRefInfo(XLogReaderState *record, bool pretty,
 								   bool detailed_format, StringInfo buf,
 								   uint32 *fpi_len);
-
 /*
  * Exported for the functions in timeline.c and xlogarchive.c.  Only valid
  * in the startup process.
  */
-extern PGDLLIMPORT bool ArchiveRecoveryRequested;
-extern PGDLLIMPORT bool InArchiveRecovery;
-extern PGDLLIMPORT bool StandbyMode;
+extern PGDLLIMPORT bool ArchiveRecoveryRequested(void);
+extern PGDLLIMPORT bool InArchiveRecovery(void);
+extern PGDLLIMPORT bool InStandbyMode(void);
 extern PGDLLIMPORT char *recoveryRestoreCommand;
 
 #endif							/* XLOG_INTERNAL_H */
diff --git a/src/include/access/xlogrecovery.h b/src/include/access/xlogrecovery.h
index 91446303024..2d44905ea36 100644
--- a/src/include/access/xlogrecovery.h
+++ b/src/include/access/xlogrecovery.h
@@ -16,6 +16,37 @@
 #include "lib/stringinfo.h"
 #include "utils/timestamp.h"
 
+/*
+ * The flag indicates if we allow hot standby queries to be run.
+ */
+#define SX_HOT_STANDBY_ACTIVE			0x01	/* SX: Startup Xlog */
+/*
+ * The flag indicates if a standby promotion has been triggered.
+ */
+#define SX_PROMOTE_IS_TRIGGERED			0x02
+/*
+ * When SX_ARCHIVE_RECOVERY_REQUESTED is set, archive recovery was requested,
+ * i.e. signal files were present.  When SX_IN_ARCHIVE_RECOVERY is set, we are
+ * currently recovering using offline XLOG archives.  These variables are only
+ * valid in the startup process.
+ *
+ * When SX_ARCHIVE_RECOVERY_REQUESTED is set, but SX_IN_ARCHIVE_RECOVERY is
+ * not, we're currently performing crash recovery using only XLOG files in
+ * pg_wal, but will switch to using offline XLOG archives as soon as we reach
+ * the end of WAL in pg_wal.
+ */
+#define SX_ARCHIVE_RECOVERY_REQUESTED	0x04
+#define SX_IN_ARCHIVE_RECOVERY			0x08
+/*
+ * When SX_STANDBY_MODE_REQUESTED is set, standby mode was requested, i.e.
+ * standby.signal file was present.  When SX_IN_STANDBY_MODE is set, we are
+ * currently in standby mode.  These variables are only valid in the startup
+ * process. They work similarly to SX_ARCHIVE_RECOVERY_REQUESTED and
+ * SX_IN_ARCHIVE_RECOVERY.
+ */
+#define SX_STANDBY_MODE_REQUESTED		0x10
+#define SX_IN_STANDBY_MODE				0x20
+
 /*
  * Recovery target type.
  * Only set during a Point in Time recovery, not when in standby mode.
@@ -73,9 +104,6 @@ extern PGDLLIMPORT TimeLineID recoveryTargetTLI;
 /* Have we already reached a consistent database state? */
 extern PGDLLIMPORT bool reachedConsistency;
 
-/* Are we currently in standby mode? */
-extern PGDLLIMPORT bool StandbyMode;
-
 extern Size XLogRecoveryShmemSize(void);
 extern void XLogRecoveryShmemInit(void);
 
-- 
2.34.1

