From: Simon Riggs Date: Tue, 8 Feb 2011 18:30:22 +0000 (+0000) Subject: Basic Recovery Control functions for use in Hot Standby. Pause, Resume, X-Git-Url: http://git.osdn.net/view?a=commitdiff_plain;h=8c6e3adbf792c2bba448e88cbf2c8e03fb802e73;p=pg-rex%2Fsyncrep.git Basic Recovery Control functions for use in Hot Standby. Pause, Resume, Status check functions only. Also, new recovery.conf parameter to pause_at_recovery_target, default on. Simon Riggs, reviewed by Fujii Masao --- diff --git a/doc/src/sgml/func.sgml b/doc/src/sgml/func.sgml index 2bbada0d06..e26e7614c9 100644 --- a/doc/src/sgml/func.sgml +++ b/doc/src/sgml/func.sgml @@ -14174,6 +14174,64 @@ postgres=# SELECT * FROM pg_xlogfile_name_offset(pg_stop_backup()); + The functions shown in control the progress of recovery. + These functions may be executed only during recovery. + + + + Recovery Control Functions + + + Name Return Type Description + + + + + + + pg_is_xlog_replay_paused() + + bool + True if recovery is paused. + + + + + pg_xlog_replay_pause() + + void + Pauses recovery immediately. + + + + + pg_xlog_replay_resume() + + void + Restarts recovery if it was paused. + + + + +
+ + + While recovery is paused no further database changes are applied. + If in hot standby, all new queries will see the same consistent snapshot + of the database, and no further query conflicts will be generated until + recovery is resumed. + + + + If streaming replication is disabled, the paused state may continue + indefinitely without problem. While streaming replication is in + progress WAL records will continue to be received, which will + eventually fill available disk space, depending upon the duration of + the pause, the rate of WAL generation and available disk space. + + + The functions shown in calculate the disk space usage of database objects. diff --git a/doc/src/sgml/recovery-config.sgml b/doc/src/sgml/recovery-config.sgml index 454ec84f1a..190e589032 100644 --- a/doc/src/sgml/recovery-config.sgml +++ b/doc/src/sgml/recovery-config.sgml @@ -227,6 +227,31 @@ restore_command = 'copy "C:\\server\\archivedir\\%f" "%p"' # Windows + + pause_at_recovery_target + (boolean) + + + pause_at_recovery_target recovery parameter + + + + Specifies whether recovery should pause when the recovery target + is reached. The default is true, if a recovery target is set. + This is intended to allow queries to be executed against the + database to check if this recovery target is the most desirable + point for recovery. The paused state can be resumed by using + pg_xlog_replay_resume() (See + ), which then + causes recovery to end. If this recovery target is not the + desired stopping point, then shutdown the server, change the + recovery target settings to a later target and restart to + continue recovery. + + + + diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c index 1a3eed212f..5ba3e26f81 100644 --- a/src/backend/access/transam/xlog.c +++ b/src/backend/access/transam/xlog.c @@ -182,6 +182,7 @@ static char *recoveryEndCommand = NULL; static char *archiveCleanupCommand = NULL; static RecoveryTargetType recoveryTarget = RECOVERY_TARGET_UNSET; static bool recoveryTargetInclusive = true; +static bool recoveryPauseAtTarget = true; static TransactionId recoveryTargetXid; static TimestampTz recoveryTargetTime; @@ -423,6 +424,8 @@ typedef struct XLogCtlData XLogRecPtr recoveryLastRecPtr; /* timestamp of last COMMIT/ABORT record replayed (or being replayed) */ TimestampTz recoveryLastXTime; + /* Are we requested to pause recovery? */ + bool recoveryPause; slock_t info_lck; /* locks shared variables shown above */ } XLogCtlData; @@ -570,6 +573,9 @@ static void readRecoveryCommandFile(void); static void exitArchiveRecovery(TimeLineID endTLI, uint32 endLogId, uint32 endLogSeg); static bool recoveryStopsHere(XLogRecord *record, bool *includeThis); +static void recoveryPausesHere(void); +static bool RecoveryIsPaused(void); +static void SetRecoveryPause(bool recoveryPause); static void SetLatestXTime(TimestampTz xtime); static TimestampTz GetLatestXTime(void); static void CheckRequiredParameterValues(void); @@ -5126,6 +5132,15 @@ readRecoveryCommandFile(void) (errmsg("archive_cleanup_command = '%s'", archiveCleanupCommand))); } + else if (strcmp(item->name, "pause_at_recovery_target") == 0) + { + if (!parse_bool(item->value, &recoveryPauseAtTarget)) + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("parameter \"%s\" requires a Boolean value", "pause_at_recovery_target"))); + ereport(DEBUG2, + (errmsg("pause_at_recovery_target = '%s'", item->value))); + } else if (strcmp(item->name, "recovery_target_timeline") == 0) { rtliGiven = true; @@ -5509,6 +5524,110 @@ recoveryStopsHere(XLogRecord *record, bool *includeThis) } /* + * Recheck shared recoveryPause by polling. + * + * XXX Can also be done with shared latch. + */ +static void +recoveryPausesHere(void) +{ + while (RecoveryIsPaused()); + { + pg_usleep(1000000L); /* 1000 ms */ + HandleStartupProcInterrupts(); + }; +} + +static bool +RecoveryIsPaused(void) +{ + /* use volatile pointer to prevent code rearrangement */ + volatile XLogCtlData *xlogctl = XLogCtl; + bool recoveryPause; + + SpinLockAcquire(&xlogctl->info_lck); + recoveryPause = xlogctl->recoveryPause; + SpinLockRelease(&xlogctl->info_lck); + + return recoveryPause; +} + +static void +SetRecoveryPause(bool recoveryPause) +{ + /* use volatile pointer to prevent code rearrangement */ + volatile XLogCtlData *xlogctl = XLogCtl; + + SpinLockAcquire(&xlogctl->info_lck); + xlogctl->recoveryPause = recoveryPause; + SpinLockRelease(&xlogctl->info_lck); +} + +/* + * pg_xlog_replay_pause - pause recovery now + */ +Datum +pg_xlog_replay_pause(PG_FUNCTION_ARGS) +{ + if (!superuser()) + ereport(ERROR, + (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), + (errmsg("must be superuser to control recovery")))); + + 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."))); + + SetRecoveryPause(true); + + PG_RETURN_VOID(); +} + +/* + * pg_xlog_replay_resume - resume recovery now + */ +Datum +pg_xlog_replay_resume(PG_FUNCTION_ARGS) +{ + if (!superuser()) + ereport(ERROR, + (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), + (errmsg("must be superuser to control recovery")))); + + 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."))); + + SetRecoveryPause(false); + + PG_RETURN_VOID(); +} + +/* + * pg_is_xlog_replay_paused + */ +Datum +pg_is_xlog_replay_paused(PG_FUNCTION_ARGS) +{ + if (!superuser()) + ereport(ERROR, + (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), + (errmsg("must be superuser to control recovery")))); + + 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(RecoveryIsPaused()); +} + +/* * Save timestamp of latest processed commit/abort record. * * We keep this in XLogCtl, not a simple static variable, so that it can be @@ -6074,6 +6193,13 @@ StartupXLOG(void) StandbyRecoverPreparedTransactions(false); } } + else + { + /* + * Must not pause unless we are going to enter Hot Standby. + */ + recoveryPauseAtTarget = false; + } /* Initialize resource managers */ for (rmid = 0; rmid <= RM_MAX_ID; rmid++) @@ -6098,6 +6224,7 @@ StartupXLOG(void) xlogctl->replayEndRecPtr = ReadRecPtr; xlogctl->recoveryLastRecPtr = ReadRecPtr; xlogctl->recoveryLastXTime = 0; + xlogctl->recoveryPause = false; SpinLockRelease(&xlogctl->info_lck); /* Also ensure XLogReceiptTime has a sane value */ @@ -6146,6 +6273,7 @@ StartupXLOG(void) { bool recoveryContinue = true; bool recoveryApply = true; + bool recoveryPause = false; ErrorContextCallback errcontext; TimestampTz xtime; @@ -6192,6 +6320,11 @@ StartupXLOG(void) */ if (recoveryStopsHere(record, &recoveryApply)) { + if (recoveryPauseAtTarget) + { + SetRecoveryPause(true); + recoveryPausesHere(); + } reachedStopPoint = true; /* see below */ recoveryContinue = false; if (!recoveryApply) @@ -6218,8 +6351,12 @@ StartupXLOG(void) */ SpinLockAcquire(&xlogctl->info_lck); xlogctl->replayEndRecPtr = EndRecPtr; + recoveryPause = xlogctl->recoveryPause; SpinLockRelease(&xlogctl->info_lck); + if (recoveryPause) + recoveryPausesHere(); + /* * If we are attempting to enter Hot Standby mode, process * XIDs we see diff --git a/src/include/access/xlog_internal.h b/src/include/access/xlog_internal.h index ba640359cc..6390113de3 100644 --- a/src/include/access/xlog_internal.h +++ b/src/include/access/xlog_internal.h @@ -275,5 +275,8 @@ extern Datum pg_last_xact_replay_timestamp(PG_FUNCTION_ARGS); extern Datum pg_xlogfile_name_offset(PG_FUNCTION_ARGS); extern Datum pg_xlogfile_name(PG_FUNCTION_ARGS); extern Datum pg_is_in_recovery(PG_FUNCTION_ARGS); +extern Datum pg_xlog_replay_pause(PG_FUNCTION_ARGS); +extern Datum pg_xlog_replay_resume(PG_FUNCTION_ARGS); +extern Datum pg_is_xlog_replay_paused(PG_FUNCTION_ARGS); #endif /* XLOG_INTERNAL_H */ diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h index f8b5d4da3d..734f43a1e4 100644 --- a/src/include/catalog/pg_proc.h +++ b/src/include/catalog/pg_proc.h @@ -3416,6 +3416,13 @@ DESCR("last xlog replay location"); DATA(insert OID = 3830 ( pg_last_xact_replay_timestamp PGNSP PGUID 12 1 0 0 f f f t f v 0 0 1184 "" _null_ _null_ _null_ _null_ pg_last_xact_replay_timestamp _null_ _null_ _null_ )); DESCR("timestamp of last replay xact"); +DATA(insert OID = 3071 ( pg_xlog_replay_pause PGNSP PGUID 12 1 0 0 f f f t f v 0 0 2278 "" _null_ _null_ _null_ _null_ pg_xlog_replay_pause _null_ _null_ _null_ )); +DESCR("pauses xlog replay"); +DATA(insert OID = 3072 ( pg_xlog_replay_resume PGNSP PGUID 12 1 0 0 f f f t f v 0 0 2278 "" _null_ _null_ _null_ _null_ pg_xlog_replay_resume _null_ _null_ _null_ )); +DESCR("resumes xlog replay, if it was paused"); +DATA(insert OID = 3073 ( pg_is_xlog_replay_paused PGNSP PGUID 12 1 0 0 f f f t f v 0 0 16 "" _null_ _null_ _null_ _null_ pg_is_xlog_replay_paused _null_ _null_ _null_ )); +DESCR("true if xlog replay is paused"); + DATA(insert OID = 2621 ( pg_reload_conf PGNSP PGUID 12 1 0 0 f f f t f v 0 0 16 "" _null_ _null_ _null_ _null_ pg_reload_conf _null_ _null_ _null_ )); DESCR("reload configuration files"); DATA(insert OID = 2622 ( pg_rotate_logfile PGNSP PGUID 12 1 0 0 f f f t f v 0 0 16 "" _null_ _null_ _null_ _null_ pg_rotate_logfile _null_ _null_ _null_ ));