Commit 8c6e3adb authored by Simon Riggs's avatar Simon Riggs

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
parent f9f9d696
......@@ -14173,6 +14173,64 @@ postgres=# SELECT * FROM pg_xlogfile_name_offset(pg_stop_backup());
</tgroup>
</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>
The functions shown in <xref linkend="functions-admin-dbsize"> calculate
the disk space usage of database objects.
......
......@@ -227,6 +227,31 @@ restore_command = 'copy "C:\\server\\archivedir\\%f" "%p"' # Windows
</listitem>
</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>
</sect1>
......
......@@ -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;
......@@ -5508,6 +5523,110 @@ recoveryStopsHere(XLogRecord *record, bool *includeThis)
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.
*
......@@ -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
......
......@@ -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 */
......@@ -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_ ));
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment