diff --git a/src/backend/access/transam/timeline.c b/src/backend/access/transam/timeline.c index a27f27cc037d..c9f53c4b6670 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 ec40c0b7c42b..a8740561b4f2 100644 --- a/src/backend/access/transam/xlog.c +++ b/src/backend/access/transam/xlog.c @@ -5573,7 +5573,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), @@ -5586,7 +5586,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", @@ -5746,8 +5746,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); @@ -5879,7 +5879,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; @@ -5932,7 +5932,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; @@ -5966,7 +5966,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; @@ -6079,7 +6079,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, @@ -6131,7 +6131,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, @@ -6327,7 +6327,7 @@ StartupXLOG(void) XLogReportParameters(); /* If this is archive recovery, perform post-recovery cleanup actions. */ - if (ArchiveRecoveryRequested) + if (ArchiveRecoveryRequested()) CleanupAfterArchiveRecovery(EndOfLogTLI, EndOfLog, newTLI); /* @@ -6486,7 +6486,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; @@ -8480,7 +8480,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, @@ -8721,7 +8721,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 1ef1713c91a4..ad379acc30a3 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/xlogfuncs.c b/src/backend/access/transam/xlogfuncs.c index 8c3090165f00..9b71ff7583c7 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 6ce979f2d8bc..4260dabd64c3 100644 --- a/src/backend/access/transam/xlogrecovery.c +++ b/src/backend/access/transam/xlogrecovery.c @@ -124,30 +124,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; @@ -171,16 +148,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; @@ -303,23 +283,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 @@ -446,6 +419,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 */ @@ -483,7 +457,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 @@ -511,8 +485,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, @@ -550,7 +524,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); /* @@ -605,8 +579,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(); /* @@ -755,14 +729,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(); } @@ -805,9 +779,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) @@ -919,7 +893,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; @@ -936,7 +910,7 @@ InitWalRecovery(ControlFileData *ControlFile, bool *wasShutdown_ptr, */ if (InRecovery) { - if (InArchiveRecovery) + if (InArchiveRecovery()) { ControlFile->state = DB_IN_ARCHIVE_RECOVERY; } @@ -955,7 +929,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) @@ -1002,7 +976,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; @@ -1088,17 +1062,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; @@ -1107,7 +1080,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"))); @@ -1116,13 +1089,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)) @@ -1163,8 +1136,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) { @@ -1502,7 +1475,7 @@ FinishWalRecovery(void) * i.e., calling XLogShutdownWalRcv(). */ Assert(!WalRcvStreaming()); - StandbyMode = false; + localRecoveryFlags &= ~SX_IN_STANDBY_MODE; /* * Determine where to start writing WAL next. @@ -1540,7 +1513,7 @@ FinishWalRecovery(void) */ result->endOfLogTLI = xlogreader->seg.ws_tli; - if (ArchiveRecoveryRequested) + if (ArchiveRecoveryRequested()) { /* * We are no longer in archive recovery state. @@ -1548,8 +1521,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 @@ -1629,7 +1602,7 @@ ShutdownWalRecovery(void) XLogReaderFree(xlogreader); XLogPrefetcherFree(xlogprefetcher); - if (ArchiveRecoveryRequested) + if (ArchiveRecoveryRequested()) { /* * Since there might be a partial WAL segment named RECOVERYXLOG, get @@ -1647,7 +1620,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); } @@ -1749,7 +1722,7 @@ PerformWalRecovery(void) LSN_FORMAT_ARGS(xlogreader->ReadRecPtr)))); /* Prepare to report progress of the redo phase. */ - if (!StandbyMode) + if (!InStandbyMode()) begin_startup_progress_phase(); /* @@ -1757,7 +1730,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)); @@ -1902,7 +1875,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, @@ -2194,7 +2167,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 @@ -2265,15 +2238,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); } @@ -2593,7 +2566,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 */ @@ -2745,7 +2718,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; @@ -2936,11 +2909,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) @@ -3006,7 +2979,7 @@ recoveryApplyDelay(XLogReaderState *record) return false; /* nothing to do if crash recovery is requested */ - if (!ArchiveRecoveryRequested) + if (!ArchiveRecoveryRequested()) return false; /* @@ -3168,13 +3141,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; @@ -3243,13 +3216,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); @@ -3269,7 +3242,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; @@ -3331,7 +3304,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)) { @@ -3484,7 +3457,7 @@ XLogPageRead(XLogReaderState *xlogreader, XLogRecPtr targetPagePtr, int reqLen, * page header here for the retry. Instead, ReadPageInternal() is * responsible for the validation. */ - if (StandbyMode && + if (InStandbyMode() && (targetPagePtr % wal_segment_size) == 0 && !XLogReaderValidatePageHeader(xlogreader, targetPagePtr, readBuf)) { @@ -3521,7 +3494,7 @@ XLogPageRead(XLogReaderState *xlogreader, XLogRecPtr targetPagePtr, int reqLen, readSource = XLOG_FROM_ANY; /* In standby-mode, keep trying */ - if (StandbyMode) + if (InStandbyMode()) goto retry; else return XLREAD_FAIL; @@ -3596,10 +3569,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; @@ -3637,7 +3610,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; @@ -3647,7 +3620,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; /* @@ -3679,7 +3652,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 @@ -3750,7 +3723,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; } @@ -3810,7 +3783,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 @@ -4403,6 +4376,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? @@ -4418,21 +4407,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); /* @@ -4443,7 +4433,7 @@ SetPromoteIsTriggered(void) */ SetRecoveryPause(false); - LocalPromoteIsTriggered = true; + localRecoveryFlags |= SX_PROMOTE_IS_TRIGGERED; } /* @@ -4452,7 +4442,7 @@ SetPromoteIsTriggered(void) static bool CheckForStandbyTrigger(void) { - if (LocalPromoteIsTriggered) + if (localRecoveryFlags & SX_PROMOTE_IS_TRIGGERED) return true; if (IsPromoteSignaled() && CheckPromoteSignal()) @@ -4526,16 +4516,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; } } @@ -4547,7 +4538,7 @@ static bool HotStandbyActiveInReplay(void) { Assert(AmStartupProcess() || !IsPostmasterEnvironment); - return LocalHotStandbyActive; + return localRecoveryFlags & SX_HOT_STANDBY_ACTIVE; } /* @@ -5070,3 +5061,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 e22d41891e68..9daec6184778 100644 --- a/src/backend/replication/logical/slotsync.c +++ b/src/backend/replication/logical/slotsync.c @@ -1523,7 +1523,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/backend/replication/slot.c b/src/backend/replication/slot.c index 600b87fa9cb6..a103bbdaf987 100644 --- a/src/backend/replication/slot.c +++ b/src/backend/replication/slot.c @@ -2537,7 +2537,7 @@ RestoreSlotFromDisk(const char *name) * primary reduces wal_level < logical while hot standby is disabled, * logical slots would remain valid even after promotion. */ - if (StandbyMode && !EnableHotStandby) + if (InStandbyMode() && !EnableHotStandby) ereport(FATAL, (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE), errmsg("logical replication slot \"%s\" exists on the standby, but \"hot_standby\" = \"off\"", diff --git a/src/include/access/xlog_internal.h b/src/include/access/xlog_internal.h index 2cf8d55d706d..2fdf03e33cbc 100644 --- a/src/include/access/xlog_internal.h +++ b/src/include/access/xlog_internal.h @@ -397,9 +397,9 @@ extern void XLogRecGetBlockRefInfo(XLogReaderState *record, bool pretty, * 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 91446303024a..faa6c666edec 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); @@ -144,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 5d5be8ba4e16..2c869acce1ff 100644 --- a/src/include/catalog/pg_proc.dat +++ b/src/include/catalog/pg_proc.dat @@ -6757,6 +6757,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 => '',