Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 8c6e3ad

Browse files
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
1 parent f9f9d69 commit 8c6e3ad

File tree

5 files changed

+230
-0
lines changed

5 files changed

+230
-0
lines changed

doc/src/sgml/func.sgml

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14173,6 +14173,64 @@ postgres=# SELECT * FROM pg_xlogfile_name_offset(pg_stop_backup());
1417314173
</tgroup>
1417414174
</table>
1417514175

14176+
<para>
14177+
The functions shown in <xref
14178+
linkend="functions-recovery-control-table"> control the progress of recovery.
14179+
These functions may be executed only during recovery.
14180+
</para>
14181+
14182+
<table id="functions-recovery-control-table">
14183+
<title>Recovery Control Functions</title>
14184+
<tgroup cols="3">
14185+
<thead>
14186+
<row><entry>Name</entry> <entry>Return Type</entry> <entry>Description</entry>
14187+
</row>
14188+
</thead>
14189+
14190+
<tbody>
14191+
<row>
14192+
<entry>
14193+
<literal><function>pg_is_xlog_replay_paused()</function></literal>
14194+
</entry>
14195+
<entry><type>bool</type></entry>
14196+
<entry>True if recovery is paused.
14197+
</entry>
14198+
</row>
14199+
<row>
14200+
<entry>
14201+
<literal><function>pg_xlog_replay_pause()</function></literal>
14202+
</entry>
14203+
<entry><type>void</type></entry>
14204+
<entry>Pauses recovery immediately.
14205+
</entry>
14206+
</row>
14207+
<row>
14208+
<entry>
14209+
<literal><function>pg_xlog_replay_resume()</function></literal>
14210+
</entry>
14211+
<entry><type>void</type></entry>
14212+
<entry>Restarts recovery if it was paused.
14213+
</entry>
14214+
</row>
14215+
</tbody>
14216+
</tgroup>
14217+
</table>
14218+
14219+
<para>
14220+
While recovery is paused no further database changes are applied.
14221+
If in hot standby, all new queries will see the same consistent snapshot
14222+
of the database, and no further query conflicts will be generated until
14223+
recovery is resumed.
14224+
</para>
14225+
14226+
<para>
14227+
If streaming replication is disabled, the paused state may continue
14228+
indefinitely without problem. While streaming replication is in
14229+
progress WAL records will continue to be received, which will
14230+
eventually fill available disk space, depending upon the duration of
14231+
the pause, the rate of WAL generation and available disk space.
14232+
</para>
14233+
1417614234
<para>
1417714235
The functions shown in <xref linkend="functions-admin-dbsize"> calculate
1417814236
the disk space usage of database objects.

doc/src/sgml/recovery-config.sgml

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -227,6 +227,31 @@ restore_command = 'copy "C:\\server\\archivedir\\%f" "%p"' # Windows
227227
</listitem>
228228
</varlistentry>
229229

230+
<varlistentry id="pause-at-recovery-target"
231+
xreflabel="pause_at_recovery_target">
232+
<term><varname>pause_at_recovery_target</varname>
233+
(<type>boolean</type>)
234+
</term>
235+
<indexterm>
236+
<primary><varname>pause_at_recovery_target</> recovery parameter</primary>
237+
</indexterm>
238+
<listitem>
239+
<para>
240+
Specifies whether recovery should pause when the recovery target
241+
is reached. The default is true, if a recovery target is set.
242+
This is intended to allow queries to be executed against the
243+
database to check if this recovery target is the most desirable
244+
point for recovery. The paused state can be resumed by using
245+
<function>pg_xlog_replay_resume()</> (See
246+
<xref linkend="functions-recovery-control-table">), which then
247+
causes recovery to end. If this recovery target is not the
248+
desired stopping point, then shutdown the server, change the
249+
recovery target settings to a later target and restart to
250+
continue recovery.
251+
</para>
252+
</listitem>
253+
</varlistentry>
254+
230255
</variablelist>
231256
</sect1>
232257

src/backend/access/transam/xlog.c

Lines changed: 137 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -182,6 +182,7 @@ static char *recoveryEndCommand = NULL;
182182
static char *archiveCleanupCommand = NULL;
183183
static RecoveryTargetType recoveryTarget = RECOVERY_TARGET_UNSET;
184184
static bool recoveryTargetInclusive = true;
185+
static bool recoveryPauseAtTarget = true;
185186
static TransactionId recoveryTargetXid;
186187
static 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);
570573
static void exitArchiveRecovery(TimeLineID endTLI,
571574
uint32 endLogId, uint32 endLogSeg);
572575
static bool recoveryStopsHere(XLogRecord *record, bool *includeThis);
576+
static void recoveryPausesHere(void);
577+
static bool RecoveryIsPaused(void);
578+
static void SetRecoveryPause(bool recoveryPause);
573579
static void SetLatestXTime(TimestampTz xtime);
574580
static TimestampTz GetLatestXTime(void);
575581
static 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

src/include/access/xlog_internal.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -275,5 +275,8 @@ extern Datum pg_last_xact_replay_timestamp(PG_FUNCTION_ARGS);
275275
extern Datum pg_xlogfile_name_offset(PG_FUNCTION_ARGS);
276276
extern Datum pg_xlogfile_name(PG_FUNCTION_ARGS);
277277
extern Datum pg_is_in_recovery(PG_FUNCTION_ARGS);
278+
extern Datum pg_xlog_replay_pause(PG_FUNCTION_ARGS);
279+
extern Datum pg_xlog_replay_resume(PG_FUNCTION_ARGS);
280+
extern Datum pg_is_xlog_replay_paused(PG_FUNCTION_ARGS);
278281

279282
#endif /* XLOG_INTERNAL_H */

src/include/catalog/pg_proc.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3416,6 +3416,13 @@ DESCR("last xlog replay location");
34163416
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_ ));
34173417
DESCR("timestamp of last replay xact");
34183418

3419+
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_ ));
3420+
DESCR("pauses xlog replay");
3421+
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_ ));
3422+
DESCR("resumes xlog replay, if it was paused");
3423+
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_ ));
3424+
DESCR("true if xlog replay is paused");
3425+
34193426
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_ ));
34203427
DESCR("reload configuration files");
34213428
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_ ));

0 commit comments

Comments
 (0)