From 5160bdcd32436eecc66b5ae524c532e02c1c3c53 Mon Sep 17 00:00:00 2001 From: Amul Sul Date: Fri, 19 Jun 2020 06:29:36 -0400 Subject: [PATCH v45 3/6] Implement wal prohibit state using global barrier. Implementation: 1. A user tries to change server state to WAL-Prohibited by calling pg_prohibit_wal(true) sql function, the current state generation to inprogress in shared memory marked and signaled checkpointer process. Checkpointer process by noticing that the current state transition, emits the barrier request, and then acknowledges back to the backend who requested the state change once the transition has been completed. Final state will be updated in control file to make it persistent across the system restarts. 2. When a backend receives the WAL-Prohibited barrier, at that moment if it is already in a transaction and the transaction already assigned XID, then the backend will be killed by throwing FATAL(XXX: need more discussion on this) 3. Otherwise, if that backend running transaction without valid XID then, we don't need to do anything special right now, simply call ResetLocalXLogInsertAllowed() so that any future WAL insert in will check XLogInsertAllowed() first which set WAL prohibited state appropriately. 4. A new transaction (in an existing or in a new backend) starts as a read-only transaction. 5. Autovacuum launcher as well as checkpointer will not do anything in WAL-Prohibited server state until someone wakes us up. E.g. a backend might later on request us to put the system back where WAL is no longer prohibited. 6. At shutdown in WAL-Prohibited mode, we'll skip shutdown checkpoint and xlog rotation. Starting up again will perform crash recovery but the end-of-recovery checkpoint, necessary WAL write to start a server normally will be skipped and it will be performed when the system changed to WAL is no longer prohibited. 7. Altering WAL-Prohibited mode is restricted on standby server. 8. The presence of recovery.signal and/or recovery.signal file will implicitly pull out the server from the WAL prohibited state permanently. 9. Add wal_prohibited GUC show the system state -- will be "on" when system is WAL prohibited. --- src/backend/access/transam/Makefile | 1 + src/backend/access/transam/walprohibit.c | 379 ++++++++++++++++++++++ src/backend/access/transam/xact.c | 36 +- src/backend/access/transam/xlog.c | 162 ++++++++- src/backend/access/transam/xlogrecovery.c | 22 +- src/backend/catalog/system_functions.sql | 2 + src/backend/commands/variable.c | 7 + src/backend/postmaster/autovacuum.c | 8 +- src/backend/postmaster/bgwriter.c | 2 +- src/backend/postmaster/checkpointer.c | 37 +++ src/backend/storage/ipc/ipci.c | 7 + src/backend/storage/ipc/procsignal.c | 4 + src/backend/storage/lmgr/lock.c | 6 +- src/backend/storage/sync/sync.c | 31 +- src/backend/tcop/utility.c | 1 + src/backend/utils/activity/wait_event.c | 3 + src/backend/utils/misc/guc.c | 27 ++ src/bin/pg_controldata/pg_controldata.c | 2 + src/include/access/walprohibit.h | 60 ++++ src/include/access/xlog.h | 13 + src/include/catalog/pg_control.h | 3 + src/include/catalog/pg_proc.dat | 4 + src/include/postmaster/bgwriter.h | 2 + src/include/storage/procsignal.h | 3 +- src/include/utils/wait_event.h | 3 +- src/test/regress/expected/guc.out | 3 +- src/tools/pgindent/typedefs.list | 1 + 27 files changed, 781 insertions(+), 48 deletions(-) create mode 100644 src/backend/access/transam/walprohibit.c create mode 100644 src/include/access/walprohibit.h diff --git a/src/backend/access/transam/Makefile b/src/backend/access/transam/Makefile index 3e5444a6f7..8f9b267cf0 100644 --- a/src/backend/access/transam/Makefile +++ b/src/backend/access/transam/Makefile @@ -26,6 +26,7 @@ OBJS = \ twophase.o \ twophase_rmgr.o \ varsup.o \ + walprohibit.o \ xact.o \ xlog.o \ xlogarchive.o \ diff --git a/src/backend/access/transam/walprohibit.c b/src/backend/access/transam/walprohibit.c new file mode 100644 index 0000000000..d968c71494 --- /dev/null +++ b/src/backend/access/transam/walprohibit.c @@ -0,0 +1,379 @@ +/*------------------------------------------------------------------------- + * + * walprohibit.c + * PostgreSQL write-ahead log prohibit states + * + * + * Portions Copyright (c) 2021, PostgreSQL Global Development Group + * + * src/backend/access/transam/walprohibit.c + * + *------------------------------------------------------------------------- + */ + +#include "postgres.h" + +#include "access/walprohibit.h" +#include "fmgr.h" +#include "pgstat.h" +#include "port/atomics.h" +#include "postmaster/bgwriter.h" +#include "postmaster/interrupt.h" +#include "storage/condition_variable.h" +#include "storage/procsignal.h" +#include "storage/shmem.h" +#include "storage/latch.h" +#include "utils/acl.h" +#include "utils/fmgroids.h" +#include "utils/fmgrprotos.h" + +/* + * Shared-memory WAL prohibit state structure + */ +typedef struct WALProhibitData +{ + /* + * Indicates current WAL prohibit state counter and the last two bits of + * this counter indicates current wal prohibit state. + */ + pg_atomic_uint32 wal_prohibit_counter; + + /* Signaled when requested WAL prohibit state changes */ + ConditionVariable wal_prohibit_cv; +} WALProhibitData; + +static WALProhibitData *WALProhibit = NULL; + +static inline uint32 GetWALProhibitCounter(void); +static inline uint32 AdvanceWALProhibitStateCounter(void); + +/* + * ProcessBarrierWALProhibit() + * + * Force a backend to take an appropriate action when system wide WAL prohibit + * state is changing. + */ +bool +ProcessBarrierWALProhibit(void) +{ + /* + * Kill off any transactions that have an XID *before* allowing the system + * to go WAL prohibit state. + */ + if (FullTransactionIdIsValid(GetTopFullTransactionIdIfAny())) + { + /* + * Should be here only while transiting towards the WAL prohibit + * state. + */ + Assert(GetWALProhibitState() == WALPROHIBIT_STATE_READ_ONLY); + + /* + * XXX: Kill off the whole session by throwing FATAL instead of + * killing transaction by throwing ERROR due to following reasons that + * need be thought: + * + * 1. Due to some presents challenges with the wire protocol, we could + * not simply kill of idle transaction. + * + * 2. If we are here in subtransaction then the ERROR will kill the + * current subtransaction only. In the case of invalidations, that + * might be good enough, but for XID assignment it's not, because + * assigning an XID to a subtransaction also causes higher + * sub-transaction levels and the parent transaction to get XIDs. + */ + ereport(FATAL, + (errcode(ERRCODE_ACTIVE_SQL_TRANSACTION), + errmsg("WAL is now prohibited"), + errhint("Sessions with open write transactions must be terminated."))); + } + + /* Return to "check" state */ + ResetLocalXLogInsertAllowed(); + + return true; +} + +/* + * pg_prohibit_wal() + * + * SQL callable function to toggle WAL prohibit state. + * + * NB: Function always returns true that leaves scope for the future code + * changes might need to return false for some reason. + */ +Datum +pg_prohibit_wal(PG_FUNCTION_ARGS) +{ + bool walprohibit = PG_GETARG_BOOL(0); + uint32 wal_prohibit_counter; + uint32 target_counter_value; + + /* WAL prohibit state changes not allowed during recovery. */ + PreventCommandDuringRecovery("pg_prohibit_wal()"); + + /* For more detail on state transition, see comment for WALProhibitState */ + switch (GetWALProhibitState()) + { + case WALPROHIBIT_STATE_READ_WRITE: + if (!walprohibit) + PG_RETURN_BOOL(true); /* already in the requested state */ + break; + + case WALPROHIBIT_STATE_GOING_READ_WRITE: + if (walprohibit) + ereport(ERROR, + (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE), + errmsg("system state transition to WAL permission is already in progress"), + errhint("Try again after sometime."))); + break; + + case WALPROHIBIT_STATE_READ_ONLY: + if (walprohibit) + PG_RETURN_BOOL(true); /* already in the requested state */ + break; + + case WALPROHIBIT_STATE_GOING_READ_ONLY: + if (!walprohibit) + ereport(ERROR, + (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE), + errmsg("system state transition to WAL prohibition is already in progress"), + errhint("Try again after sometime."))); + break; + } + + wal_prohibit_counter = AdvanceWALProhibitStateCounter(); + target_counter_value = wal_prohibit_counter + 1; + +#ifdef USE_ASSERT_CHECKING + { + /* Target state must be the requested one. */ + WALProhibitState target_state = + CounterGetWALProhibitState(target_counter_value); + + Assert((walprohibit && target_state == WALPROHIBIT_STATE_READ_ONLY) || + (!walprohibit && target_state == WALPROHIBIT_STATE_READ_WRITE)); + } +#endif + + /* + * If in a standalone backend, just do it ourselves. + */ + if (!IsPostmasterEnvironment) + { + ProcessWALProhibitStateChangeRequest(); + PG_RETURN_BOOL(true); + } + + /* + * It is not a final state since we yet to convey this WAL prohibit state + * to all backend. Checkpointer will do that and update the shared memory + * wal prohibit state counter and control file. + */ + if (!SendSignalToCheckpointer(SIGUSR1)) + { + ereport(WARNING, + (errmsg("could not change system state now"), + errdetail("Checkpointer might not be running."), + errhint("The relaunched checkpointer process will automatically complete the system state change."))); + PG_RETURN_BOOL(true); /* no wait */ + } + + /* Wait for the state counter in shared memory to change. */ + ConditionVariablePrepareToSleep(&WALProhibit->wal_prohibit_cv); + + /* + * We'll be done once the wal prohibit state counter reaches to target + * value. + */ + while (GetWALProhibitCounter() < target_counter_value) + ConditionVariableSleep(&WALProhibit->wal_prohibit_cv, + WAIT_EVENT_WALPROHIBIT_STATE_CHANGE); + ConditionVariableCancelSleep(); + + PG_RETURN_BOOL(true); +} + +/* + * IsWALProhibited() + * + * Is the system still in WAL prohibited state? + */ +bool +IsWALProhibited(void) +{ + /* Other than read-write state will be considered as WAL prohibited state */ + return (GetWALProhibitState() != WALPROHIBIT_STATE_READ_WRITE); +} + +/* + * AdvanceWALProhibitStateCounter() + * + * Increment wal prohibit counter by 1. + */ +static inline uint32 +AdvanceWALProhibitStateCounter(void) +{ + return pg_atomic_add_fetch_u32(&WALProhibit->wal_prohibit_counter, 1); +} + +/* + * ProcessWALProhibitStateChangeRequest() + */ +void +ProcessWALProhibitStateChangeRequest(void) +{ + bool isReadOnlyRequest; + uint64 barrier_gen; + uint32 wal_prohibit_counter PG_USED_FOR_ASSERTS_ONLY; + WALProhibitState cur_state; + + /* + * Should be a checkpointer process or a single-user backend to complete WAL + * prohibit state transition. + */ + if (!(AmCheckpointerProcess() || !IsPostmasterEnvironment)) + return; + + /* Fetch shared wal prohibit state */ + cur_state = GetWALProhibitState(); + + /* Quick exit if not in transition state */ + if (cur_state != WALPROHIBIT_STATE_GOING_READ_ONLY && + cur_state != WALPROHIBIT_STATE_GOING_READ_WRITE) + return; + + isReadOnlyRequest = (cur_state == WALPROHIBIT_STATE_GOING_READ_ONLY); + + /* + * Update control file to make the state persistent. + * + * Once wal prohibit state transition set then that needs to be completed. + * If the server crashes before the state completion, then the control file + * information will be used to set the final wal prohibit state on restart. + */ + SetControlFileWALProhibitFlag(isReadOnlyRequest); + + /* + * If the server is started in wal prohibited state then the required wal + * write operation in the startup process to start the server normally has + * been skipped at that time, if it is, then does that right away. + */ + if (!isReadOnlyRequest) + { + XLogAcceptWritesState state = GetXLogWriteAllowedState(); + + /* Quick exit if already in XLogAcceptWrites() */ + if (state == XLOG_ACCEPT_WRITES_STARTED) + return; + + /* Complete pending XLogAcceptWrites() */ + if (state != XLOG_ACCEPT_WRITES_DONE) + PerformPendingXLogAcceptWrites(); + } + + /* + * Increment shared memory state counter first and them emit global barrier. + * + * Any backend connect afterword will follow respective WAL prohibited state. + */ + wal_prohibit_counter = AdvanceWALProhibitStateCounter(); + + /* + * WAL prohibit state change is initiated. We need to complete the state + * transition by setting requested WAL prohibit state in all backends. + */ + elog(DEBUG1, "waiting for backends to adopt requested WAL prohibit state change"); + + /* Emit global barrier */ + barrier_gen = EmitProcSignalBarrier(PROCSIGNAL_BARRIER_WALPROHIBIT); + WaitForProcSignalBarrier(barrier_gen); + + /* + * Don't need to be too aggressive to flush XLOG data right away since + * XLogFlush is not restricted in the wal prohibited state. + */ + XLogFlush(GetXLogWriteRecPtr()); + + if (isReadOnlyRequest) + { + /* Should have set the final state where WAL is prohibited. */ + Assert(CounterGetWALProhibitState(wal_prohibit_counter) == + WALPROHIBIT_STATE_READ_ONLY); + + ereport(LOG, (errmsg("WAL is now prohibited"))); + } + else + { + /* + * Should have set the final state where WAL is no longer + * prohibited. + */ + Assert(CounterGetWALProhibitState(wal_prohibit_counter) == + WALPROHIBIT_STATE_READ_WRITE); + + ereport(LOG, (errmsg("WAL is no longer prohibited"))); + } + + /* Wake up the backend waiting on this. */ + ConditionVariableBroadcast(&WALProhibit->wal_prohibit_cv); +} + +/* + * GetWALProhibitCounter() + */ +static inline uint32 +GetWALProhibitCounter(void) +{ + return pg_atomic_read_u32(&WALProhibit->wal_prohibit_counter); +} + +/* + * GetWALProhibitState() + */ +WALProhibitState +GetWALProhibitState(void) +{ + return CounterGetWALProhibitState(GetWALProhibitCounter()); +} + +/* + * WALProhibitStateCounterInit() + * + * Initialization of shared wal prohibit state counter. + */ +void +WALProhibitStateCounterInit(bool wal_prohibited) +{ + WALProhibitState new_state; + + Assert(AmStartupProcess() || !IsPostmasterEnvironment); + + new_state = wal_prohibited ? + WALPROHIBIT_STATE_READ_ONLY : WALPROHIBIT_STATE_READ_WRITE; + + pg_atomic_init_u32(&WALProhibit->wal_prohibit_counter, (uint32) new_state); +} + +/* + * WALProhibitStateShmemInit() + * + * Initialization of shared memory for WAL prohibit state. + */ +void +WALProhibitStateShmemInit(void) +{ + bool found; + + WALProhibit = (WALProhibitData *) + ShmemInitStruct("WAL Prohibit State", + sizeof(WALProhibitData), + &found); + + if (!found) + { + /* First time through ... */ + memset(WALProhibit, 0, sizeof(WALProhibitData)); + ConditionVariableInit(&WALProhibit->wal_prohibit_cv); + } +} diff --git a/src/backend/access/transam/xact.c b/src/backend/access/transam/xact.c index bf2fc08d94..62d1018220 100644 --- a/src/backend/access/transam/xact.c +++ b/src/backend/access/transam/xact.c @@ -2030,23 +2030,27 @@ StartTransaction(void) Assert(s->prevSecContext == 0); /* - * Make sure we've reset xact state variables + * Reset xact state variables. * - * If recovery is still in progress, mark this transaction as read-only. - * We have lower level defences in XLogInsert and elsewhere to stop us - * from modifying data during recovery, but this gives the normal - * indication to the user that the transaction is read-only. - */ - if (RecoveryInProgress()) - { - s->startedInRecovery = true; - XactReadOnly = true; - } - else - { - s->startedInRecovery = false; - XactReadOnly = DefaultXactReadOnly; - } + * If it is not currently possible to insert write-ahead log records, either + * because we are still in recovery or because pg_prohibit_wal() function + * has been executed, force this to be a read-only transaction. We have + * lower level defences in XLogBeginInsert() and elsewhere to stop us from + * modifying data during recovery when !XLogInsertAllowed(), but this gives + * the normal indication to the user that the transaction is read-only. + * + * On the other hand, we only need to set the startedInRecovery flag when + * the transaction started during recovery, and not when WAL is otherwise + * prohibited. This information is used by RelationGetIndexScan() to decide + * whether to permit (1) relying on existing killed-tuple markings and (2) + * further killing of index tuples. Even when WAL is prohibited on the + * master, it's still the master, so the former is OK; and since killing + * index tuples doesn't generate WAL, the latter is also OK. See comments + * in RelationGetIndexScan() and MarkBufferDirtyHint(). + */ + XactReadOnly = DefaultXactReadOnly || !XLogInsertAllowed(); + s->startedInRecovery = RecoveryInProgress(); + XactDeferrable = DefaultXactDeferrable; XactIsoLevel = DefaultXactIsoLevel; forceSyncCommit = false; diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c index 19a499e4e6..5447dcb085 100644 --- a/src/backend/access/transam/xlog.c +++ b/src/backend/access/transam/xlog.c @@ -55,6 +55,7 @@ #include "access/timeline.h" #include "access/transam.h" #include "access/twophase.h" +#include "access/walprohibit.h" #include "access/xact.h" #include "access/xlog_internal.h" #include "access/xlogarchive.h" @@ -226,9 +227,10 @@ static bool LocalRecoveryInProgress = true; * 0: unconditionally not allowed to insert XLOG * -1: must check RecoveryInProgress(); disallow until it is false * Most processes start with -1 and transition to 1 after seeing that recovery - * is not in progress. But we can also force the value for special cases. - * The coding in XLogInsertAllowed() depends on the first two of these states - * being numerically the same as bool true and false. + * is not in progress or the server state is not a WAL prohibited state. But + * we can also force the value for special cases. The coding in + * XLogInsertAllowed() depends on the first two of these states being + * numerically the same as bool true and false. */ static int LocalXLogInsertAllowed = -1; @@ -569,6 +571,12 @@ typedef struct XLogCtlData XLogRecPtr endOfLog; TimeLineID endOfLogTLI; + /* + * SharedXLogAllowWritesState indicates the state of the last recovery + * checkpoint and required wal write to start the normal server. + * Protected by info_lck. + */ + XLogAcceptWritesState SharedXLogAllowWritesState; slock_t info_lck; /* locks shared variables shown above */ } XLogCtlData; @@ -4216,6 +4224,16 @@ UpdateControlFile(void) update_controlfile(DataDir, ControlFile, true); } +/* Set ControlFile's WAL prohibit flag */ + void +SetControlFileWALProhibitFlag(bool walProhibited) +{ + LWLockAcquire(ControlFileLock, LW_EXCLUSIVE); + ControlFile->wal_prohibited = walProhibited; + UpdateControlFile(); + LWLockRelease(ControlFileLock); +} + /* * Returns the unique system identifier from control file. */ @@ -4490,6 +4508,7 @@ XLOGShmemInit(void) XLogCtl->SharedRecoveryState = RECOVERY_STATE_CRASH; XLogCtl->InstallXLogFileSegmentActive = false; XLogCtl->WalWriterSleeping = false; + XLogCtl->SharedXLogAllowWritesState = XLOG_ACCEPT_WRITES_NONE; SpinLockInit(&XLogCtl->Insert.insertpos_lck); SpinLockInit(&XLogCtl->info_lck); @@ -5588,8 +5607,29 @@ StartupXLOG(void) */ Insert->fullPageWrites = lastFullPageWrites; - /* Prepare to accept WAL writes. */ - promoted = XLogAcceptWrites(); + /* + * Before enabling WAL insertion, initialize WAL prohibit state in shared + * memory that will decide the further WAL insert should be allowed or + * not. + */ + WALProhibitStateCounterInit(ControlFile->wal_prohibited); + + /* + * Skip wal writes and end of recovery checkpoint if the system is in WAL + * prohibited state. + */ + if (IsWALProhibited()) + { + XLogCtl->SharedXLogAllowWritesState = XLOG_ACCEPT_WRITES_DELAYED; + + ereport(LOG, + (errmsg("skipping startup checkpoint because the WAL is now prohibited"))); + } + else + { + /* Prepare to accept WAL writes. */ + promoted = XLogAcceptWrites(); + } /* * All done with end-of-recovery actions. @@ -5658,6 +5698,28 @@ XLogAcceptWrites(void) */ volatile XLogCtlData *xlogctl = XLogCtl; + /* + * Quick exit if required wal writes to start server normally are performed + * or in progress. + */ + if (xlogctl->SharedXLogAllowWritesState == XLOG_ACCEPT_WRITES_DONE || + xlogctl->SharedXLogAllowWritesState == XLOG_ACCEPT_WRITES_STARTED) + return promoted; + + /* + * Update started shared memory status to avoid any re-entrance. Spinlock + * protection isn't needed since only one process will be updating this + * value at a time. + */ + xlogctl->SharedXLogAllowWritesState = XLOG_ACCEPT_WRITES_STARTED; + + /* + * If the system in wal prohibited state, then only the checkpointer process + * should be here to complete this operation which might have skipped + * previously while booting the system in WAL prohibited state. + */ + Assert(!IsWALProhibited() || AmCheckpointerProcess()); + /* Enable WAL writes for this backend only. */ LocalSetXLogInsertAllowed(); @@ -5699,9 +5761,34 @@ XLogAcceptWrites(void) */ CompleteCommitTsInitialization(); + /* + * Update completion status. + */ + XLogCtl->SharedXLogAllowWritesState = XLOG_ACCEPT_WRITES_DONE; + return promoted; } +/* + * Wrapper function to call XLogAcceptWrites() for checkpointer process. + */ +void +PerformPendingXLogAcceptWrites(void) +{ + /* Prepare to accept WAL writes. */ + (void) XLogAcceptWrites(); + + /* + * We need to update DBState explicitly like the startup process + * because end-of-recovery checkpoint would set db state to + * shutdown. + */ + LWLockAcquire(ControlFileLock, LW_EXCLUSIVE); + ControlFile->state = DB_IN_PRODUCTION; + UpdateControlFile(); + LWLockRelease(ControlFileLock); +} + /* * Callback from PerformWalRecovery(), called when we switch from crash * recovery to archive recovery mode. Updates the control file accordingly. @@ -5815,6 +5902,11 @@ PerformRecoveryXLogAction(void) */ CreateEndOfRecoveryRecord(); } + else if (AmCheckpointerProcess()) + { + /* In checkpointer process, just do it ourselves */ + CreateCheckPoint(CHECKPOINT_END_OF_RECOVERY | CHECKPOINT_IMMEDIATE); + } else { RequestCheckpoint(CHECKPOINT_END_OF_RECOVERY | @@ -5882,9 +5974,9 @@ GetRecoveryState(void) /* * Is this process allowed to insert new WAL records? * - * Ordinarily this is essentially equivalent to !RecoveryInProgress(). - * But we also have provisions for forcing the result "true" or "false" - * within specific processes regardless of the global state. + * Ordinarily this is essentially equivalent to !RecoveryInProgress() and + * !IsWALProhibited(). But we also have provisions for forcing the result + * "true" or "false" within specific processes regardless of the global state. */ bool XLogInsertAllowed(void) @@ -5903,9 +5995,20 @@ XLogInsertAllowed(void) if (RecoveryInProgress()) return false; + /* Or, in WAL prohibited state */ + if (IsWALProhibited()) + { + /* + * Set it to "unconditionally false" to avoid checking until it gets + * reset. + */ + LocalXLogInsertAllowed = 0; + return false; + } + /* - * On exit from recovery, reset to "unconditionally true", since there is - * no need to keep checking. + * On exit from recovery or WAL prohibited state, reset to + * "unconditionally true", since there is no need to keep checking. */ LocalXLogInsertAllowed = 1; return true; @@ -5929,6 +6032,12 @@ LocalSetXLogInsertAllowed(void) return oldXLogAllowed; } +void +ResetLocalXLogInsertAllowed(void) +{ + LocalXLogInsertAllowed = -1; +} + /* * Return the current Redo pointer from shared memory. * @@ -6079,6 +6188,16 @@ GetLastSegSwitchData(XLogRecPtr *lastSwitchLSN) return result; } +/* + * Fetch latest state of allow WAL writes. + */ +XLogAcceptWritesState +GetXLogWriteAllowedState(void) +{ + /* Since the value can't be changing concurrently, no lock is required. */ + return ((volatile XLogCtlData *) XLogCtl)->SharedXLogAllowWritesState; +} + /* * This must be called ONCE during postmaster or standalone-backend shutdown */ @@ -6109,9 +6228,13 @@ ShutdownXLOG(int code, Datum arg) */ WalSndWaitStopping(); + /* + * The restartpoint, checkpoint, or xlog rotation will be performed if the + * WAL writing is permitted. + */ if (RecoveryInProgress()) CreateRestartPoint(CHECKPOINT_IS_SHUTDOWN | CHECKPOINT_IMMEDIATE); - else + else if (XLogInsertAllowed()) { /* * If archiving is enabled, rotate the last XLOG file so that all the @@ -6124,6 +6247,9 @@ ShutdownXLOG(int code, Datum arg) CreateCheckPoint(CHECKPOINT_IS_SHUTDOWN | CHECKPOINT_IMMEDIATE); } + else + ereport(LOG, + (errmsg("skipping shutdown checkpoint because the WAL is now prohibited"))); } /* @@ -6374,8 +6500,13 @@ CreateCheckPoint(int flags) shutdown = false; /* sanity check */ - if (RecoveryInProgress() && (flags & CHECKPOINT_END_OF_RECOVERY) == 0) - elog(ERROR, "can't create a checkpoint during recovery"); + if ((flags & CHECKPOINT_END_OF_RECOVERY) == 0) + { + if (RecoveryInProgress()) + elog(ERROR, "can't create a checkpoint during recovery"); + else if (!XLogInsertAllowed()) + elog(ERROR, "can't create a checkpoint while WAL is prohibited"); + } /* * Prepare to accumulate statistics. @@ -6846,10 +6977,11 @@ CreateOverwriteContrecordRecord(XLogRecPtr aborted_lsn, XLogRecPtr pagePtr, XLogRecPtr recptr; XLogPageHeader pagehdr; XLogRecPtr startPos; + XLogAcceptWritesState state = GetXLogWriteAllowedState(); /* sanity checks */ - if (!RecoveryInProgress()) - elog(ERROR, "can only be used at end of recovery"); + if (state != XLOG_ACCEPT_WRITES_STARTED) + elog(ERROR, "can only be used at enabling WAL writes"); if (pagePtr % XLOG_BLCKSZ != 0) elog(ERROR, "invalid position for missing continuation record %X/%X", LSN_FORMAT_ARGS(pagePtr)); diff --git a/src/backend/access/transam/xlogrecovery.c b/src/backend/access/transam/xlogrecovery.c index 99cad8bcf9..e120970446 100644 --- a/src/backend/access/transam/xlogrecovery.c +++ b/src/backend/access/transam/xlogrecovery.c @@ -558,13 +558,27 @@ InitWalRecovery(ControlFileData *ControlFile, bool *wasShutdown_ptr, (errmsg("starting archive recovery"))); } - /* - * Take ownership of the wakeup latch if we're going to sleep during - * recovery. - */ if (ArchiveRecoveryRequested) + { + /* + * Take ownership of the wakeup latch if we're going to sleep during + * recovery. + */ OwnLatch(&XLogRecoveryCtl->recoveryWakeupLatch); + /* + * Since archive recovery is requested, we cannot be in a wal prohibited + * state. + */ + if (ControlFile->wal_prohibited) + { + /* No need to hold ControlFileLock yet, we aren't up far enough */ + ControlFile->wal_prohibited = false; + ereport(LOG, + (errmsg("clearing WAL prohibition because the system is in archive recovery"))); + } + } + private = palloc0(sizeof(XLogPageReadPrivate)); xlogreader = XLogReaderAllocate(wal_segment_size, NULL, diff --git a/src/backend/catalog/system_functions.sql b/src/backend/catalog/system_functions.sql index 73da687d5d..ad8769a534 100644 --- a/src/backend/catalog/system_functions.sql +++ b/src/backend/catalog/system_functions.sql @@ -709,6 +709,8 @@ REVOKE EXECUTE ON FUNCTION pg_ls_logicalmapdir() FROM PUBLIC; REVOKE EXECUTE ON FUNCTION pg_ls_replslotdir(text) FROM PUBLIC; +REVOKE EXECUTE ON FUNCTION pg_prohibit_wal(bool) FROM public; + -- -- We also set up some things as accessible to standard roles. -- diff --git a/src/backend/commands/variable.c b/src/backend/commands/variable.c index e5ddcda0b4..a446d9c13a 100644 --- a/src/backend/commands/variable.c +++ b/src/backend/commands/variable.c @@ -508,6 +508,13 @@ check_transaction_read_only(bool *newval, void **extra, GucSource source) GUC_check_errmsg("cannot set transaction read-write mode during recovery"); return false; } + /* Can't go to r/w mode while WAL is prohibited */ + if (!XLogInsertAllowed()) + { + GUC_check_errcode(ERRCODE_FEATURE_NOT_SUPPORTED); + GUC_check_errmsg("cannot set transaction read-write mode while WAL is prohibited"); + return false; + } } return true; diff --git a/src/backend/postmaster/autovacuum.c b/src/backend/postmaster/autovacuum.c index f36c40e852..1f0398e1f1 100644 --- a/src/backend/postmaster/autovacuum.c +++ b/src/backend/postmaster/autovacuum.c @@ -694,10 +694,12 @@ AutoVacLauncherMain(int argc, char *argv[]) /* * There are some conditions that we need to check before trying to - * start a worker. First, we need to make sure that there is a worker - * slot available. Second, we need to make sure that no other worker - * failed while starting up. + * start a worker. First, the wal writes are permitted. Second, we + * need to make sure that there is a worker slot available. Third, we + * need to make sure that no other worker failed while starting up. */ + if (!XLogInsertAllowed()) + continue; current_time = GetCurrentTimestamp(); LWLockAcquire(AutovacuumLock, LW_SHARED); diff --git a/src/backend/postmaster/bgwriter.c b/src/backend/postmaster/bgwriter.c index 91e6f6ea18..ecbcac18c5 100644 --- a/src/backend/postmaster/bgwriter.c +++ b/src/backend/postmaster/bgwriter.c @@ -273,7 +273,7 @@ BackgroundWriterMain(void) * Checkpointer, when active, is barely ever in its mainloop and thus * makes it hard to log regularly. */ - if (XLogStandbyInfoActive() && !RecoveryInProgress()) + if (XLogStandbyInfoActive() && XLogInsertAllowed()) { TimestampTz timeout = 0; TimestampTz now = GetCurrentTimestamp(); diff --git a/src/backend/postmaster/checkpointer.c b/src/backend/postmaster/checkpointer.c index c937c39f50..09dd645bca 100644 --- a/src/backend/postmaster/checkpointer.c +++ b/src/backend/postmaster/checkpointer.c @@ -36,6 +36,7 @@ #include #include +#include "access/walprohibit.h" #include "access/xlog.h" #include "access/xlog_internal.h" #include "access/xlogrecovery.h" @@ -350,6 +351,7 @@ CheckpointerMain(void) pg_time_t now; int elapsed_secs; int cur_timeout; + WALProhibitState cur_state; /* Clear any already-pending wakeups */ ResetLatch(MyLatch); @@ -359,6 +361,22 @@ CheckpointerMain(void) */ AbsorbSyncRequests(); HandleCheckpointerInterrupts(); + ProcessWALProhibitStateChangeRequest(); + + /* Should be in WAL permitted state to perform the checkpoint */ + cur_state = GetWALProhibitState(); + if (cur_state != WALPROHIBIT_STATE_READ_WRITE) + { + /* + * Don't let Checkpointer process do anything until someone wakes it + * up. For example a backend might later on request us to put the + * system back to read-write state. + */ + if (cur_state == WALPROHIBIT_STATE_READ_ONLY) + (void) WaitLatch(MyLatch, WL_LATCH_SET | WL_EXIT_ON_PM_DEATH, + -1, WAIT_EVENT_WALPROHIBIT_STATE_CHANGE); + continue; + } /* * Detect a pending checkpoint request by checking whether the flags @@ -702,6 +720,9 @@ CheckpointWriteDelay(int flags, double progress) if (!AmCheckpointerProcess()) return; + /* Check for wal prohibit state change request */ + ProcessWALProhibitStateChangeRequest(); + /* * Perform the usual duties and take a nap, unless we're behind schedule, * in which case we just try to catch up as quickly as possible. @@ -1352,3 +1373,19 @@ FirstCallSinceLastCheckpoint(void) return FirstCall; } + +/* + * SendSignalToCheckpointer allows any process to send a signal to the checkpoint + * process. + */ +bool +SendSignalToCheckpointer(int signum) +{ + if (CheckpointerShmem->checkpointer_pid == 0) + return false; + + if (kill(CheckpointerShmem->checkpointer_pid, signum) != 0) + return false; + + return true; /* Signaled checkpointer successfully */ +} diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c index 75e456360b..68f434ef74 100644 --- a/src/backend/storage/ipc/ipci.c +++ b/src/backend/storage/ipc/ipci.c @@ -22,6 +22,7 @@ #include "access/subtrans.h" #include "access/syncscan.h" #include "access/twophase.h" +#include "access/walprohibit.h" #include "access/xlogprefetcher.h" #include "access/xlogrecovery.h" #include "commands/async.h" @@ -254,6 +255,12 @@ CreateSharedMemoryAndSemaphores(void) MultiXactShmemInit(); InitBufferPool(); + /* + * Set up shared memory structure need to handle concurrent WAL prohibit + * state change requests. + */ + WALProhibitStateShmemInit(); + /* * Set up lock manager */ diff --git a/src/backend/storage/ipc/procsignal.c b/src/backend/storage/ipc/procsignal.c index f41563a0a4..471ed11128 100644 --- a/src/backend/storage/ipc/procsignal.c +++ b/src/backend/storage/ipc/procsignal.c @@ -18,6 +18,7 @@ #include #include "access/parallel.h" +#include "access/walprohibit.h" #include "port/pg_bitutils.h" #include "commands/async.h" #include "miscadmin.h" @@ -539,6 +540,9 @@ ProcessProcSignalBarrier(void) case PROCSIGNAL_BARRIER_SMGRRELEASE: processed = ProcessBarrierSmgrRelease(); break; + case PROCSIGNAL_BARRIER_WALPROHIBIT: + processed = ProcessBarrierWALProhibit(); + break; } /* diff --git a/src/backend/storage/lmgr/lock.c b/src/backend/storage/lmgr/lock.c index ee2e15c17e..36a08ebf30 100644 --- a/src/backend/storage/lmgr/lock.c +++ b/src/backend/storage/lmgr/lock.c @@ -794,15 +794,15 @@ LockAcquireExtended(const LOCKTAG *locktag, if (lockmode <= 0 || lockmode > lockMethodTable->numLockModes) elog(ERROR, "unrecognized lock mode: %d", lockmode); - if (RecoveryInProgress() && !InRecovery && + if (!XLogInsertAllowed() && !InRecovery && (locktag->locktag_type == LOCKTAG_OBJECT || locktag->locktag_type == LOCKTAG_RELATION) && lockmode > RowExclusiveLock) ereport(ERROR, (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE), - errmsg("cannot acquire lock mode %s on database objects while recovery is in progress", + errmsg("cannot acquire lock mode %s on database objects while recovery is in progress or when WAL is prohibited", lockMethodTable->lockModeNames[lockmode]), - errhint("Only RowExclusiveLock or less can be acquired on database objects during recovery."))); + errhint("Only RowExclusiveLock or less can be acquired on database objects during recovery or when WAL is prohibited"))); #ifdef LOCK_DEBUG if (LOCK_DEBUG_ENABLED(locktag)) diff --git a/src/backend/storage/sync/sync.c b/src/backend/storage/sync/sync.c index c695d816fc..1512150e4f 100644 --- a/src/backend/storage/sync/sync.c +++ b/src/backend/storage/sync/sync.c @@ -21,6 +21,7 @@ #include "access/commit_ts.h" #include "access/clog.h" #include "access/multixact.h" +#include "access/walprohibit.h" #include "access/xlog.h" #include "access/xlogutils.h" #include "commands/tablespace.h" @@ -255,9 +256,17 @@ SyncPostCheckpoint(void) entry->canceled = true; /* - * As in ProcessSyncRequests, we don't want to stop absorbing fsync - * requests for a long time when there are many deletions to be done. - * We can safely call AbsorbSyncRequests() at this point in the loop. + * As in ProcessSyncRequests, we don't want to stop processing wal + * prohibit change requests for a long time when there are many + * deletions to be done. It needs to be check and processed by + * checkpointer as soon as possible. + */ + ProcessWALProhibitStateChangeRequest(); + + /* + * Similarly, we don't want to stop absorbing fsync requests for the + * long time. We can safely call AbsorbSyncRequests() at this point in + * the loop (note it might try to delete list entries). */ if (--absorb_counter <= 0) { @@ -315,6 +324,9 @@ ProcessSyncRequests(void) if (!pendingOps) elog(ERROR, "cannot sync without a pendingOps table"); + /* Check for wal prohibit state change request for checkpointer */ + ProcessWALProhibitStateChangeRequest(); + /* * If we are in the checkpointer, the sync had better include all fsync * requests that were queued by backends up to this point. The tightest @@ -373,6 +385,13 @@ ProcessSyncRequests(void) { int failures; + /* + * Don't want to stop processing wal prohibit change requests for a long + * time when there are many fsync requests to be processed. It needs to + * be check and processed by checkpointer as soon as possible. + */ + ProcessWALProhibitStateChangeRequest(); + /* * If the entry is new then don't process it this time; it is new. * Note "continue" bypasses the hash-remove call at the bottom of the @@ -459,6 +478,12 @@ ProcessSyncRequests(void) errmsg_internal("could not fsync file \"%s\" but retrying: %m", path))); + /* + * For the same reason mentioned previously for the same + * function call. + */ + ProcessWALProhibitStateChangeRequest(); + /* * Absorb incoming requests and check to see if a cancel * arrived for this relation fork. diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c index f364a9b88a..b6f96a9cb9 100644 --- a/src/backend/tcop/utility.c +++ b/src/backend/tcop/utility.c @@ -19,6 +19,7 @@ #include "access/htup_details.h" #include "access/reloptions.h" #include "access/twophase.h" +#include "access/walprohibit.h" #include "access/xact.h" #include "access/xlog.h" #include "catalog/catalog.h" diff --git a/src/backend/utils/activity/wait_event.c b/src/backend/utils/activity/wait_event.c index 87c15b9c6f..aa5330eb63 100644 --- a/src/backend/utils/activity/wait_event.c +++ b/src/backend/utils/activity/wait_event.c @@ -741,6 +741,9 @@ pgstat_get_wait_io(WaitEventIO w) case WAIT_EVENT_WAL_WRITE: event_name = "WALWrite"; break; + case WAIT_EVENT_WALPROHIBIT_STATE_CHANGE: + event_name = "SystemWALProhibitStateChange"; + break; /* no default case, so that compiler will warn */ } diff --git a/src/backend/utils/misc/guc.c b/src/backend/utils/misc/guc.c index 22b5571a70..72e9990ca9 100644 --- a/src/backend/utils/misc/guc.c +++ b/src/backend/utils/misc/guc.c @@ -39,6 +39,7 @@ #include "access/toast_compression.h" #include "access/transam.h" #include "access/twophase.h" +#include "access/walprohibit.h" #include "access/xact.h" #include "access/xlog_internal.h" #include "access/xlogprefetcher.h" @@ -242,6 +243,7 @@ static bool check_recovery_target_lsn(char **newval, void **extra, GucSource sou static void assign_recovery_target_lsn(const char *newval, void *extra); static bool check_primary_slot_name(char **newval, void **extra, GucSource source); static bool check_default_with_oids(bool *newval, void **extra, GucSource source); +static const char *show_wal_prohibited(void); /* Private functions in guc-file.l that need to be called from guc.c */ static ConfigVariable *ProcessConfigFileInternal(GucContext context, @@ -716,6 +718,7 @@ static char *recovery_target_string; static char *recovery_target_xid_string; static char *recovery_target_name_string; static char *recovery_target_lsn_string; +static bool wal_prohibited; /* should be static, but commands/variable.c needs to get at this */ @@ -2182,6 +2185,18 @@ static struct config_bool ConfigureNamesBool[] = NULL, NULL, NULL }, + { + /* Not for general use */ + {"wal_prohibited", PGC_INTERNAL, WAL_SETTINGS, + gettext_noop("Shows whether the WAL is prohibited."), + NULL, + GUC_NO_RESET_ALL | GUC_NOT_IN_SAMPLE | GUC_DISALLOW_IN_FILE + }, + &wal_prohibited, + false, + NULL, NULL, show_wal_prohibited + }, + /* End-of-list marker */ { {NULL, 0, 0, NULL, NULL}, NULL, false, NULL, NULL, NULL @@ -12891,4 +12906,16 @@ check_default_with_oids(bool *newval, void **extra, GucSource source) return true; } +/* + * NB: The return string should be the same as the _ShowOption() for boolean + * type. + */ +static const char * +show_wal_prohibited(void) +{ + if (IsWALProhibited()) + return "on"; + return "off"; +} + #include "guc-file.c" diff --git a/src/bin/pg_controldata/pg_controldata.c b/src/bin/pg_controldata/pg_controldata.c index f911f98d94..e4d99a50c0 100644 --- a/src/bin/pg_controldata/pg_controldata.c +++ b/src/bin/pg_controldata/pg_controldata.c @@ -284,6 +284,8 @@ main(int argc, char *argv[]) LSN_FORMAT_ARGS(ControlFile->backupEndPoint)); printf(_("End-of-backup record required: %s\n"), ControlFile->backupEndRequired ? _("yes") : _("no")); + printf(_("WAL write prohibited: %s\n"), + ControlFile->wal_prohibited ? _("yes") : _("no")); printf(_("wal_level setting: %s\n"), wal_level_str(ControlFile->wal_level)); printf(_("wal_log_hints setting: %s\n"), diff --git a/src/include/access/walprohibit.h b/src/include/access/walprohibit.h new file mode 100644 index 0000000000..d71522cbf3 --- /dev/null +++ b/src/include/access/walprohibit.h @@ -0,0 +1,60 @@ +/* + * walprohibit.h + * + * PostgreSQL write-ahead log prohibit states + * + * Portions Copyright (c) 2021, PostgreSQL Global Development Group + * + * src/include/access/walprohibit.h + */ + +#ifndef WALPROHIBIT_H +#define WALPROHIBIT_H + +#include "access/xact.h" +#include "access/xlog.h" +#include "miscadmin.h" +#include "nodes/parsenodes.h" + +/* + * WAL Prohibit States. + * + * There are four possible WAL states. A brand new database cluster is always + * initially WALPROHIBIT_STATE_READ_WRITE. If the user tries to make it read + * only, then we enter the state WALPROHIBIT_STATE_GOING_READ_ONLY. When the + * transition is complete, we enter the state WALPROHIBIT_STATE_READ_ONLY. If + * the user subsequently tries to make it read write, we will enter the state + * WALPROHIBIT_STATE_GOING_READ_WRITE. When that transition is complete, we + * will enter the state WALPROHIBIT_STATE_READ_WRITE. These four state + * transitions are the only ones possible; for example, if we're currently in + * state WALPROHIBIT_STATE_GOING_READ_ONLY, an attempt to go read-write will + * produce an error, and a second attempt to go read-only will not cause a state + * change. Thus, we can represent the state as a shared-memory counter whose + * value only ever changes by adding 1. The initial value at postmaster startup + * is either 0 or 2, depending on whether the control file specifies the system + * is starting read-write or read-only. + */ +typedef enum +{ + WALPROHIBIT_STATE_READ_WRITE = 0, /* WAL permitted */ + WALPROHIBIT_STATE_GOING_READ_ONLY = 1, + WALPROHIBIT_STATE_READ_ONLY = 2, /* WAL prohibited */ + WALPROHIBIT_STATE_GOING_READ_WRITE = 3 +} WALProhibitState; + +static inline WALProhibitState +CounterGetWALProhibitState(uint32 wal_prohibit_counter) +{ + /* Extract last two bits */ + return (WALProhibitState) (wal_prohibit_counter & 3); +} + +extern bool ProcessBarrierWALProhibit(void); +extern void MarkCheckPointSkippedInWalProhibitState(void); +extern void WALProhibitStateCounterInit(bool wal_prohibited); +extern void WALProhibitStateShmemInit(void); +extern bool IsWALProhibited(void); +extern void ProcessWALProhibitStateChangeRequest(void); +extern WALProhibitState GetWALProhibitState(void); + +#endif /* WALPROHIBIT_H */ diff --git a/src/include/access/xlog.h b/src/include/access/xlog.h index 5e1e3446ae..19bf3171bd 100644 --- a/src/include/access/xlog.h +++ b/src/include/access/xlog.h @@ -80,6 +80,15 @@ typedef enum WalCompression WAL_COMPRESSION_ZSTD } WalCompression; +/* State of XLogAcceptWrites() execution */ +typedef enum XLogAcceptWritesState +{ + XLOG_ACCEPT_WRITES_NONE = 0, /* initial state, not started */ + XLOG_ACCEPT_WRITES_DELAYED, /* skipped XLogAcceptWrites() for now */ + XLOG_ACCEPT_WRITES_STARTED, /* inside XLogAcceptWrites() */ + XLOG_ACCEPT_WRITES_DONE /* done with XLogAcceptWrites() */ +} XLogAcceptWritesState; + /* Recovery states */ typedef enum RecoveryState { @@ -217,6 +226,7 @@ extern void issue_xlog_fsync(int fd, XLogSegNo segno, TimeLineID tli); extern bool RecoveryInProgress(void); extern RecoveryState GetRecoveryState(void); extern bool XLogInsertAllowed(void); +extern void ResetLocalXLogInsertAllowed(void); extern XLogRecPtr GetXLogInsertRecPtr(void); extern XLogRecPtr GetXLogWriteRecPtr(void); @@ -230,6 +240,7 @@ extern void BootStrapXLOG(void); extern void LocalProcessControlFile(bool reset); extern void StartupXLOG(void); extern void ShutdownXLOG(int code, Datum arg); +extern void PerformPendingXLogAcceptWrites(void); extern void CreateCheckPoint(int flags); extern bool CreateRestartPoint(int flags); extern WALAvailability GetWALAvailability(XLogRecPtr targetLSN); @@ -243,8 +254,10 @@ extern XLogRecPtr GetInsertRecPtr(void); extern XLogRecPtr GetFlushRecPtr(TimeLineID *insertTLI); extern TimeLineID GetWALInsertionTimeLine(void); extern XLogRecPtr GetLastImportantRecPtr(void); +extern XLogAcceptWritesState GetXLogWriteAllowedState(void); extern void SetWalWriterSleeping(bool sleeping); +extern void SetControlFileWALProhibitFlag(bool wal_prohibited); extern void assign_max_wal_size(int newval, void *extra); extern void assign_checkpoint_completion_target(double newval, void *extra); diff --git a/src/include/catalog/pg_control.h b/src/include/catalog/pg_control.h index 06368e2366..7078245b64 100644 --- a/src/include/catalog/pg_control.h +++ b/src/include/catalog/pg_control.h @@ -182,6 +182,9 @@ typedef struct ControlFileData int max_locks_per_xact; bool track_commit_timestamp; + /* WAL prohibited determines if the WAL insert is allowed or not. */ + bool wal_prohibited; + /* * This data is used to check for hardware-architecture compatibility of * the database and the backend executable. We need not check endianness diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat index 6d378ff785..67dcb8af87 100644 --- a/src/include/catalog/pg_proc.dat +++ b/src/include/catalog/pg_proc.dat @@ -11838,6 +11838,10 @@ proname => 'pg_partition_root', prorettype => 'regclass', proargtypes => 'regclass', prosrc => 'pg_partition_root' }, +{ oid => '4549', descr => 'change server to permit or prohibit wal writes', + proname => 'pg_prohibit_wal', prorettype => 'bool', + proargtypes => 'bool', prosrc => 'pg_prohibit_wal' }, + { oid => '4350', descr => 'Unicode normalization', proname => 'normalize', prorettype => 'text', proargtypes => 'text text', prosrc => 'unicode_normalize_func' }, diff --git a/src/include/postmaster/bgwriter.h b/src/include/postmaster/bgwriter.h index 2882efd67b..738ea5b0bb 100644 --- a/src/include/postmaster/bgwriter.h +++ b/src/include/postmaster/bgwriter.h @@ -42,4 +42,6 @@ extern void CheckpointerShmemInit(void); extern bool FirstCallSinceLastCheckpoint(void); +extern bool SendSignalToCheckpointer(int signum); + #endif /* _BGWRITER_H */ diff --git a/src/include/storage/procsignal.h b/src/include/storage/procsignal.h index ee636900f3..2ab644e3e0 100644 --- a/src/include/storage/procsignal.h +++ b/src/include/storage/procsignal.h @@ -49,7 +49,8 @@ typedef enum typedef enum { - PROCSIGNAL_BARRIER_SMGRRELEASE /* ask smgr to close files */ + PROCSIGNAL_BARRIER_SMGRRELEASE, /* ask smgr to close files */ + PROCSIGNAL_BARRIER_WALPROHIBIT } ProcSignalBarrierType; /* diff --git a/src/include/utils/wait_event.h b/src/include/utils/wait_event.h index b578e2ec75..552a5d0e2d 100644 --- a/src/include/utils/wait_event.h +++ b/src/include/utils/wait_event.h @@ -229,7 +229,8 @@ typedef enum WAIT_EVENT_WAL_READ, WAIT_EVENT_WAL_SYNC, WAIT_EVENT_WAL_SYNC_METHOD_ASSIGN, - WAIT_EVENT_WAL_WRITE + WAIT_EVENT_WAL_WRITE, + WAIT_EVENT_WALPROHIBIT_STATE_CHANGE } WaitEventIO; diff --git a/src/test/regress/expected/guc.out b/src/test/regress/expected/guc.out index 3de6404ba5..a9eed063d5 100644 --- a/src/test/regress/expected/guc.out +++ b/src/test/regress/expected/guc.out @@ -896,7 +896,8 @@ SELECT name FROM tab_settings_flags transaction_deferrable transaction_isolation transaction_read_only -(3 rows) + wal_prohibited +(4 rows) -- NO_SHOW_ALL implies NOT_IN_SAMPLE. SELECT name FROM tab_settings_flags diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index be3fafadf8..9e02d0507c 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -2854,6 +2854,7 @@ WALAvailability WALInsertLock WALInsertLockPadded WALOpenSegment +WALProhibitData WALReadError WALSegmentCloseCB WALSegmentContext -- 2.18.0