Skip to content

Commit c016ce7

Browse files
Named restore points in recovery. Users can record named points, then
new recovery.conf parameter recovery_target_name allows PITR to specify named points as recovery targets. Jaime Casanova, reviewed by Euler Taveira de Oliveira, plus minor edits
1 parent 8c6e3ad commit c016ce7

File tree

9 files changed

+209
-17
lines changed

9 files changed

+209
-17
lines changed

doc/src/sgml/backup.sgml

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1086,9 +1086,10 @@ restore_command = 'cp /mnt/server/archivedir/%f %p'
10861086
the junior DBA dropped your main transaction table), just specify the
10871087
required stopping point in <filename>recovery.conf</>. You can specify
10881088
the stop point, known as the <quote>recovery target</>, either by
1089-
date/time or by completion of a specific transaction ID. As of this
1090-
writing only the date/time option is very usable, since there are no tools
1091-
to help you identify with any accuracy which transaction ID to use.
1089+
date/time, named restore point or by completion of a specific transaction
1090+
ID. As of this writing only the date/time and named restore point options
1091+
are very usable, since there are no tools to help you identify with any
1092+
accuracy which transaction ID to use.
10921093
</para>
10931094

10941095
<note>

doc/src/sgml/func.sgml

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13914,6 +13914,9 @@ SELECT set_config('log_statement_stats', 'off', false);
1391413914
<indexterm>
1391513915
<primary>backup</primary>
1391613916
</indexterm>
13917+
<indexterm>
13918+
<primary>pg_create_restore_point</primary>
13919+
</indexterm>
1391713920
<indexterm>
1391813921
<primary>pg_current_xlog_insert_location</primary>
1391913922
</indexterm>
@@ -13951,6 +13954,13 @@ SELECT set_config('log_statement_stats', 'off', false);
1395113954
</thead>
1395213955

1395313956
<tbody>
13957+
<row>
13958+
<entry>
13959+
<literal><function>pg_create_restore_point(<parameter>name</> <type>text</>)</function></literal>
13960+
</entry>
13961+
<entry><type>text</type></entry>
13962+
<entry>Create a named point for performing restore (restricted to superusers)</entry>
13963+
</row>
1395413964
<row>
1395513965
<entry>
1395613966
<literal><function>pg_current_xlog_insert_location()</function></literal>

doc/src/sgml/recovery-config.sgml

Lines changed: 23 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -143,6 +143,25 @@ restore_command = 'copy "C:\\server\\archivedir\\%f" "%p"' # Windows
143143
<title>Recovery Target Settings</title>
144144
<variablelist>
145145

146+
<varlistentry id="recovery-target-name" xreflabel="recovery_target_name">
147+
<term><varname>recovery_target_name</varname>
148+
(<type>string</type>)
149+
</term>
150+
<indexterm>
151+
<primary><varname>recovery_target_name</> recovery parameter</primary>
152+
</indexterm>
153+
<listitem>
154+
<para>
155+
This parameter specifies the named restore point, created with
156+
<function>pg_create_restore_point()</> to which recovery will proceed.
157+
At most one of <varname>recovery_target_name</>,
158+
<xref linkend="recovery-target-time"> or
159+
<xref linkend="recovery-target-xid"> can be specified. The default is to
160+
recover to the end of the WAL log.
161+
</para>
162+
</listitem>
163+
</varlistentry>
164+
146165
<varlistentry id="recovery-target-time" xreflabel="recovery_target_time">
147166
<term><varname>recovery_target_time</varname>
148167
(<type>timestamp</type>)
@@ -154,7 +173,8 @@ restore_command = 'copy "C:\\server\\archivedir\\%f" "%p"' # Windows
154173
<para>
155174
This parameter specifies the time stamp up to which recovery
156175
will proceed.
157-
At most one of <varname>recovery_target_time</> and
176+
At most one of <varname>recovery_target_time</>,
177+
<xref linkend="recovery-target-name"> or
158178
<xref linkend="recovery-target-xid"> can be specified.
159179
The default is to recover to the end of the WAL log.
160180
The precise stopping point is also influenced by
@@ -176,7 +196,8 @@ restore_command = 'copy "C:\\server\\archivedir\\%f" "%p"' # Windows
176196
start, transactions can complete in a different numeric order.
177197
The transactions that will be recovered are those that committed
178198
before (and optionally including) the specified one.
179-
At most one of <varname>recovery_target_xid</> and
199+
At most one of <varname>recovery_target_xid</>,
200+
<xref linkend="recovery-target-name"> or
180201
<xref linkend="recovery-target-time"> can be specified.
181202
The default is to recover to the end of the WAL log.
182203
The precise stopping point is also influenced by

src/backend/access/transam/recovery.conf.sample

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -66,11 +66,14 @@
6666
# If you want to stop rollforward at a specific point, you
6767
# must set a recovery target.
6868
#
69-
# You may set a recovery target either by transactionId, or
70-
# by timestamp. Recovery may either include or exclude the
69+
# You may set a recovery target either by transactionId, by name,
70+
# or by timestamp. Recovery may either include or exclude the
7171
# transaction(s) with the recovery target value (ie, stop either
7272
# just after or just before the given target, respectively).
7373
#
74+
#
75+
#recovery_target_name = '' # e.g. 'daily backup 2011-01-26'
76+
#
7477
#recovery_target_time = '' # e.g. '2004-07-14 22:39:00 EST'
7578
#
7679
#recovery_target_xid = ''

src/backend/access/transam/xlog.c

Lines changed: 161 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -185,15 +185,17 @@ static bool recoveryTargetInclusive = true;
185185
static bool recoveryPauseAtTarget = true;
186186
static TransactionId recoveryTargetXid;
187187
static TimestampTz recoveryTargetTime;
188+
static char *recoveryTargetName;
188189

189190
/* options taken from recovery.conf for XLOG streaming */
190191
static bool StandbyMode = false;
191192
static char *PrimaryConnInfo = NULL;
192193
static char *TriggerFile = NULL;
193194

194-
/* if recoveryStopsHere returns true, it saves actual stop xid/time here */
195+
/* if recoveryStopsHere returns true, it saves actual stop xid/time/name here */
195196
static TransactionId recoveryStopXid;
196197
static TimestampTz recoveryStopTime;
198+
static char recoveryStopName[MAXFNAMELEN];
197199
static bool recoveryStopAfter;
198200

199201
/*
@@ -551,6 +553,13 @@ typedef struct xl_parameter_change
551553
int wal_level;
552554
} xl_parameter_change;
553555

556+
/* logs restore point */
557+
typedef struct xl_restore_point
558+
{
559+
TimestampTz rp_time;
560+
char rp_name[MAXFNAMELEN];
561+
} xl_restore_point;
562+
554563
/*
555564
* Flags set by interrupt handlers for later service in the redo loop.
556565
*/
@@ -4391,6 +4400,13 @@ writeTimeLineHistory(TimeLineID newTLI, TimeLineID parentTLI,
43914400
xlogfname,
43924401
recoveryStopAfter ? "after" : "before",
43934402
timestamptz_to_str(recoveryStopTime));
4403+
else if (recoveryTarget == RECOVERY_TARGET_NAME)
4404+
snprintf(buffer, sizeof(buffer),
4405+
"%s%u\t%s\tat restore point \"%s\"\n",
4406+
(srcfd < 0) ? "" : "\n",
4407+
parentTLI,
4408+
xlogfname,
4409+
recoveryStopName);
43944410
else
43954411
snprintf(buffer, sizeof(buffer),
43964412
"%s%u\t%s\tno recovery target specified\n",
@@ -5178,10 +5194,11 @@ readRecoveryCommandFile(void)
51785194
else if (strcmp(item->name, "recovery_target_time") == 0)
51795195
{
51805196
/*
5181-
* if recovery_target_xid specified, then this overrides
5182-
* recovery_target_time
5197+
* if recovery_target_xid or recovery_target_name specified, then
5198+
* this overrides recovery_target_time
51835199
*/
5184-
if (recoveryTarget == RECOVERY_TARGET_XID)
5200+
if (recoveryTarget == RECOVERY_TARGET_XID ||
5201+
recoveryTarget == RECOVERY_TARGET_NAME)
51855202
continue;
51865203
recoveryTarget = RECOVERY_TARGET_TIME;
51875204

@@ -5197,6 +5214,26 @@ readRecoveryCommandFile(void)
51975214
(errmsg("recovery_target_time = '%s'",
51985215
timestamptz_to_str(recoveryTargetTime))));
51995216
}
5217+
else if (strcmp(item->name, "recovery_target_name") == 0)
5218+
{
5219+
/*
5220+
* if recovery_target_xid specified, then this overrides
5221+
* recovery_target_name
5222+
*/
5223+
if (recoveryTarget == RECOVERY_TARGET_XID)
5224+
continue;
5225+
recoveryTarget = RECOVERY_TARGET_NAME;
5226+
5227+
recoveryTargetName = pstrdup(item->value);
5228+
if (strlen(recoveryTargetName) >= MAXFNAMELEN)
5229+
ereport(FATAL,
5230+
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
5231+
errmsg("recovery_target_name is too long")));
5232+
5233+
ereport(DEBUG2,
5234+
(errmsg("recovery_target_name = '%s'",
5235+
recoveryTargetName)));
5236+
}
52005237
else if (strcmp(item->name, "recovery_target_inclusive") == 0)
52015238
{
52025239
/*
@@ -5411,8 +5448,8 @@ exitArchiveRecovery(TimeLineID endTLI, uint32 endLogId, uint32 endLogSeg)
54115448
* Returns TRUE if we are stopping, FALSE otherwise. On TRUE return,
54125449
* *includeThis is set TRUE if we should apply this record before stopping.
54135450
*
5414-
* We also track the timestamp of the latest applied COMMIT/ABORT record
5415-
* in XLogCtl->recoveryLastXTime, for logging purposes.
5451+
* We also track the timestamp of the latest applied COMMIT/ABORT/RESTORE POINT
5452+
* record in XLogCtl->recoveryLastXTime, for logging purposes.
54165453
* Also, some information is saved in recoveryStopXid et al for use in
54175454
* annotating the new timeline's history file.
54185455
*/
@@ -5422,9 +5459,10 @@ recoveryStopsHere(XLogRecord *record, bool *includeThis)
54225459
bool stopsHere;
54235460
uint8 record_info;
54245461
TimestampTz recordXtime;
5462+
char recordRPName[MAXFNAMELEN];
54255463

5426-
/* We only consider stopping at COMMIT or ABORT records */
5427-
if (record->xl_rmid != RM_XACT_ID)
5464+
/* We only consider stopping at COMMIT, ABORT or RESTORE POINT records */
5465+
if (record->xl_rmid != RM_XACT_ID && record->xl_rmid != RM_XLOG_ID)
54285466
return false;
54295467
record_info = record->xl_info & ~XLR_INFO_MASK;
54305468
if (record_info == XLOG_XACT_COMMIT)
@@ -5441,6 +5479,14 @@ recoveryStopsHere(XLogRecord *record, bool *includeThis)
54415479
recordXactAbortData = (xl_xact_abort *) XLogRecGetData(record);
54425480
recordXtime = recordXactAbortData->xact_time;
54435481
}
5482+
else if (record_info == XLOG_RESTORE_POINT)
5483+
{
5484+
xl_restore_point *recordRestorePointData;
5485+
5486+
recordRestorePointData = (xl_restore_point *) XLogRecGetData(record);
5487+
recordXtime = recordRestorePointData->rp_time;
5488+
strncpy(recordRPName, recordRestorePointData->rp_name, MAXFNAMELEN);
5489+
}
54445490
else
54455491
return false;
54465492

@@ -5466,6 +5512,20 @@ recoveryStopsHere(XLogRecord *record, bool *includeThis)
54665512
if (stopsHere)
54675513
*includeThis = recoveryTargetInclusive;
54685514
}
5515+
else if (recoveryTarget == RECOVERY_TARGET_NAME)
5516+
{
5517+
/*
5518+
* there can be many restore points that share the same name, so we stop
5519+
* at the first one
5520+
*/
5521+
stopsHere = (strcmp(recordRPName, recoveryTargetName) == 0);
5522+
5523+
/*
5524+
* ignore recoveryTargetInclusive because this is not a transaction
5525+
* record
5526+
*/
5527+
*includeThis = false;
5528+
}
54695529
else
54705530
{
54715531
/*
@@ -5500,7 +5560,7 @@ recoveryStopsHere(XLogRecord *record, bool *includeThis)
55005560
recoveryStopXid,
55015561
timestamptz_to_str(recoveryStopTime))));
55025562
}
5503-
else
5563+
else if (record_info == XLOG_XACT_ABORT)
55045564
{
55055565
if (recoveryStopAfter)
55065566
ereport(LOG,
@@ -5513,6 +5573,15 @@ recoveryStopsHere(XLogRecord *record, bool *includeThis)
55135573
recoveryStopXid,
55145574
timestamptz_to_str(recoveryStopTime))));
55155575
}
5576+
else
5577+
{
5578+
strncpy(recoveryStopName, recordRPName, MAXFNAMELEN);
5579+
5580+
ereport(LOG,
5581+
(errmsg("recovery stopping at restore point \"%s\", time %s",
5582+
recoveryStopName,
5583+
timestamptz_to_str(recoveryStopTime))));
5584+
}
55165585

55175586
if (recoveryStopAfter)
55185587
SetLatestXTime(recordXtime);
@@ -5900,6 +5969,10 @@ StartupXLOG(void)
59005969
ereport(LOG,
59015970
(errmsg("starting point-in-time recovery to %s",
59025971
timestamptz_to_str(recoveryTargetTime))));
5972+
else if (recoveryTarget == RECOVERY_TARGET_NAME)
5973+
ereport(LOG,
5974+
(errmsg("starting point-in-time recovery to \"%s\"",
5975+
recoveryTargetName)));
59035976
else
59045977
ereport(LOG,
59055978
(errmsg("starting archive recovery")));
@@ -7989,6 +8062,29 @@ RequestXLogSwitch(void)
79898062
return RecPtr;
79908063
}
79918064

8065+
/*
8066+
* Write a RESTORE POINT record
8067+
*/
8068+
XLogRecPtr
8069+
XLogRestorePoint(const char *rpName)
8070+
{
8071+
XLogRecPtr RecPtr;
8072+
XLogRecData rdata;
8073+
xl_restore_point xlrec;
8074+
8075+
xlrec.rp_time = GetCurrentTimestamp();
8076+
strncpy(xlrec.rp_name, rpName, MAXFNAMELEN);
8077+
8078+
rdata.buffer = InvalidBuffer;
8079+
rdata.data = (char *) &xlrec;
8080+
rdata.len = sizeof(xl_restore_point);
8081+
rdata.next = NULL;
8082+
8083+
RecPtr = XLogInsert(RM_XLOG_ID, XLOG_RESTORE_POINT, &rdata);
8084+
8085+
return RecPtr;
8086+
}
8087+
79928088
/*
79938089
* Check if any of the GUC parameters that are critical for hot standby
79948090
* have changed, and update the value in pg_control file if necessary.
@@ -8181,6 +8277,10 @@ xlog_redo(XLogRecPtr lsn, XLogRecord *record)
81818277
{
81828278
/* nothing to do here */
81838279
}
8280+
else if (info == XLOG_RESTORE_POINT)
8281+
{
8282+
/* nothing to do here */
8283+
}
81848284
else if (info == XLOG_BACKUP_END)
81858285
{
81868286
XLogRecPtr startpoint;
@@ -8283,6 +8383,13 @@ xlog_desc(StringInfo buf, uint8 xl_info, char *rec)
82838383
{
82848384
appendStringInfo(buf, "xlog switch");
82858385
}
8386+
else if (info == XLOG_RESTORE_POINT)
8387+
{
8388+
xl_restore_point *xlrec = (xl_restore_point *) rec;
8389+
8390+
appendStringInfo(buf, "restore point: %s", xlrec->rp_name);
8391+
8392+
}
82868393
else if (info == XLOG_BACKUP_END)
82878394
{
82888395
XLogRecPtr startpoint;
@@ -9080,6 +9187,51 @@ pg_switch_xlog(PG_FUNCTION_ARGS)
90809187
PG_RETURN_TEXT_P(cstring_to_text(location));
90819188
}
90829189

9190+
/*
9191+
* pg_create_restore_point: a named point for restore
9192+
*/
9193+
Datum
9194+
pg_create_restore_point(PG_FUNCTION_ARGS)
9195+
{
9196+
text *restore_name = PG_GETARG_TEXT_P(0);
9197+
char *restore_name_str;
9198+
XLogRecPtr restorepoint;
9199+
char location[MAXFNAMELEN];
9200+
9201+
if (!superuser())
9202+
ereport(ERROR,
9203+
(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
9204+
(errmsg("must be superuser to create a restore point"))));
9205+
9206+
if (RecoveryInProgress())
9207+
ereport(ERROR,
9208+
(errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
9209+
(errmsg("recovery is in progress"),
9210+
errhint("WAL control functions cannot be executed during recovery."))));
9211+
9212+
if (!XLogIsNeeded())
9213+
ereport(ERROR,
9214+
(errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
9215+
errmsg("WAL level not sufficient for creating a restore point"),
9216+
errhint("wal_level must be set to \"archive\" or \"hot_standby\" at server start.")));
9217+
9218+
restore_name_str = text_to_cstring(restore_name);
9219+
9220+
if (strlen(restore_name_str) >= MAXFNAMELEN)
9221+
ereport(ERROR,
9222+
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
9223+
errmsg("value too long for restore point")));
9224+
9225+
restorepoint = XLogRestorePoint(restore_name_str);
9226+
9227+
/*
9228+
* As a convenience, return the WAL location of the restore point record
9229+
*/
9230+
snprintf(location, sizeof(location), "%X/%X",
9231+
restorepoint.xlogid, restorepoint.xrecoff);
9232+
PG_RETURN_TEXT_P(cstring_to_text(location));
9233+
}
9234+
90839235
/*
90849236
* Report the current WAL write location (same format as pg_start_backup etc)
90859237
*

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy