mirror of
https://github.com/postgres/postgres.git
synced 2025-07-26 01:22:12 +03:00
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
This commit is contained in:
@ -14173,6 +14173,64 @@ postgres=# SELECT * FROM pg_xlogfile_name_offset(pg_stop_backup());
|
|||||||
</tgroup>
|
</tgroup>
|
||||||
</table>
|
</table>
|
||||||
|
|
||||||
|
<para>
|
||||||
|
The functions shown in <xref
|
||||||
|
linkend="functions-recovery-control-table"> control the progress of recovery.
|
||||||
|
These functions may be executed only during recovery.
|
||||||
|
</para>
|
||||||
|
|
||||||
|
<table id="functions-recovery-control-table">
|
||||||
|
<title>Recovery Control Functions</title>
|
||||||
|
<tgroup cols="3">
|
||||||
|
<thead>
|
||||||
|
<row><entry>Name</entry> <entry>Return Type</entry> <entry>Description</entry>
|
||||||
|
</row>
|
||||||
|
</thead>
|
||||||
|
|
||||||
|
<tbody>
|
||||||
|
<row>
|
||||||
|
<entry>
|
||||||
|
<literal><function>pg_is_xlog_replay_paused()</function></literal>
|
||||||
|
</entry>
|
||||||
|
<entry><type>bool</type></entry>
|
||||||
|
<entry>True if recovery is paused.
|
||||||
|
</entry>
|
||||||
|
</row>
|
||||||
|
<row>
|
||||||
|
<entry>
|
||||||
|
<literal><function>pg_xlog_replay_pause()</function></literal>
|
||||||
|
</entry>
|
||||||
|
<entry><type>void</type></entry>
|
||||||
|
<entry>Pauses recovery immediately.
|
||||||
|
</entry>
|
||||||
|
</row>
|
||||||
|
<row>
|
||||||
|
<entry>
|
||||||
|
<literal><function>pg_xlog_replay_resume()</function></literal>
|
||||||
|
</entry>
|
||||||
|
<entry><type>void</type></entry>
|
||||||
|
<entry>Restarts recovery if it was paused.
|
||||||
|
</entry>
|
||||||
|
</row>
|
||||||
|
</tbody>
|
||||||
|
</tgroup>
|
||||||
|
</table>
|
||||||
|
|
||||||
|
<para>
|
||||||
|
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.
|
||||||
|
</para>
|
||||||
|
|
||||||
|
<para>
|
||||||
|
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.
|
||||||
|
</para>
|
||||||
|
|
||||||
<para>
|
<para>
|
||||||
The functions shown in <xref linkend="functions-admin-dbsize"> calculate
|
The functions shown in <xref linkend="functions-admin-dbsize"> calculate
|
||||||
the disk space usage of database objects.
|
the disk space usage of database objects.
|
||||||
|
@ -227,6 +227,31 @@ restore_command = 'copy "C:\\server\\archivedir\\%f" "%p"' # Windows
|
|||||||
</listitem>
|
</listitem>
|
||||||
</varlistentry>
|
</varlistentry>
|
||||||
|
|
||||||
|
<varlistentry id="pause-at-recovery-target"
|
||||||
|
xreflabel="pause_at_recovery_target">
|
||||||
|
<term><varname>pause_at_recovery_target</varname>
|
||||||
|
(<type>boolean</type>)
|
||||||
|
</term>
|
||||||
|
<indexterm>
|
||||||
|
<primary><varname>pause_at_recovery_target</> recovery parameter</primary>
|
||||||
|
</indexterm>
|
||||||
|
<listitem>
|
||||||
|
<para>
|
||||||
|
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
|
||||||
|
<function>pg_xlog_replay_resume()</> (See
|
||||||
|
<xref linkend="functions-recovery-control-table">), 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.
|
||||||
|
</para>
|
||||||
|
</listitem>
|
||||||
|
</varlistentry>
|
||||||
|
|
||||||
</variablelist>
|
</variablelist>
|
||||||
</sect1>
|
</sect1>
|
||||||
|
|
||||||
|
@ -182,6 +182,7 @@ static char *recoveryEndCommand = NULL;
|
|||||||
static char *archiveCleanupCommand = NULL;
|
static char *archiveCleanupCommand = NULL;
|
||||||
static RecoveryTargetType recoveryTarget = RECOVERY_TARGET_UNSET;
|
static RecoveryTargetType recoveryTarget = RECOVERY_TARGET_UNSET;
|
||||||
static bool recoveryTargetInclusive = true;
|
static bool recoveryTargetInclusive = true;
|
||||||
|
static bool recoveryPauseAtTarget = true;
|
||||||
static TransactionId recoveryTargetXid;
|
static TransactionId recoveryTargetXid;
|
||||||
static TimestampTz recoveryTargetTime;
|
static TimestampTz recoveryTargetTime;
|
||||||
|
|
||||||
@ -423,6 +424,8 @@ typedef struct XLogCtlData
|
|||||||
XLogRecPtr recoveryLastRecPtr;
|
XLogRecPtr recoveryLastRecPtr;
|
||||||
/* timestamp of last COMMIT/ABORT record replayed (or being replayed) */
|
/* timestamp of last COMMIT/ABORT record replayed (or being replayed) */
|
||||||
TimestampTz recoveryLastXTime;
|
TimestampTz recoveryLastXTime;
|
||||||
|
/* Are we requested to pause recovery? */
|
||||||
|
bool recoveryPause;
|
||||||
|
|
||||||
slock_t info_lck; /* locks shared variables shown above */
|
slock_t info_lck; /* locks shared variables shown above */
|
||||||
} XLogCtlData;
|
} XLogCtlData;
|
||||||
@ -570,6 +573,9 @@ static void readRecoveryCommandFile(void);
|
|||||||
static void exitArchiveRecovery(TimeLineID endTLI,
|
static void exitArchiveRecovery(TimeLineID endTLI,
|
||||||
uint32 endLogId, uint32 endLogSeg);
|
uint32 endLogId, uint32 endLogSeg);
|
||||||
static bool recoveryStopsHere(XLogRecord *record, bool *includeThis);
|
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 void SetLatestXTime(TimestampTz xtime);
|
||||||
static TimestampTz GetLatestXTime(void);
|
static TimestampTz GetLatestXTime(void);
|
||||||
static void CheckRequiredParameterValues(void);
|
static void CheckRequiredParameterValues(void);
|
||||||
@ -5126,6 +5132,15 @@ readRecoveryCommandFile(void)
|
|||||||
(errmsg("archive_cleanup_command = '%s'",
|
(errmsg("archive_cleanup_command = '%s'",
|
||||||
archiveCleanupCommand)));
|
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)
|
else if (strcmp(item->name, "recovery_target_timeline") == 0)
|
||||||
{
|
{
|
||||||
rtliGiven = true;
|
rtliGiven = true;
|
||||||
@ -5508,6 +5523,110 @@ recoveryStopsHere(XLogRecord *record, bool *includeThis)
|
|||||||
return stopsHere;
|
return stopsHere;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 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.
|
* Save timestamp of latest processed commit/abort record.
|
||||||
*
|
*
|
||||||
@ -6074,6 +6193,13 @@ StartupXLOG(void)
|
|||||||
StandbyRecoverPreparedTransactions(false);
|
StandbyRecoverPreparedTransactions(false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
* Must not pause unless we are going to enter Hot Standby.
|
||||||
|
*/
|
||||||
|
recoveryPauseAtTarget = false;
|
||||||
|
}
|
||||||
|
|
||||||
/* Initialize resource managers */
|
/* Initialize resource managers */
|
||||||
for (rmid = 0; rmid <= RM_MAX_ID; rmid++)
|
for (rmid = 0; rmid <= RM_MAX_ID; rmid++)
|
||||||
@ -6098,6 +6224,7 @@ StartupXLOG(void)
|
|||||||
xlogctl->replayEndRecPtr = ReadRecPtr;
|
xlogctl->replayEndRecPtr = ReadRecPtr;
|
||||||
xlogctl->recoveryLastRecPtr = ReadRecPtr;
|
xlogctl->recoveryLastRecPtr = ReadRecPtr;
|
||||||
xlogctl->recoveryLastXTime = 0;
|
xlogctl->recoveryLastXTime = 0;
|
||||||
|
xlogctl->recoveryPause = false;
|
||||||
SpinLockRelease(&xlogctl->info_lck);
|
SpinLockRelease(&xlogctl->info_lck);
|
||||||
|
|
||||||
/* Also ensure XLogReceiptTime has a sane value */
|
/* Also ensure XLogReceiptTime has a sane value */
|
||||||
@ -6146,6 +6273,7 @@ StartupXLOG(void)
|
|||||||
{
|
{
|
||||||
bool recoveryContinue = true;
|
bool recoveryContinue = true;
|
||||||
bool recoveryApply = true;
|
bool recoveryApply = true;
|
||||||
|
bool recoveryPause = false;
|
||||||
ErrorContextCallback errcontext;
|
ErrorContextCallback errcontext;
|
||||||
TimestampTz xtime;
|
TimestampTz xtime;
|
||||||
|
|
||||||
@ -6192,6 +6320,11 @@ StartupXLOG(void)
|
|||||||
*/
|
*/
|
||||||
if (recoveryStopsHere(record, &recoveryApply))
|
if (recoveryStopsHere(record, &recoveryApply))
|
||||||
{
|
{
|
||||||
|
if (recoveryPauseAtTarget)
|
||||||
|
{
|
||||||
|
SetRecoveryPause(true);
|
||||||
|
recoveryPausesHere();
|
||||||
|
}
|
||||||
reachedStopPoint = true; /* see below */
|
reachedStopPoint = true; /* see below */
|
||||||
recoveryContinue = false;
|
recoveryContinue = false;
|
||||||
if (!recoveryApply)
|
if (!recoveryApply)
|
||||||
@ -6218,8 +6351,12 @@ StartupXLOG(void)
|
|||||||
*/
|
*/
|
||||||
SpinLockAcquire(&xlogctl->info_lck);
|
SpinLockAcquire(&xlogctl->info_lck);
|
||||||
xlogctl->replayEndRecPtr = EndRecPtr;
|
xlogctl->replayEndRecPtr = EndRecPtr;
|
||||||
|
recoveryPause = xlogctl->recoveryPause;
|
||||||
SpinLockRelease(&xlogctl->info_lck);
|
SpinLockRelease(&xlogctl->info_lck);
|
||||||
|
|
||||||
|
if (recoveryPause)
|
||||||
|
recoveryPausesHere();
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* If we are attempting to enter Hot Standby mode, process
|
* If we are attempting to enter Hot Standby mode, process
|
||||||
* XIDs we see
|
* XIDs we see
|
||||||
|
@ -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_offset(PG_FUNCTION_ARGS);
|
||||||
extern Datum pg_xlogfile_name(PG_FUNCTION_ARGS);
|
extern Datum pg_xlogfile_name(PG_FUNCTION_ARGS);
|
||||||
extern Datum pg_is_in_recovery(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 */
|
#endif /* XLOG_INTERNAL_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_ ));
|
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");
|
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_ ));
|
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");
|
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_ ));
|
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_ ));
|
||||||
|
Reference in New Issue
Block a user