@@ -182,6 +182,7 @@ static char *recoveryEndCommand = NULL;
182182static char * archiveCleanupCommand = NULL ;
183183static RecoveryTargetType recoveryTarget = RECOVERY_TARGET_UNSET ;
184184static bool recoveryTargetInclusive = true;
185+ static bool recoveryPauseAtTarget = true;
185186static TransactionId recoveryTargetXid ;
186187static TimestampTz recoveryTargetTime ;
187188
@@ -423,6 +424,8 @@ typedef struct XLogCtlData
423424 XLogRecPtr recoveryLastRecPtr ;
424425 /* timestamp of last COMMIT/ABORT record replayed (or being replayed) */
425426 TimestampTz recoveryLastXTime ;
427+ /* Are we requested to pause recovery? */
428+ bool recoveryPause ;
426429
427430 slock_t info_lck ; /* locks shared variables shown above */
428431} XLogCtlData ;
@@ -570,6 +573,9 @@ static void readRecoveryCommandFile(void);
570573static void exitArchiveRecovery (TimeLineID endTLI ,
571574 uint32 endLogId , uint32 endLogSeg );
572575static bool recoveryStopsHere (XLogRecord * record , bool * includeThis );
576+ static void recoveryPausesHere (void );
577+ static bool RecoveryIsPaused (void );
578+ static void SetRecoveryPause (bool recoveryPause );
573579static void SetLatestXTime (TimestampTz xtime );
574580static TimestampTz GetLatestXTime (void );
575581static void CheckRequiredParameterValues (void );
@@ -5126,6 +5132,15 @@ readRecoveryCommandFile(void)
51265132 (errmsg ("archive_cleanup_command = '%s'" ,
51275133 archiveCleanupCommand )));
51285134 }
5135+ else if (strcmp (item -> name , "pause_at_recovery_target" ) == 0 )
5136+ {
5137+ if (!parse_bool (item -> value , & recoveryPauseAtTarget ))
5138+ ereport (ERROR ,
5139+ (errcode (ERRCODE_INVALID_PARAMETER_VALUE ),
5140+ errmsg ("parameter \"%s\" requires a Boolean value" , "pause_at_recovery_target" )));
5141+ ereport (DEBUG2 ,
5142+ (errmsg ("pause_at_recovery_target = '%s'" , item -> value )));
5143+ }
51295144 else if (strcmp (item -> name , "recovery_target_timeline" ) == 0 )
51305145 {
51315146 rtliGiven = true;
@@ -5508,6 +5523,110 @@ recoveryStopsHere(XLogRecord *record, bool *includeThis)
55085523 return stopsHere ;
55095524}
55105525
5526+ /*
5527+ * Recheck shared recoveryPause by polling.
5528+ *
5529+ * XXX Can also be done with shared latch.
5530+ */
5531+ static void
5532+ recoveryPausesHere (void )
5533+ {
5534+ while (RecoveryIsPaused ());
5535+ {
5536+ pg_usleep (1000000L ); /* 1000 ms */
5537+ HandleStartupProcInterrupts ();
5538+ };
5539+ }
5540+
5541+ static bool
5542+ RecoveryIsPaused (void )
5543+ {
5544+ /* use volatile pointer to prevent code rearrangement */
5545+ volatile XLogCtlData * xlogctl = XLogCtl ;
5546+ bool recoveryPause ;
5547+
5548+ SpinLockAcquire (& xlogctl -> info_lck );
5549+ recoveryPause = xlogctl -> recoveryPause ;
5550+ SpinLockRelease (& xlogctl -> info_lck );
5551+
5552+ return recoveryPause ;
5553+ }
5554+
5555+ static void
5556+ SetRecoveryPause (bool recoveryPause )
5557+ {
5558+ /* use volatile pointer to prevent code rearrangement */
5559+ volatile XLogCtlData * xlogctl = XLogCtl ;
5560+
5561+ SpinLockAcquire (& xlogctl -> info_lck );
5562+ xlogctl -> recoveryPause = recoveryPause ;
5563+ SpinLockRelease (& xlogctl -> info_lck );
5564+ }
5565+
5566+ /*
5567+ * pg_xlog_replay_pause - pause recovery now
5568+ */
5569+ Datum
5570+ pg_xlog_replay_pause (PG_FUNCTION_ARGS )
5571+ {
5572+ if (!superuser ())
5573+ ereport (ERROR ,
5574+ (errcode (ERRCODE_INSUFFICIENT_PRIVILEGE ),
5575+ (errmsg ("must be superuser to control recovery" ))));
5576+
5577+ if (!RecoveryInProgress ())
5578+ ereport (ERROR ,
5579+ (errcode (ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE ),
5580+ errmsg ("recovery is not in progress" ),
5581+ errhint ("Recovery control functions can only be executed during recovery." )));
5582+
5583+ SetRecoveryPause (true);
5584+
5585+ PG_RETURN_VOID ();
5586+ }
5587+
5588+ /*
5589+ * pg_xlog_replay_resume - resume recovery now
5590+ */
5591+ Datum
5592+ pg_xlog_replay_resume (PG_FUNCTION_ARGS )
5593+ {
5594+ if (!superuser ())
5595+ ereport (ERROR ,
5596+ (errcode (ERRCODE_INSUFFICIENT_PRIVILEGE ),
5597+ (errmsg ("must be superuser to control recovery" ))));
5598+
5599+ if (!RecoveryInProgress ())
5600+ ereport (ERROR ,
5601+ (errcode (ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE ),
5602+ errmsg ("recovery is not in progress" ),
5603+ errhint ("Recovery control functions can only be executed during recovery." )));
5604+
5605+ SetRecoveryPause (false);
5606+
5607+ PG_RETURN_VOID ();
5608+ }
5609+
5610+ /*
5611+ * pg_is_xlog_replay_paused
5612+ */
5613+ Datum
5614+ pg_is_xlog_replay_paused (PG_FUNCTION_ARGS )
5615+ {
5616+ if (!superuser ())
5617+ ereport (ERROR ,
5618+ (errcode (ERRCODE_INSUFFICIENT_PRIVILEGE ),
5619+ (errmsg ("must be superuser to control recovery" ))));
5620+
5621+ if (!RecoveryInProgress ())
5622+ ereport (ERROR ,
5623+ (errcode (ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE ),
5624+ errmsg ("recovery is not in progress" ),
5625+ errhint ("Recovery control functions can only be executed during recovery." )));
5626+
5627+ PG_RETURN_BOOL (RecoveryIsPaused ());
5628+ }
5629+
55115630/*
55125631 * Save timestamp of latest processed commit/abort record.
55135632 *
@@ -6074,6 +6193,13 @@ StartupXLOG(void)
60746193 StandbyRecoverPreparedTransactions (false);
60756194 }
60766195 }
6196+ else
6197+ {
6198+ /*
6199+ * Must not pause unless we are going to enter Hot Standby.
6200+ */
6201+ recoveryPauseAtTarget = false;
6202+ }
60776203
60786204 /* Initialize resource managers */
60796205 for (rmid = 0 ; rmid <= RM_MAX_ID ; rmid ++ )
@@ -6098,6 +6224,7 @@ StartupXLOG(void)
60986224 xlogctl -> replayEndRecPtr = ReadRecPtr ;
60996225 xlogctl -> recoveryLastRecPtr = ReadRecPtr ;
61006226 xlogctl -> recoveryLastXTime = 0 ;
6227+ xlogctl -> recoveryPause = false;
61016228 SpinLockRelease (& xlogctl -> info_lck );
61026229
61036230 /* Also ensure XLogReceiptTime has a sane value */
@@ -6146,6 +6273,7 @@ StartupXLOG(void)
61466273 {
61476274 bool recoveryContinue = true;
61486275 bool recoveryApply = true;
6276+ bool recoveryPause = false;
61496277 ErrorContextCallback errcontext ;
61506278 TimestampTz xtime ;
61516279
@@ -6192,6 +6320,11 @@ StartupXLOG(void)
61926320 */
61936321 if (recoveryStopsHere (record , & recoveryApply ))
61946322 {
6323+ if (recoveryPauseAtTarget )
6324+ {
6325+ SetRecoveryPause (true);
6326+ recoveryPausesHere ();
6327+ }
61956328 reachedStopPoint = true; /* see below */
61966329 recoveryContinue = false;
61976330 if (!recoveryApply )
@@ -6218,8 +6351,12 @@ StartupXLOG(void)
62186351 */
62196352 SpinLockAcquire (& xlogctl -> info_lck );
62206353 xlogctl -> replayEndRecPtr = EndRecPtr ;
6354+ recoveryPause = xlogctl -> recoveryPause ;
62216355 SpinLockRelease (& xlogctl -> info_lck );
62226356
6357+ if (recoveryPause )
6358+ recoveryPausesHere ();
6359+
62236360 /*
62246361 * If we are attempting to enter Hot Standby mode, process
62256362 * XIDs we see
0 commit comments