From 37b2889d96025b60976c1495bb02de7f0b9497b4 Mon Sep 17 00:00:00 2001 From: Dilip Kumar Date: Thu, 10 Dec 2020 11:22:08 +0530 Subject: [PATCH v3] pg_is_wal_replay_paused will wait for recovery to pause Currently, pg_is_wal_replay_paused, just check whether the recovery pause is requested or not but it doesn't actually wait for recovery to get paused. With this patch if recovery pause is not requested the api will return false otherwise it will wait for recovery to get paused. --- doc/src/sgml/func.sgml | 13 ++++--- src/backend/access/transam/xlog.c | 48 ++++++++++++++++++++++---- src/backend/access/transam/xlogfuncs.c | 41 +++++++++++++++++++--- src/include/access/xlog.h | 11 +++++- 4 files changed, 97 insertions(+), 16 deletions(-) diff --git a/doc/src/sgml/func.sgml b/doc/src/sgml/func.sgml index fd0370a1b4..c51a1d8a15 100644 --- a/doc/src/sgml/func.sgml +++ b/doc/src/sgml/func.sgml @@ -25180,7 +25180,10 @@ postgres=# SELECT * FROM pg_walfile_name_offset(pg_stop_backup()); boolean - Returns true if recovery is paused. + If replay pause is requested using pg_wal_replay_pause + then this will wait for recovery to actually get paused and once + recovery is paused it will return true. Return false if replay pause + is not requested. @@ -25219,10 +25222,10 @@ postgres=# SELECT * FROM pg_walfile_name_offset(pg_stop_backup()); void - Pauses recovery. While recovery is paused, no further database - changes are applied. If hot standby is active, all new queries will - see the same consistent snapshot of the database, and no further query - conflicts will be generated until recovery is resumed. + Request to pause recovery. While recovery is paused, no further + database changes are applied. If hot standby is active, all new queries + will see the same consistent snapshot of the database, and no further + query conflicts will be generated until recovery is resumed. This function is restricted to superusers by default, but other users diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c index b18257c198..60809d1bf6 100644 --- a/src/backend/access/transam/xlog.c +++ b/src/backend/access/transam/xlog.c @@ -725,8 +725,8 @@ typedef struct XLogCtlData * only relevant for replication or archive recovery */ TimestampTz currentChunkStartTime; - /* Are we requested to pause recovery? */ - bool recoveryPause; + /* Recovery pause state */ + RecoveryPauseState recoveryPause; /* * lastFpwDisableRecPtr points to the start of the last replayed @@ -6021,6 +6021,16 @@ recoveryStopsAfter(XLogReaderState *record) return false; } +static void +CheckAndSetRecoveryPause(void) +{ + /* If recovery pause is requested then set it paused */ + SpinLockAcquire(&XLogCtl->info_lck); + if (XLogCtl->recoveryPause == RECOVERY_PAUSE_REQUESTED) + XLogCtl->recoveryPause = RECOVERY_PAUSED; + SpinLockRelease(&XLogCtl->info_lck); +} + /* * Wait until shared recoveryPause flag is cleared. * @@ -6052,12 +6062,20 @@ recoveryPausesHere(bool endOfRecovery) (errmsg("recovery has paused"), errhint("Execute pg_wal_replay_resume() to continue."))); - while (RecoveryIsPaused()) + while (RecoveryPauseRequested()) { + HandleStartupProcInterrupts(); + if (CheckForStandbyTrigger()) return; pgstat_report_wait_start(WAIT_EVENT_RECOVERY_PAUSE); + + /* + * While we are in the loop, user might resume and pause again so set + * this everytime. + */ + CheckAndSetRecoveryPause(); pg_usleep(1000000L); /* 1000 ms */ pgstat_report_wait_end(); } @@ -6065,18 +6083,30 @@ recoveryPausesHere(bool endOfRecovery) bool RecoveryIsPaused(void) +{ + bool recoveryPause; + + SpinLockAcquire(&XLogCtl->info_lck); + recoveryPause = (XLogCtl->recoveryPause == RECOVERY_PAUSED) ? true : false; + SpinLockRelease(&XLogCtl->info_lck); + + return recoveryPause; +} + +bool +RecoveryPauseRequested(void) { bool recoveryPause; SpinLockAcquire(&XLogCtl->info_lck); - recoveryPause = XLogCtl->recoveryPause; + recoveryPause = (XLogCtl->recoveryPause != RECOVERY_IN_PROGRESS) ? true : false; SpinLockRelease(&XLogCtl->info_lck); return recoveryPause; } void -SetRecoveryPause(bool recoveryPause) +SetRecoveryPause(RecoveryPauseState recoveryPause) { SpinLockAcquire(&XLogCtl->info_lck); XLogCtl->recoveryPause = recoveryPause; @@ -7445,7 +7475,7 @@ StartupXLOG(void) proc_exit(3); case RECOVERY_TARGET_ACTION_PAUSE: - SetRecoveryPause(true); + SetRecoveryPause(RECOVERY_PAUSE_REQUESTED); recoveryPausesHere(true); /* drop into promote */ @@ -12598,6 +12628,12 @@ WaitForWALToBecomeAvailable(XLogRecPtr RecPtr, bool randAccess, elog(ERROR, "unexpected WAL source %d", currentSource); } + /* test for recovery pause if user has requested the pause */ + if (((volatile XLogCtlData *) XLogCtl)->recoveryPause) + recoveryPausesHere(false); + + now = GetCurrentTimestamp(); + /* * This possibly-long loop needs to handle interrupts of startup * process. diff --git a/src/backend/access/transam/xlogfuncs.c b/src/backend/access/transam/xlogfuncs.c index 5e1aab319d..4c448a97a0 100644 --- a/src/backend/access/transam/xlogfuncs.c +++ b/src/backend/access/transam/xlogfuncs.c @@ -517,7 +517,7 @@ pg_walfile_name(PG_FUNCTION_ARGS) } /* - * pg_wal_replay_pause - pause recovery now + * pg_wal_replay_pause - Request to pause recovery * * Permission checking for this function is managed through the normal * GRANT system. @@ -538,7 +538,10 @@ pg_wal_replay_pause(PG_FUNCTION_ARGS) errhint("%s cannot be executed after promotion is triggered.", "pg_wal_replay_pause()"))); - SetRecoveryPause(true); + SetRecoveryPause(RECOVERY_PAUSE_REQUESTED); + + /* wake up the recovery process so that it can process the pause request */ + WakeupRecovery(); PG_RETURN_VOID(); } @@ -565,13 +568,17 @@ pg_wal_replay_resume(PG_FUNCTION_ARGS) errhint("%s cannot be executed after promotion is triggered.", "pg_wal_replay_resume()"))); - SetRecoveryPause(false); + SetRecoveryPause(RECOVERY_IN_PROGRESS); PG_RETURN_VOID(); } /* * pg_is_wal_replay_paused + * + * If replay pause is requested using pg_wal_replay_pause then this will wait + * for recovery to actually get paused and once recovery is paused it will + * return true. Return false if replay pause is not requested. */ Datum pg_is_wal_replay_paused(PG_FUNCTION_ARGS) @@ -582,7 +589,33 @@ pg_is_wal_replay_paused(PG_FUNCTION_ARGS) errmsg("recovery is not in progress"), errhint("Recovery control functions can only be executed during recovery."))); - PG_RETURN_BOOL(RecoveryIsPaused()); + if (!RecoveryPauseRequested()) + PG_RETURN_BOOL(false); + + /* loop until the recovery is actually paused */ + while(!RecoveryIsPaused()) + { + pg_usleep(10000L); /* wait for 10 msec */ + + /* meanwhile if recovery is resume requested then return false */ + if (!RecoveryPauseRequested()) + PG_RETURN_BOOL(false); + + CHECK_FOR_INTERRUPTS(); + + /* + * If recovery is not in progress anymore then report an error this + * could happen if the standby is promoted while we were waiting for + * recovery to get paused. + */ + if (!RecoveryInProgress()) + ereport(ERROR, + (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE), + errmsg("recovery is not in progress"), + errhint("Recovery control functions can only be executed during recovery."))); + } + + PG_RETURN_BOOL(true); } /* diff --git a/src/include/access/xlog.h b/src/include/access/xlog.h index 75ec1073bd..d00df1bf76 100644 --- a/src/include/access/xlog.h +++ b/src/include/access/xlog.h @@ -174,6 +174,14 @@ typedef enum RecoveryState RECOVERY_STATE_DONE /* currently in production */ } RecoveryState; +/* Recovery pause states */ +typedef enum RecoveryPauseState +{ + RECOVERY_IN_PROGRESS = 0, + RECOVERY_PAUSE_REQUESTED, + RECOVERY_PAUSED, +} RecoveryPauseState; + extern PGDLLIMPORT int wal_level; /* Is WAL archiving enabled (always or only while server is running normally)? */ @@ -311,7 +319,8 @@ extern XLogRecPtr GetXLogReplayRecPtr(TimeLineID *replayTLI); extern XLogRecPtr GetXLogInsertRecPtr(void); extern XLogRecPtr GetXLogWriteRecPtr(void); extern bool RecoveryIsPaused(void); -extern void SetRecoveryPause(bool recoveryPause); +extern bool RecoveryPauseRequested(void); +extern void SetRecoveryPause(RecoveryPauseState recoveryPause); extern TimestampTz GetLatestXTime(void); extern TimestampTz GetCurrentChunkReplayStartTime(void); -- 2.23.0