Hi!
Michael, sorry for such a long time to deliver next version of the patch
from my side.
In this version I have fixed all your propositions, hopefully correct.
There is one point that I would like to emphasize, namely
Shouldn't STANDBY_MODE be only used in the local flag, as well as an
ARCHIVE_RECOVERY_REQUESTED? It looks like this could push a bit more
forward the removal of more of these booleans, with a bit more work..
I made corresponding changes, but given that these three variables
bool ArchiveRecoveryRequested;
bool InArchiveRecovery;
bool StandbyMode;
are used in other units, and (if I understand correctly) we decided to
move them in one localRecoveryFlags bitset too,
I changed their extern calls to boolean functions calls instead of
extern variables.
The test additions are welcome.
My test additions were based on copy of StandbyModeRequested placed in
shared memory
even the startup process is done. With current implementation they seems
to be not very usefull now.
Respectfully,
Mikhail Litsarev,
Postgres Professional: https://postgrespro.com
From eaf661c022f82217d33cfdb3776c69675f82ac67 Mon Sep 17 00:00:00 2001
From: Mikhail Litsarev <m.litsa...@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
From 39c42bdcaa371ea356ff2db8944d4cf8535d23ba Mon Sep 17 00:00:00 2001
From: Mikhail Litsarev <m.litsa...@postgrespro.ru>
Date: Fri, 10 Jan 2025 22:31:34 +0300
Subject: [PATCH] Wrapper function to extract whole text array from recovery
flags bitset.
It returns SX_PROMOTE_IS_TRIGGERED, SX_STANDBY_MODE_REQUESTED flags for
recovery states.
---
src/backend/access/transam/xlogfuncs.c | 31 +++++++++++++++++++++++
src/backend/access/transam/xlogrecovery.c | 16 ++++++++++++
src/include/access/xlogrecovery.h | 1 +
src/include/catalog/pg_proc.dat | 5 ++++
4 files changed, 53 insertions(+)
diff --git a/src/backend/access/transam/xlogfuncs.c b/src/backend/access/transam/xlogfuncs.c
index 8c3090165f0..9b71ff7583c 100644
--- a/src/backend/access/transam/xlogfuncs.c
+++ b/src/backend/access/transam/xlogfuncs.c
@@ -30,6 +30,7 @@
#include "storage/fd.h"
#include "storage/latch.h"
#include "storage/standby.h"
+#include "utils/array.h"
#include "utils/builtins.h"
#include "utils/memutils.h"
#include "utils/pg_lsn.h"
@@ -748,3 +749,33 @@ pg_promote(PG_FUNCTION_ARGS)
wait_seconds)));
PG_RETURN_BOOL(false);
}
+
+Datum
+pg_get_recovery_flags(PG_FUNCTION_ARGS)
+{
+/*
+ * Currently supported number of recovery flags is equal to two:
+ * {SX_PROMOTE_IS_TRIGGERED, SX_STANDBY_MODE_REQUESTED}.
+ * The SX_STANDBY_MODE_REQUESTED is valid only in the startup process.
+ */
+#define MAX_RECOVERY_FLAGS 2
+
+ bits32 recovery_flags;
+ int cnt = 0;
+ Datum flags[MAX_RECOVERY_FLAGS];
+ ArrayType *txt_arr;
+
+ recovery_flags = GetXLogRecoveryFlags();
+
+ if (recovery_flags & SX_PROMOTE_IS_TRIGGERED)
+ flags[cnt++] = CStringGetTextDatum("PROMOTE_IS_TRIGGERED");
+
+ if (recovery_flags & SX_STANDBY_MODE_REQUESTED)
+ flags[cnt++] = CStringGetTextDatum("STANDBY_MODE_REQUESTED");
+
+ Assert(cnt <= MAX_RECOVERY_FLAGS);
+
+ /* Returns bit array as Datum */
+ txt_arr = construct_array_builtin(flags, cnt, TEXTOID);
+ PG_RETURN_ARRAYTYPE_P(txt_arr);
+}
diff --git a/src/backend/access/transam/xlogrecovery.c b/src/backend/access/transam/xlogrecovery.c
index e75c7891fd5..d675b46c592 100644
--- a/src/backend/access/transam/xlogrecovery.c
+++ b/src/backend/access/transam/xlogrecovery.c
@@ -4355,6 +4355,22 @@ StartupRequestWalReceiverRestart(void)
}
}
+/*
+ * Return SX_PROMOTE_IS_TRIGGERED, SX_STANDBY_MODE_REQUESTED flags for
+ * recovery states.
+ */
+bits32 GetXLogRecoveryFlags(void)
+{
+ bits32 flags = 0;
+
+ SpinLockAcquire(&XLogRecoveryCtl->info_lck);
+ flags = XLogRecoveryCtl->sharedRecoveryFlags;
+ SpinLockRelease(&XLogRecoveryCtl->info_lck);
+
+ flags |= (localRecoveryFlags & SX_STANDBY_MODE_REQUESTED);
+
+ return flags;
+}
/*
* Has a standby promotion already been triggered?
diff --git a/src/include/access/xlogrecovery.h b/src/include/access/xlogrecovery.h
index 2d44905ea36..faa6c666ede 100644
--- a/src/include/access/xlogrecovery.h
+++ b/src/include/access/xlogrecovery.h
@@ -172,6 +172,7 @@ extern TimestampTz GetLatestXTime(void);
extern TimestampTz GetCurrentChunkReplayStartTime(void);
extern XLogRecPtr GetCurrentReplayRecPtr(TimeLineID *replayEndTLI);
+extern bits32 GetXLogRecoveryFlags(void);
extern bool PromoteIsTriggered(void);
extern bool CheckPromoteSignal(void);
extern void WakeupRecovery(void);
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index b37e8a6f882..e1e10f6a5d6 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -6660,6 +6660,11 @@
proname => 'pg_is_in_recovery', provolatile => 'v', prorettype => 'bool',
proargtypes => '', prosrc => 'pg_is_in_recovery' },
+{ oid => '8439',
+ descr => 'return flags for recovery states',
+ proname => 'pg_get_recovery_flags', provolatile => 'v', prorettype => '_text',
+ proargtypes => '', prosrc => 'pg_get_recovery_flags' },
+
{ oid => '3820', descr => 'current wal flush location',
proname => 'pg_last_wal_receive_lsn', provolatile => 'v',
prorettype => 'pg_lsn', proargtypes => '',
--
2.34.1