From d682247c7434efddd42689133f08a4f938defc95 Mon Sep 17 00:00:00 2001 From: Simon Riggs Date: Tue, 17 Feb 2009 21:21:29 +0000 Subject: [PATCH] Add pg_recovery_pause_lsn() --- src/backend/access/transam/xlog.c | 84 +++++++++++++++++++++++++----- src/include/access/xlog_internal.h | 1 + src/include/catalog/pg_proc.h | 2 + 3 files changed, 74 insertions(+), 13 deletions(-) diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c index dae4e34560..079729413f 100644 --- a/src/backend/access/transam/xlog.c +++ b/src/backend/access/transam/xlog.c @@ -158,6 +158,7 @@ static bool recoveryTargetExact = false; static bool recoveryTargetInclusive = true; static TransactionId recoveryTargetXid; static TimestampTz recoveryTargetTime; +static XLogRecPtr recoveryTargetLSN; static int recoveryTargetAdvance = 0; bool InHotStandby = true; @@ -166,10 +167,11 @@ bool InHotStandby = true; #define RECOVERY_TARGET_PAUSE_ALL 1 #define RECOVERY_TARGET_PAUSE_XID 2 #define RECOVERY_TARGET_PAUSE_TIME 3 -#define RECOVERY_TARGET_ADVANCE 4 -#define RECOVERY_TARGET_STOP_IMMEDIATE 5 -#define RECOVERY_TARGET_STOP_XID 6 -#define RECOVERY_TARGET_STOP_TIME 7 +#define RECOVERY_TARGET_PAUSE_LSN 4 +#define RECOVERY_TARGET_ADVANCE 5 +#define RECOVERY_TARGET_STOP_IMMEDIATE 6 +#define RECOVERY_TARGET_STOP_XID 7 +#define RECOVERY_TARGET_STOP_TIME 8 static int recoveryTargetMode = RECOVERY_TARGET_NONE; #define DEFAULT_MAX_STANDBY_DELAY 30 @@ -375,6 +377,7 @@ typedef struct XLogCtlData TransactionId recoveryTargetXid; TimestampTz recoveryTargetTime; int recoveryTargetAdvance; + XLogRecPtr recoveryTargetLSN; TimestampTz recoveryLastXTime; TransactionId recoveryLastXid; @@ -5044,6 +5047,9 @@ recoveryStopsHere(XLogRecord *record, bool *includeThis) /* Decide how to act on any pause target */ switch (recoveryTargetMode) { + case RECOVERY_TARGET_PAUSE_LSN: + return false; + case RECOVERY_TARGET_NONE: /* * If we aren't paused and we're not looking to stop, @@ -5271,12 +5277,33 @@ recoveryStopsHere(XLogRecord *record, bool *includeThis) return stopsHere; } +static void +recoveryPausesAfterLSN(void) +{ + while (recoveryTargetMode == RECOVERY_TARGET_PAUSE_LSN && + XLByteLE(recoveryTargetLSN, LastRec)) + { + { + /* use volatile pointer to prevent code rearrangement */ + volatile XLogCtlData *xlogctl = XLogCtl; + + SpinLockAcquire(&xlogctl->info_lck); + recoveryTargetMode = xlogctl->recoveryTargetMode; + recoveryTargetLSN = xlogctl->recoveryTargetLSN; + SpinLockRelease(&xlogctl->info_lck); + } + + pg_usleep(25000L); + } +} + /* * Utility function used by various user functions to set the recovery * target mode. This allows user control over the progress of recovery. */ static void -SetRecoveryTargetMode(int mode, TransactionId xid, TimestampTz ts, int advance) +SetRecoveryTargetMode(int mode, TransactionId xid, TimestampTz ts, + XLogRecPtr lsn, int advance) { if (!superuser()) ereport(ERROR, @@ -5304,11 +5331,11 @@ SetRecoveryTargetMode(int mode, TransactionId xid, TimestampTz ts, int advance) xlogctl->recoveryTargetTime = ts; else if (mode == RECOVERY_TARGET_ADVANCE) xlogctl->recoveryTargetAdvance = advance; + else if (mode == RECOVERY_TARGET_PAUSE_LSN) + xlogctl->recoveryTargetLSN = lsn; SpinLockRelease(&xlogctl->info_lck); } - - return; } /* @@ -5318,7 +5345,8 @@ SetRecoveryTargetMode(int mode, TransactionId xid, TimestampTz ts, int advance) Datum pg_recovery_continue(PG_FUNCTION_ARGS) { - SetRecoveryTargetMode(RECOVERY_TARGET_NONE, InvalidTransactionId, 0, 0); + SetRecoveryTargetMode(RECOVERY_TARGET_NONE, + InvalidTransactionId, 0, InvalidXLogRecPtr, 0); PG_RETURN_VOID(); } @@ -5330,7 +5358,8 @@ pg_recovery_continue(PG_FUNCTION_ARGS) Datum pg_recovery_pause(PG_FUNCTION_ARGS) { - SetRecoveryTargetMode(RECOVERY_TARGET_PAUSE_ALL, InvalidTransactionId, 0, 0); + SetRecoveryTargetMode(RECOVERY_TARGET_PAUSE_ALL, + InvalidTransactionId, 0, InvalidXLogRecPtr, 0); PG_RETURN_VOID(); } @@ -5348,7 +5377,8 @@ pg_recovery_pause_xid(PG_FUNCTION_ARGS) if (xid < 3) elog(ERROR, "cannot specify special values for transaction id"); - SetRecoveryTargetMode(RECOVERY_TARGET_PAUSE_XID, xid, 0, 0); + SetRecoveryTargetMode(RECOVERY_TARGET_PAUSE_XID, + xid, 0, InvalidXLogRecPtr, 0); PG_RETURN_VOID(); } @@ -5362,7 +5392,26 @@ pg_recovery_pause_time(PG_FUNCTION_ARGS) { TimestampTz ts = PG_GETARG_TIMESTAMPTZ(0); - SetRecoveryTargetMode(RECOVERY_TARGET_PAUSE_TIME, InvalidTransactionId, ts, 0); + SetRecoveryTargetMode(RECOVERY_TARGET_PAUSE_TIME, + InvalidTransactionId, ts, InvalidXLogRecPtr, 0); + + PG_RETURN_VOID(); +} + +/* + * Pause recovery after stated LSN, if ever reached. Once paused, stays paused + * until asked to play again. + */ +Datum +pg_recovery_pause_lsn(PG_FUNCTION_ARGS) +{ + XLogRecPtr lsn; + + lsn.xlogid = PG_GETARG_INT32(0); + lsn.xrecoff = PG_GETARG_INT32(1); + + SetRecoveryTargetMode(RECOVERY_TARGET_PAUSE_LSN, + InvalidTransactionId, 0, lsn, 0); PG_RETURN_VOID(); } @@ -5378,7 +5427,8 @@ pg_recovery_advance(PG_FUNCTION_ARGS) if (adv < 1) elog(ERROR, "recovery advance must be greater than or equal to 1"); - SetRecoveryTargetMode(RECOVERY_TARGET_ADVANCE, InvalidTransactionId, 0, adv); + SetRecoveryTargetMode(RECOVERY_TARGET_ADVANCE, + InvalidTransactionId, 0, InvalidXLogRecPtr, adv); PG_RETURN_VOID(); } @@ -5389,7 +5439,8 @@ pg_recovery_advance(PG_FUNCTION_ARGS) Datum pg_recovery_stop(PG_FUNCTION_ARGS) { - SetRecoveryTargetMode(RECOVERY_TARGET_STOP_IMMEDIATE, InvalidTransactionId, 0, 0); + SetRecoveryTargetMode(RECOVERY_TARGET_STOP_IMMEDIATE, + InvalidTransactionId, 0, InvalidXLogRecPtr, 0); PG_RETURN_VOID(); } @@ -5433,6 +5484,11 @@ pg_current_recovery_target(PG_FUNCTION_ARGS) appendStringInfo(&buf, "Recovery will pause after transaction completion timestamp %s", timestamptz_to_str(recoveryTargetTime)); break; + case RECOVERY_TARGET_PAUSE_LSN: + appendStringInfo(&buf, "Recovery will pause after applying record at xlog location %X/%X", + recoveryTargetLSN.xlogid, + recoveryTargetLSN.xrecoff); + break; case RECOVERY_TARGET_ADVANCE: appendStringInfo(&buf, "Recovery will advance"); break; @@ -5994,6 +6050,8 @@ StartupXLOG(void) LastRec = ReadRecPtr; + recoveryPausesAfterLSN(); + record = ReadRecord(NULL, LOG); } while (record != NULL && recoveryContinue); diff --git a/src/include/access/xlog_internal.h b/src/include/access/xlog_internal.h index cc7f2530e6..f6b1ca557f 100644 --- a/src/include/access/xlog_internal.h +++ b/src/include/access/xlog_internal.h @@ -260,6 +260,7 @@ extern Datum pg_recovery_pause(PG_FUNCTION_ARGS); extern Datum pg_recovery_pause_cleanup(PG_FUNCTION_ARGS); extern Datum pg_recovery_pause_xid(PG_FUNCTION_ARGS); extern Datum pg_recovery_pause_time(PG_FUNCTION_ARGS); +extern Datum pg_recovery_pause_lsn(PG_FUNCTION_ARGS); extern Datum pg_recovery_advance(PG_FUNCTION_ARGS); extern Datum pg_recovery_stop(PG_FUNCTION_ARGS); extern Datum pg_current_recovery_target(PG_FUNCTION_ARGS); diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h index 1602f2b07b..c07989b97b 100644 --- a/src/include/catalog/pg_proc.h +++ b/src/include/catalog/pg_proc.h @@ -3282,6 +3282,8 @@ DATA(insert OID = 3807 ( pg_recovery_stop PGNSP PGUID 12 1 0 0 f f f t f v 0 0 DESCR("stop recovery immediately"); DATA(insert OID = 3808 ( pg_current_recovery_target PGNSP PGUID 12 1 0 0 f f f t f v 0 0 25 "" _null_ _null_ _null_ _null_ pg_current_recovery_target _null_ _null_ _null_ )); DESCR("get current recovery target state and target values, if any"); +DATA(insert OID = 3809 ( pg_recovery_pause_lsn PGNSP PGUID 12 1 0 0 f f f t f v 2 0 2278 "23 23" _null_ _null_ _null_ _null_ pg_recovery_pause_lsn _null_ _null_ _null_ )); +DESCR("continue recovery until a transaction with specified timestamp completes, if ever seen, then pause recovery"); DATA(insert OID = 3810 ( pg_is_in_recovery PGNSP PGUID 12 1 0 0 f f f t f v 0 0 16 "" _null_ _null_ _null_ _null_ pg_is_in_recovery _null_ _null_ _null_ )); DESCR("true if server is in recovery"); -- 2.39.5