Named restore points
Hi,
Here is a patch that implements "named restore points".
It allows DBAs to specify an exact point to which they can recover
but that point will have a name, so they have a better control of when
they want to stop recovery (ie: DBA's won't depend of remember
specific times, dates and such).
This adds a new function: pg_create_restore_point(text) (i'm not
wedded with the name so if someone wants to suggest something better,
that's fine with me), a new xlog record and a new recovery_target
parameter in recovery.conf
--
Jaime Casanova www.2ndQuadrant.com
Professional PostgreSQL: Soporte y capacitación de PostgreSQL
Attachments:
named_restore_points.patchtext/x-patch; charset=US-ASCII; name=named_restore_points.patchDownload
diff --git a/doc/src/sgml/backup.sgml b/doc/src/sgml/backup.sgml
index db7c834..e12720c 100644
*** a/doc/src/sgml/backup.sgml
--- b/doc/src/sgml/backup.sgml
*************** restore_command = 'cp /mnt/server/archiv
*** 1075,1083 ****
the junior DBA dropped your main transaction table), just specify the
required stopping point in <filename>recovery.conf</>. You can specify
the stop point, known as the <quote>recovery target</>, either by
! date/time or by completion of a specific transaction ID. As of this
! writing only the date/time option is very usable, since there are no tools
! to help you identify with any accuracy which transaction ID to use.
</para>
<note>
--- 1075,1084 ----
the junior DBA dropped your main transaction table), just specify the
required stopping point in <filename>recovery.conf</>. You can specify
the stop point, known as the <quote>recovery target</>, either by
! date/time, a named restore point or by completion of a specific transaction ID.
! As of this writing, there are no tools to help you identify with any accuracy
! which transaction ID to use so only date/time and named restore points are
! useful.
</para>
<note>
diff --git a/doc/src/sgml/func.sgml b/doc/src/sgml/func.sgml
index 04769f1..d335104 100644
*** a/doc/src/sgml/func.sgml
--- b/doc/src/sgml/func.sgml
*************** SELECT set_config('log_statement_stats',
*** 13930,13935 ****
--- 13930,13938 ----
<primary>pg_switch_xlog</primary>
</indexterm>
<indexterm>
+ <primary>pg_create_restore_point</primary>
+ </indexterm>
+ <indexterm>
<primary>pg_xlogfile_name</primary>
</indexterm>
<indexterm>
*************** SELECT set_config('log_statement_stats',
*** 13988,13993 ****
--- 13991,14003 ----
</row>
<row>
<entry>
+ <literal><function>pg_create_restore_point(<parameter>name</> <type>text</> )</function></literal>
+ </entry>
+ <entry><type>text</type></entry>
+ <entry>Establish a named point for restore (restricted to superusers)</entry>
+ </row>
+ <row>
+ <entry>
<literal><function>pg_xlogfile_name(<parameter>location</> <type>text</>)</function></literal>
</entry>
<entry><type>text</type></entry>
diff --git a/doc/src/sgml/recovery-config.sgml b/doc/src/sgml/recovery-config.sgml
index bd9dfd1..b3fc001 100644
*** a/doc/src/sgml/recovery-config.sgml
--- b/doc/src/sgml/recovery-config.sgml
*************** restore_command = 'copy "C:\\server\\arc
*** 144,149 ****
--- 144,171 ----
<title>Recovery target settings</title>
<variablelist>
+ <varlistentry id="recovery-target-name" xreflabel="recovery_target_name">
+ <term><varname>recovery_target_name</varname>
+ (<type>string</type>)
+ </term>
+ <indexterm>
+ <primary><varname>recovery_target_name</> recovery parameter</primary>
+ </indexterm>
+ <listitem>
+ <para>
+ This parameter specifies a named restore point, created with
+ <function>pg_create_restore_point()</>, to which recovery
+ will proceed.
+ At most one of <varname>recovery_target_name</>,
+ <xref linkend="recovery-target-time"> or
+ <xref linkend="recovery-target-xid"> can be specified.
+ The default is to recover to the end of the WAL log.
+ The precise stopping point is also influenced by
+ <xref linkend="recovery-target-inclusive">.
+ </para>
+ </listitem>
+ </varlistentry>
+
<varlistentry id="recovery-target-time" xreflabel="recovery_target_time">
<term><varname>recovery_target_time</varname>
(<type>timestamp</type>)
*************** restore_command = 'copy "C:\\server\\arc
*** 155,161 ****
<para>
This parameter specifies the time stamp up to which recovery
will proceed.
! At most one of <varname>recovery_target_time</> and
<xref linkend="recovery-target-xid"> can be specified.
The default is to recover to the end of the WAL log.
The precise stopping point is also influenced by
--- 177,184 ----
<para>
This parameter specifies the time stamp up to which recovery
will proceed.
! At most one of <varname>recovery_target_time</>,
! <xref linkend="recovery-target-name"> or
<xref linkend="recovery-target-xid"> can be specified.
The default is to recover to the end of the WAL log.
The precise stopping point is also influenced by
*************** restore_command = 'copy "C:\\server\\arc
*** 177,183 ****
start, transactions can complete in a different numeric order.
The transactions that will be recovered are those that committed
before (and optionally including) the specified one.
! At most one of <varname>recovery_target_xid</> and
<xref linkend="recovery-target-time"> can be specified.
The default is to recover to the end of the WAL log.
The precise stopping point is also influenced by
--- 200,207 ----
start, transactions can complete in a different numeric order.
The transactions that will be recovered are those that committed
before (and optionally including) the specified one.
! At most one of <varname>recovery_target_xid</>,
! <xref linkend="recovery-target-name"> or
<xref linkend="recovery-target-time"> can be specified.
The default is to recover to the end of the WAL log.
The precise stopping point is also influenced by
diff --git a/src/backend/access/transam/recovery.conf.sample b/src/backend/access/transam/recovery.conf.sample
index f8f6f9b..552aae1 100644
*** a/src/backend/access/transam/recovery.conf.sample
--- b/src/backend/access/transam/recovery.conf.sample
***************
*** 66,76 ****
# If you want to stop rollforward at a specific point, you
# must set a recovery target.
#
! # You may set a recovery target either by transactionId, or
! # by timestamp. Recovery may either include or exclude the
# transaction(s) with the recovery target value (ie, stop either
# just after or just before the given target, respectively).
#
#recovery_target_time = '' # e.g. '2004-07-14 22:39:00 EST'
#
#recovery_target_xid = ''
--- 66,78 ----
# If you want to stop rollforward at a specific point, you
# must set a recovery target.
#
! # You may set a recovery target either by transactionId, a named point
! # or by timestamp. Recovery may either include or exclude the
# transaction(s) with the recovery target value (ie, stop either
# just after or just before the given target, respectively).
#
+ #recovery_target_name = ''
+ #
#recovery_target_time = '' # e.g. '2004-07-14 22:39:00 EST'
#
#recovery_target_xid = ''
diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c
index 5b6a230..a84e3f4 100644
*** a/src/backend/access/transam/xlog.c
--- b/src/backend/access/transam/xlog.c
*************** static RecoveryTargetType recoveryTarget
*** 186,191 ****
--- 186,192 ----
static bool recoveryTargetInclusive = true;
static TransactionId recoveryTargetXid;
static TimestampTz recoveryTargetTime;
+ static char *recoveryTargetName;
/* options taken from recovery.conf for XLOG streaming */
static bool StandbyMode = false;
*************** static char *TriggerFile = NULL;
*** 195,200 ****
--- 196,202 ----
/* if recoveryStopsHere returns true, it saves actual stop xid/time here */
static TransactionId recoveryStopXid;
static TimestampTz recoveryStopTime;
+ static char recoveryStopNamedRestorePoint[MAXFNAMELEN];
static bool recoveryStopAfter;
/*
*************** typedef struct xl_parameter_change
*** 541,546 ****
--- 543,557 ----
int wal_level;
} xl_parameter_change;
+ /*
+ * named restore points
+ */
+ typedef struct xl_named_restore_points
+ {
+ TimestampTz xtime;
+ char name[MAXFNAMELEN];
+ } xl_named_restore_points;
+
/*
* Flags set by interrupt handlers for later service in the redo loop.
*/
*************** writeTimeLineHistory(TimeLineID newTLI,
*** 4378,4383 ****
--- 4389,4402 ----
xlogfname,
recoveryStopAfter ? "after" : "before",
timestamptz_to_str(recoveryStopTime));
+ else if (recoveryTarget == RECOVERY_TARGET_NAME)
+ snprintf(buffer, sizeof(buffer),
+ "%s%u\t%s\t%s named restore point %s\n",
+ (srcfd < 0) ? "" : "\n",
+ parentTLI,
+ xlogfname,
+ recoveryStopAfter ? "after" : "before",
+ recoveryStopNamedRestorePoint);
else
snprintf(buffer, sizeof(buffer),
"%s%u\t%s\tno recovery target specified\n",
*************** readRecoveryCommandFile(void)
*** 5101,5108 ****
--- 5120,5142 ----
ereport(DEBUG2,
(errmsg("recovery_target_timeline = latest")));
}
+ else if (strcmp(item->name, "recovery_target_name") == 0)
+ {
+ recoveryTargetName = pstrdup(item->value);
+ ereport(DEBUG2,
+ (errmsg("recovery_target_name = '%s'",
+ recoveryTargetName)));
+ recoveryTarget = RECOVERY_TARGET_NAME;
+ }
else if (strcmp(item->name, "recovery_target_xid") == 0)
{
+ /*
+ * if recovery_target_name specified, then this overrides
+ * recovery_target_xid
+ */
+ if (recoveryTarget == RECOVERY_TARGET_NAME)
+ continue;
+
errno = 0;
recoveryTargetXid = (TransactionId) strtoul(item->value, NULL, 0);
if (errno == EINVAL || errno == ERANGE)
*************** readRecoveryCommandFile(void)
*** 5117,5126 ****
else if (strcmp(item->name, "recovery_target_time") == 0)
{
/*
! * if recovery_target_xid specified, then this overrides
! * recovery_target_time
*/
! if (recoveryTarget == RECOVERY_TARGET_XID)
continue;
recoveryTarget = RECOVERY_TARGET_TIME;
--- 5151,5160 ----
else if (strcmp(item->name, "recovery_target_time") == 0)
{
/*
! * if recovery_target_xid or recovery_target_name specified,
! * then this overrides recovery_target_time
*/
! if (recoveryTarget == RECOVERY_TARGET_XID || recoveryTarget == RECOVERY_TARGET_NAME)
continue;
recoveryTarget = RECOVERY_TARGET_TIME;
*************** exitArchiveRecovery(TimeLineID endTLI, u
*** 5358,5371 ****
static bool
recoveryStopsHere(XLogRecord *record, bool *includeThis)
{
bool stopsHere;
uint8 record_info;
TimestampTz recordXtime;
! /* We only consider stopping at COMMIT or ABORT records */
if (record->xl_rmid != RM_XACT_ID)
! return false;
record_info = record->xl_info & ~XLR_INFO_MASK;
if (record_info == XLOG_XACT_COMMIT)
{
xl_xact_commit *recordXactCommitData;
--- 5392,5418 ----
static bool
recoveryStopsHere(XLogRecord *record, bool *includeThis)
{
+ bool couldStop;
bool stopsHere;
uint8 record_info;
TimestampTz recordXtime;
! /*
! * We could stop at COMMIT or ABORT records
! */
! couldStop = true;
if (record->xl_rmid != RM_XACT_ID)
! couldStop = false;
! /*
! * Or when we found a named restore point
! */
record_info = record->xl_info & ~XLR_INFO_MASK;
+ if ((record->xl_rmid == RM_XLOG_ID) && (record_info == XLOG_RESTORE_POINT))
+ couldStop = true;
+
+ if (!couldStop)
+ return false;
+
if (record_info == XLOG_XACT_COMMIT)
{
xl_xact_commit *recordXactCommitData;
*************** recoveryStopsHere(XLogRecord *record, bo
*** 5380,5385 ****
--- 5427,5439 ----
recordXactAbortData = (xl_xact_abort *) XLogRecGetData(record);
recordXtime = recordXactAbortData->xact_time;
}
+ else if (record_info == XLOG_RESTORE_POINT)
+ {
+ xl_named_restore_points *recordNamedRestorePoint;
+
+ recordNamedRestorePoint = (xl_named_restore_points *) XLogRecGetData(record);
+ recordXtime = recordNamedRestorePoint->xtime;
+ }
else
return false;
*************** recoveryStopsHere(XLogRecord *record, bo
*** 5405,5411 ****
if (stopsHere)
*includeThis = recoveryTargetInclusive;
}
! else
{
/*
* there can be many transactions that share the same commit time, so
--- 5459,5465 ----
if (stopsHere)
*includeThis = recoveryTargetInclusive;
}
! else if (recoveryTarget == RECOVERY_TARGET_TIME)
{
/*
* there can be many transactions that share the same commit time, so
*************** recoveryStopsHere(XLogRecord *record, bo
*** 5419,5424 ****
--- 5473,5492 ----
if (stopsHere)
*includeThis = false;
}
+ else
+ {
+ char name[MAXFNAMELEN];
+
+ strncpy(name, ((xl_named_restore_points *) XLogRecGetData(record))->name, MAXFNAMELEN);
+ stopsHere = (strncmp(name, recoveryTargetName, MAXFNAMELEN) == 0);
+
+ /*
+ * Because this record does nothing but hold the restore point name
+ * applying or not this record is just waste so recoveryTargetInclusive
+ * is meaningless in this case
+ */
+ *includeThis = false;
+ }
if (stopsHere)
{
*************** recoveryStopsHere(XLogRecord *record, bo
*** 5426,5431 ****
--- 5494,5502 ----
recoveryStopTime = recordXtime;
recoveryStopAfter = *includeThis;
+ if (recoveryTarget == RECOVERY_TARGET_NAME)
+ strncpy(recoveryStopNamedRestorePoint, recoveryTargetName, MAXFNAMELEN);
+
if (record_info == XLOG_XACT_COMMIT)
{
if (recoveryStopAfter)
*************** recoveryStopsHere(XLogRecord *record, bo
*** 5439,5445 ****
recoveryStopXid,
timestamptz_to_str(recoveryStopTime))));
}
! else
{
if (recoveryStopAfter)
ereport(LOG,
--- 5510,5516 ----
recoveryStopXid,
timestamptz_to_str(recoveryStopTime))));
}
! else if (record_info == XLOG_XACT_ABORT)
{
if (recoveryStopAfter)
ereport(LOG,
*************** recoveryStopsHere(XLogRecord *record, bo
*** 5452,5457 ****
--- 5523,5533 ----
recoveryStopXid,
timestamptz_to_str(recoveryStopTime))));
}
+ else
+ ereport(LOG,
+ (errmsg("recovery stopping at named point %s, time %s",
+ recoveryStopNamedRestorePoint,
+ timestamptz_to_str(recoveryStopTime))));
if (recoveryStopAfter)
SetLatestXTime(recordXtime);
*************** XLogPutNextOid(Oid nextOid)
*** 7774,7779 ****
--- 7850,7877 ----
}
/*
+ * Write a restore point record
+ */
+ XLogRecPtr
+ XLogRestorePointName(char *name)
+ {
+ XLogRecPtr RecPtr;
+ XLogRecData rdata;
+ xl_named_restore_points xlrec;
+
+ xlrec.xtime = GetCurrentTimestamp();
+ strncpy(xlrec.name, name, MAXFNAMELEN);
+
+ rdata.data = (char *) &xlrec;
+ rdata.len = sizeof(xl_named_restore_points);
+ rdata.buffer = InvalidBuffer;
+ rdata.next = NULL;
+ RecPtr = XLogInsert(RM_XLOG_ID, XLOG_RESTORE_POINT, &rdata);
+
+ return RecPtr;
+ }
+
+ /*
* Write an XLOG SWITCH record.
*
* Here we just blindly issue an XLogInsert request for the record.
*************** xlog_redo(XLogRecPtr lsn, XLogRecord *re
*** 7988,7993 ****
--- 8086,8095 ----
{
/* nothing to do here */
}
+ else if (info == XLOG_RESTORE_POINT)
+ {
+ /* nothing to do here */
+ }
else if (info == XLOG_SWITCH)
{
/* nothing to do here */
*************** xlog_desc(StringInfo buf, uint8 xl_info,
*** 8127,8132 ****
--- 8229,8244 ----
xlrec.max_locks_per_xact,
wal_level_str);
}
+ else if (info == XLOG_RESTORE_POINT)
+ {
+ xl_named_restore_points xlrec;
+ char name[MAXFNAMELEN];
+
+ memcpy(&xlrec, rec, sizeof(xl_named_restore_points));
+ strncpy(name, xlrec.name, MAXFNAMELEN);
+
+ appendStringInfo(buf, "named restore point: '%s'", name);
+ }
else
appendStringInfo(buf, "UNKNOWN");
}
*************** do_pg_abort_backup(void)
*** 8755,8760 ****
--- 8867,8909 ----
}
/*
+ * pg_create_restore_point: a named point for restore
+ */
+ Datum
+ pg_create_restore_point(PG_FUNCTION_ARGS)
+ {
+ text *restore_name = PG_GETARG_TEXT_P(0);
+ char *restore_name_str;
+ XLogRecPtr restorepoint;
+ char location[MAXFNAMELEN];
+
+ if (!superuser())
+ ereport(ERROR,
+ (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
+ (errmsg("must be superuser to create a restore point"))));
+
+ if (RecoveryInProgress())
+ ereport(ERROR,
+ (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
+ errmsg("recovery is in progress"),
+ errhint("WAL control functions cannot be executed during recovery.")));
+
+ if (!XLogArchivingActive())
+ ereport(NOTICE,
+ (errmsg("WAL archiving is not enabled; you must ensure that WAL segments are copied through other means for restore points to be usefull for you")));
+
+ restore_name_str = text_to_cstring(restore_name);
+ restorepoint = XLogRestorePointName(restore_name_str);
+
+ /*
+ * As a convenience, return the WAL location of the restore point record
+ */
+ snprintf(location, sizeof(location), "%X/%X",
+ restorepoint.xlogid, restorepoint.xrecoff);
+ PG_RETURN_TEXT_P(cstring_to_text(location));
+ }
+
+ /*
* pg_switch_xlog: switch to next xlog file
*/
Datum
diff --git a/src/include/access/xlog.h b/src/include/access/xlog.h
index 74d3427..3394152 100644
*** a/src/include/access/xlog.h
--- b/src/include/access/xlog.h
*************** typedef enum
*** 184,190 ****
{
RECOVERY_TARGET_UNSET,
RECOVERY_TARGET_XID,
! RECOVERY_TARGET_TIME
} RecoveryTargetType;
extern XLogRecPtr XactLastRecEnd;
--- 184,191 ----
{
RECOVERY_TARGET_UNSET,
RECOVERY_TARGET_XID,
! RECOVERY_TARGET_TIME,
! RECOVERY_TARGET_NAME
} RecoveryTargetType;
extern XLogRecPtr XactLastRecEnd;
*************** extern void InitXLOGAccess(void);
*** 302,307 ****
--- 303,309 ----
extern void CreateCheckPoint(int flags);
extern bool CreateRestartPoint(int flags);
extern void XLogPutNextOid(Oid nextOid);
+ extern XLogRecPtr XLogRestorePointName(char *name);
extern XLogRecPtr GetRedoRecPtr(void);
extern XLogRecPtr GetInsertRecPtr(void);
extern XLogRecPtr GetFlushRecPtr(void);
diff --git a/src/include/access/xlog_internal.h b/src/include/access/xlog_internal.h
index ba64035..dd5a252 100644
*** a/src/include/access/xlog_internal.h
--- b/src/include/access/xlog_internal.h
*************** extern XLogRecPtr RequestXLogSwitch(void
*** 266,271 ****
--- 266,272 ----
*/
extern Datum pg_start_backup(PG_FUNCTION_ARGS);
extern Datum pg_stop_backup(PG_FUNCTION_ARGS);
+ extern Datum pg_create_restore_point(PG_FUNCTION_ARGS);
extern Datum pg_switch_xlog(PG_FUNCTION_ARGS);
extern Datum pg_current_xlog_location(PG_FUNCTION_ARGS);
extern Datum pg_current_xlog_insert_location(PG_FUNCTION_ARGS);
diff --git a/src/include/catalog/pg_control.h b/src/include/catalog/pg_control.h
index ddf1398..43c1abe 100644
*** a/src/include/catalog/pg_control.h
--- b/src/include/catalog/pg_control.h
*************** typedef struct CheckPoint
*** 59,64 ****
--- 59,65 ----
#define XLOG_SWITCH 0x40
#define XLOG_BACKUP_END 0x50
#define XLOG_PARAMETER_CHANGE 0x60
+ #define XLOG_RESTORE_POINT 0x70
/*
diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h
index f8b5d4d..b037109 100644
*** a/src/include/catalog/pg_proc.h
--- b/src/include/catalog/pg_proc.h
*************** DATA(insert OID = 2172 ( pg_start_backup
*** 3395,3400 ****
--- 3395,3402 ----
DESCR("prepare for taking an online backup");
DATA(insert OID = 2173 ( pg_stop_backup PGNSP PGUID 12 1 0 0 f f f t f v 0 0 25 "" _null_ _null_ _null_ _null_ pg_stop_backup _null_ _null_ _null_ ));
DESCR("finish taking an online backup");
+ DATA(insert OID = 2847 ( pg_create_restore_point PGNSP PGUID 12 1 0 0 f f f t f v 1 0 25 "25" _null_ _null_ _null_ _null_ pg_create_restore_point _null_ _null_ _null_ ));
+ DESCR("create a named restore point");
DATA(insert OID = 2848 ( pg_switch_xlog PGNSP PGUID 12 1 0 0 f f f t f v 0 0 25 "" _null_ _null_ _null_ _null_ pg_switch_xlog _null_ _null_ _null_ ));
DESCR("switch to new xlog file");
DATA(insert OID = 2849 ( pg_current_xlog_location PGNSP PGUID 12 1 0 0 f f f t f v 0 0 25 "" _null_ _null_ _null_ _null_ pg_current_xlog_location _null_ _null_ _null_ ));
Jaime Casanova <jaime@2ndquadrant.com> writes:
Here is a patch that implements "named restore points".
It allows DBAs to specify an exact point to which they can recover
but that point will have a name, so they have a better control of when
they want to stop recovery (ie: DBA's won't depend of remember
specific times, dates and such).
This adds a new function: pg_create_restore_point(text) (i'm not
wedded with the name so if someone wants to suggest something better,
that's fine with me), a new xlog record and a new recovery_target
parameter in recovery.conf
This seems like it's a lot of mechanism for an awfully small use-case.
How often are people actually going to have the foresight to know that
"right now" is when they would want to restore to later? And is it
really any easier to use a label for that than a timestamp? You're
still going to need to keep track of which label means what.
regards, tom lane
Em 14-01-2011 17:41, Jaime Casanova escreveu:
Here is a patch that implements "named restore points".
Nice feature. I only read the provided documentation and it seems inconsistent
to allow name, time, and xid at recovery_target_name because (i) someone could
name the recovery point as '1234567' (xid) or '2011-01-14' (I use this format
a lot) and (ii) if the suffix name is *_name* it shouldn't allow xid and time.
IMHO, recovery_target_name should allow only names.
--
Euler Taveira de Oliveira
http://www.timbira.com/
On Fri, Jan 14, 2011 at 5:42 PM, Euler Taveira de Oliveira
<euler@timbira.com> wrote:
Em 14-01-2011 17:41, Jaime Casanova escreveu:
Here is a patch that implements "named restore points".
Nice feature. I only read the provided documentation and it seems
inconsistent to allow name, time, and xid at recovery_target_name
it only allow names, but those names could be anything
--
Jaime Casanova www.2ndQuadrant.com
Professional PostgreSQL: Soporte y capacitación de PostgreSQL
On Fri, Jan 14, 2011 at 3:41 PM, Jaime Casanova <jaime@2ndquadrant.com> wrote:
Here is a patch that implements "named restore points".
It allows DBAs to specify an exact point to which they can recover
but that point will have a name, so they have a better control of when
they want to stop recovery (ie: DBA's won't depend of remember
specific times, dates and such).This adds a new function: pg_create_restore_point(text) (i'm not
wedded with the name so if someone wants to suggest something better,
that's fine with me), a new xlog record and a new recovery_target
parameter in recovery.conf
Neat.
--
Robert Haas
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company
On Fri, 2011-01-14 at 17:18 -0500, Tom Lane wrote:
Jaime Casanova <jaime@2ndquadrant.com> writes:
Here is a patch that implements "named restore points".
It allows DBAs to specify an exact point to which they can recover
but that point will have a name, so they have a better control of when
they want to stop recovery (ie: DBA's won't depend of remember
specific times, dates and such).This adds a new function: pg_create_restore_point(text) (i'm not
wedded with the name so if someone wants to suggest something better,
that's fine with me), a new xlog record and a new recovery_target
parameter in recovery.confThis seems like it's a lot of mechanism for an awfully small use-case.
How often are people actually going to have the foresight to know that
"right now" is when they would want to restore to later? And is it
really any easier to use a label for that than a timestamp? You're
still going to need to keep track of which label means what.
I think its the other way around. In order to know what time to restore
to, you have to keep an external list of times when interesting things
happened. This gives you a way of putting that metadata into the log
stream so everything you need is in one place.
You can put a restore point in before or after any major activity, so
you can restore the database if that fails.
e.g. 'daily backup 2001/1/11', 'reference data update 2011/2/5',
'pg_upgrade', etc..
--
Simon Riggs http://www.2ndQuadrant.com/books/
PostgreSQL Development, 24x7 Support, Training and Services
Em 14-01-2011 19:50, Jaime Casanova escreveu:
On Fri, Jan 14, 2011 at 5:42 PM, Euler Taveira de Oliveira
<euler@timbira.com> wrote:Em 14-01-2011 17:41, Jaime Casanova escreveu:
Here is a patch that implements "named restore points".
Nice feature. I only read the provided documentation and it seems
inconsistent to allow name, time, and xid at recovery_target_nameit only allow names, but those names could be anything
OK. I will review your patch at the beginning of the week.
--
Euler Taveira de Oliveira
http://www.timbira.com/
On Fri, Jan 14, 2011 at 8:33 PM, Euler Taveira de Oliveira
<euler@timbira.com> wrote:
OK. I will review your patch at the beginning of the week.
thanks
--
Jaime Casanova www.2ndQuadrant.com
Professional PostgreSQL: Soporte y capacitación de PostgreSQL
On Fri, Jan 14, 2011 at 8:33 PM, Euler Taveira de Oliveira
<euler@timbira.com> wrote:
OK. I will review your patch at the beginning of the week.
Euler, are you still planning to review this? We're running out of time.
--
Robert Haas
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company
Em 14-01-2011 17:41, Jaime Casanova escreveu:
Here is a patch that implements "named restore points".
Sorry, I was swamped with work. :(
Your patch no longer applied so I rebased it and slightly modified it. Review
is below...
+ The default is to recover to the end of the WAL log.
+ The precise stopping point is also influenced by
+ <xref linkend="recovery-target-inclusive">.
+ </para>
This isn't valid. recovery_target_name are not influenced by
recovery_target_inclusive. Sentence removed.
+ static char recoveryStopNamedRestorePoint[MAXFNAMELEN];
Is MAXFNAMELEN appropriate? AFAICS it is used for file name length. [Looking
at code...] It seems to be used for backup label too so it is not so
inappropriate.
+ typedef struct xl_named_restore_points
+ {
+ TimestampTz xtime;
+ char name[MAXFNAMELEN];
+ } xl_named_restore_points;
+
I prefixed those struct members so it won't get confused elsewhere.
+ else if (recoveryTarget == RECOVERY_TARGET_NAME)
+ snprintf(buffer, sizeof(buffer),
+ "%s%u\t%s\t%s named restore point %s\n",
+ (srcfd < 0) ? "" : "\n",
+ parentTLI,
+ xlogfname,
+ recoveryStopAfter ? "after" : "before",
+ recoveryStopNamedRestorePoint);
It doesn't matter if it is after or before the restore point. After/Before
only make sense when we're dealing with transaction or time. Removed.
else if (strcmp(item->name, "recovery_target_xid") == 0)
{
+ /*
+ * if recovery_target_name specified, then this overrides
+ * recovery_target_xid
+ */
+ if (recoveryTarget == RECOVERY_TARGET_NAME)
+ continue;
+
IMHO the right recovery precedence is xid -> name -> time. If you're
specifying xid that's because you know what you are doing. Name takes
precedence over time because it is easier to remember a name than a time. I
implemented this order in the updated patch.
+ recoveryTargetName = pstrdup(item->value);
I also added a check for long names.
+ if ((record->xl_rmid == RM_XLOG_ID) && (record_info == XLOG_RESTORE_POINT))
+ couldStop = true;
+
+ if (!couldStop)
+ return false;
+
I reworked this code path because it seems confusing.
+ recordNamedRestorePoint = (xl_named_restore_points *) XLogRecGetData(record);
+ recordXtime = recordNamedRestorePoint->xtime;
Why don't you store the named restore point here too? You will need it a few
lines below.
+ char name[MAXFNAMELEN];
+
+ memcpy(&xlrec, rec, sizeof(xl_named_restore_points));
+ strncpy(name, xlrec.name, MAXFNAMELEN);
Is it really necessary? I removed it.
+ Datum
+ pg_create_restore_point(PG_FUNCTION_ARGS)
+ {
You should have added a check for long restore point names. Added in the
updated patch.
+ ereport(NOTICE,
+ (errmsg("WAL archiving is not enabled; you must ensure that
WAL segments are copied through other means for restore points to be usefull
for you")));
+
Sentence was rewritten as "WAL archiving is not enabled; you must ensure that
WAL segments are copied through other means to recover up to named restore point".
Finally, this is a nice feature iif we have a way to know what named restore
points are available. DBAs need to take note of this list (that is not good)
and the lazy ones will have a hard time to recover the right name (possibly
with a xlog dump tool).
So how could we store this information? Perhaps a file in
$PGDATA/pg_xlog/restore_label that contains the label (and possibly the WAL
location). Also it must have a way to transmit the restore_label when we add
another restore point. I didn't implement this part (Jaime?) and it seems as
important as the new xlog record type that is in the patch. It seems
complicate but I don't have ideas. Anyone? The restore point names could be
obtained by querying a function (say, pg_restore_point_names or
pg_restore_point_list).
Someone could argue that this feature could be reached if we store label and
WAL location in a file (say restore_label). This mechanism doesn't need a new
WAL record but the downside is that if we lost restore_label we are dead. I'm
not in favor of this approach because it seems too fragile.
I will mark this patch waiting on author because of those open issues.
This patch needs to bump catalog version because of the new function. I'm not
sure if the new record type requires bumping the xlog magic number.
I'm attaching the updated patch and two scripts that I used to play with the
patch.
--
Euler Taveira de Oliveira
http://www.timbira.com/
On Tue, Feb 1, 2011 at 10:02 AM, Euler Taveira de Oliveira
<euler@timbira.com> wrote:
Em 14-01-2011 17:41, Jaime Casanova escreveu:
Here is a patch that implements "named restore points".
Sorry, I was swamped with work. :(
Your patch no longer applied so I rebased it and slightly modified it.
Review is below...
Hi,
Thanks for the review, i've been without internet connection for 4
days so i haven't seen the review until now...
+ The default is to recover to the end of the WAL log. + The precise stopping point is also influenced by + <xref linkend="recovery-target-inclusive">. + </para>This isn't valid. recovery_target_name are not influenced by
recovery_target_inclusive. Sentence removed.
good point! docs are boring so i was in automatic mode ;)
+ static char recoveryStopNamedRestorePoint[MAXFNAMELEN];
Is MAXFNAMELEN appropriate? AFAICS it is used for file name length. [Looking
at code...] It seems to be used for backup label too so it is not so
inappropriate.
right, i used it because it is used for backup label
+ else if (recoveryTarget == RECOVERY_TARGET_NAME) + snprintf(buffer, sizeof(buffer), + "%s%u\t%s\t%s named restore point %s\n", + (srcfd < 0) ? "" : "\n", + parentTLI, + xlogfname, + recoveryStopAfter ? "after" : "before", + recoveryStopNamedRestorePoint);It doesn't matter if it is after or before the restore point. After/Before
only make sense when we're dealing with transaction or time. Removed.
you're right
else if (strcmp(item->name, "recovery_target_xid") == 0) { + /* + * if recovery_target_name specified, then this overrides + * recovery_target_xid + */ + if (recoveryTarget == RECOVERY_TARGET_NAME) + continue; +IMHO the right recovery precedence is xid -> name -> time. If you're
specifying xid that's because you know what you are doing. Name takes
precedence over time because it is easier to remember a name than a time. I
implemented this order in the updated patch.
actually i was expecting to hear opinions about this and i agree with you
+ recoveryTargetName = pstrdup(item->value);
I also added a check for long names.
ok
+ if ((record->xl_rmid == RM_XLOG_ID) && (record_info == XLOG_RESTORE_POINT)) + couldStop = true; + + if (!couldStop) + return false; +I reworked this code path because it seems confusing.
it is... it was the result of debugging an stupid error on my side...
+ recordNamedRestorePoint = (xl_named_restore_points *) XLogRecGetData(record); + recordXtime = recordNamedRestorePoint->xtime;Why don't you store the named restore point here too? You will need it a few
lines below.
don't remember, will see
+ Datum + pg_create_restore_point(PG_FUNCTION_ARGS) + {You should have added a check for long restore point names. Added in the
updated patch.
ok
+ ereport(NOTICE, + (errmsg("WAL archiving is not enabled; you must ensure that WAL segments are copied through other means for restore points to be usefull for you"))); +Sentence was rewritten as "WAL archiving is not enabled; you must ensure
that WAL segments are copied through other means to recover up to named
restore point".
sounds better, thanks
Finally, this is a nice feature iif we have a way to know what named restore
points are available. DBAs need to take note of this list (that is not good)
and the lazy ones will have a hard time to recover the right name (possibly
with a xlog dump tool).So how could we store this information? Perhaps a file in
$PGDATA/pg_xlog/restore_label that contains the label (and possibly the WAL
location). Also it must have a way to transmit the restore_label when we add
another restore point. I didn't implement this part (Jaime?) and it seems as
important as the new xlog record type that is in the patch. It seems
complicate but I don't have ideas. Anyone? The restore point names could be
obtained by querying a function (say, pg_restore_point_names or
pg_restore_point_list).
IMHO, probably the best answer is a tool to retrieve that info... the
problem is that a "restore_label" file should be closely attached to
the WAL segment where the named restore point is... and a sql function
won't say anything about named restore points that are in archived WAL
segments...
I will mark this patch waiting on author because of those open issues.
I'm attaching the updated patch and two scripts that I used to play with the
patch.
ok, i will see you're reviewed version later today
--
Jaime Casanova www.2ndQuadrant.com
Professional PostgreSQL: Soporte y capacitación de PostgreSQL
On Fri, Feb 4, 2011 at 9:15 PM, Jaime Casanova <jaime@2ndquadrant.com> wrote:
ok, i will see you're reviewed version later today
This patch is still marked as "Needs Review" in the CommitFest
application, but I'm thinking perhaps it should be changed to Ready
for Committer? Are there any open issues?
--
Robert Haas
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company
On Mon, Feb 7, 2011 at 10:59 PM, Robert Haas <robertmhaas@gmail.com> wrote:
On Fri, Feb 4, 2011 at 9:15 PM, Jaime Casanova <jaime@2ndquadrant.com> wrote:
ok, i will see you're reviewed version later today
This patch is still marked as "Needs Review" in the CommitFest
application, but I'm thinking perhaps it should be changed to Ready
for Committer? Are there any open issues?
only things i can found are:
+ static char recoveryStopNamedRestorePoint[MAXFNAMELEN];
Is MAXFNAMELEN appropriate? AFAICS it is used for file name length. [Looking
at code...] It seems to be used for backup label too so it is not so
inappropriate.
which is just a question about if MAXFNAMELEN is the right length to use
and
Finally, this is a nice feature iif we have a way to know what named restore
points are available. DBAs need to take note of this list (that is not good)
and the lazy ones will have a hard time to recover the right name (possibly
with a xlog dump tool).So how could we store this information? Perhaps a file in
$PGDATA/pg_xlog/restore_label that contains the label (and possibly the WAL
location). Also it must have a way to transmit the restore_label when we add
another restore point. I didn't implement this part (Jaime?) and it seems as
important as the new xlog record type that is in the patch. It seems
complicate but I don't have ideas. Anyone? The restore point names could be
obtained by querying a function (say, pg_restore_point_names or
pg_restore_point_list).
i still think this should be a separate tool or a dba written list,
the reason beign that with sql function we were not able track restore
points in archived segments... if you like i can try to build a simple
tool for this but don't think that is a showstopper, even without that
the feature is useful IMHO at least
--
Jaime Casanova www.2ndQuadrant.com
Professional PostgreSQL: Soporte y capacitación de PostgreSQL
On Tue, Feb 8, 2011 at 2:05 AM, Jaime Casanova <jaime@2ndquadrant.com> wrote:
Finally, this is a nice feature iif we have a way to know what named restore
points are available. DBAs need to take note of this list (that is not good)
and the lazy ones will have a hard time to recover the right name (possibly
with a xlog dump tool).So how could we store this information? Perhaps a file in
$PGDATA/pg_xlog/restore_label that contains the label (and possibly the WAL
location). Also it must have a way to transmit the restore_label when we add
another restore point. I didn't implement this part (Jaime?) and it seems as
important as the new xlog record type that is in the patch. It seems
complicate but I don't have ideas. Anyone? The restore point names could be
obtained by querying a function (say, pg_restore_point_names or
pg_restore_point_list).i still think this should be a separate tool or a dba written list,
I agree. Keeping track of where you've set named restore points is
not going to be a problem with a simple solution. Which restore
points are available is going to depend on which base backup you
restored and what WAL files you stuffed into pg_xlog.
--
Robert Haas
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company
On Tue, 2011-02-08 at 08:05 -0500, Robert Haas wrote:
On Tue, Feb 8, 2011 at 2:05 AM, Jaime Casanova <jaime@2ndquadrant.com> wrote:
Finally, this is a nice feature iif we have a way to know what named restore
points are available. DBAs need to take note of this list (that is not good)
and the lazy ones will have a hard time to recover the right name (possibly
with a xlog dump tool).So how could we store this information? Perhaps a file in
$PGDATA/pg_xlog/restore_label that contains the label (and possibly the WAL
location). Also it must have a way to transmit the restore_label when we add
another restore point. I didn't implement this part (Jaime?) and it seems as
important as the new xlog record type that is in the patch. It seems
complicate but I don't have ideas. Anyone? The restore point names could be
obtained by querying a function (say, pg_restore_point_names or
pg_restore_point_list).i still think this should be a separate tool or a dba written list,
I agree. Keeping track of where you've set named restore points is
not going to be a problem with a simple solution. Which restore
points are available is going to depend on which base backup you
restored and what WAL files you stuffed into pg_xlog.
Yeah agreed. No need for restore_label
--
Simon Riggs http://www.2ndQuadrant.com/books/
PostgreSQL Development, 24x7 Support, Training and Services
On Fri, 2011-02-04 at 21:15 -0500, Jaime Casanova wrote:
+ else if (recoveryTarget == RECOVERY_TARGET_NAME) + snprintf(buffer, sizeof(buffer), + "%s%u\t%s\t%s named restore point %s\n",
+ (srcfd < 0) ? "" : "\n", + parentTLI, + xlogfname, + recoveryStopAfter ? "after" :"before",
+ recoveryStopNamedRestorePoint);
It doesn't matter if it is after or before the restore point.
After/Before
only make sense when we're dealing with transaction or time.
Removed.
you're right
Not sure I understand the comment "only make sense when we're dealing
with transaction or time." Why?
At present, I think the ability to stop before/after a named restore
point should be put back.
--
Simon Riggs http://www.2ndQuadrant.com/books/
PostgreSQL Development, 24x7 Support, Training and Services
Em 08-02-2011 11:05, Simon Riggs escreveu:
On Fri, 2011-02-04 at 21:15 -0500, Jaime Casanova wrote:
+ else if (recoveryTarget == RECOVERY_TARGET_NAME) + snprintf(buffer, sizeof(buffer), + "%s%u\t%s\t%s named restore point %s\n",
+ (srcfd< 0) ? "" : "\n", + parentTLI, + xlogfname, + recoveryStopAfter ? "after" :"before",
+ recoveryStopNamedRestorePoint);
It doesn't matter if it is after or before the restore point.
After/Before
only make sense when we're dealing with transaction or time.
Removed.
you're right
Not sure I understand the comment "only make sense when we're dealing
with transaction or time." Why?
Because named restore point is a noop xlog record; besides, transaction and
time involves xlog records that contain data.
--
Euler Taveira de Oliveira
http://www.timbira.com/
On Tue, 2011-02-08 at 14:07 -0300, Euler Taveira de Oliveira wrote:
Not sure I understand the comment "only make sense when we're dealing
with transaction or time." Why?Because named restore point is a noop xlog record; besides, transaction and
time involves xlog records that contain data.
Thank you. How obvious!
--
Simon Riggs http://www.2ndQuadrant.com/books/
PostgreSQL Development, 24x7 Support, Training and Services
On Tue, 2011-02-08 at 14:07 -0300, Euler Taveira de Oliveira wrote:
Because named restore point is a noop xlog record; besides, transaction and
time involves xlog records that contain data.
Committed. Thanks for the patch and the review.
I changed the patch to require wal_level > minimal, rather than
archive_mode = on.
--
Simon Riggs http://www.2ndQuadrant.com/books/
PostgreSQL Development, 24x7 Support, Training and Services
On 8 February 2011 19:53, Simon Riggs <simon@2ndquadrant.com> wrote:
On Tue, 2011-02-08 at 14:07 -0300, Euler Taveira de Oliveira wrote:
Because named restore point is a noop xlog record; besides, transaction and
time involves xlog records that contain data.Committed. Thanks for the patch and the review.
I changed the patch to require wal_level > minimal, rather than
archive_mode = on.
This could do with a bit more documentation about usage. Below the
Backup Control Functions table
(http://developer.postgresql.org/pgdocs/postgres/functions-admin.html#FUNCTIONS-ADMIN-BACKUP-TABLE),
each function has a paragraph detailing what it does.
Also, I notice you can easily write over a label. The case I'm
thinking of is someone in psql creating a named restore point, then
later on, they go in again, accidentally cursor up and select the
previous statement and create it again. Would this mean that the
previous label is lost, or would it be the case that any subsequent
duplicate labels would have no effect unless the WAL files with the
original label in were consumed? In either case, a note in the docs
about this would be useful.
And I don't see these label creations getting logged either. Could we
output that to the log because at least then users can grep the
directory for labels, and, in most cases, the time they occurred?
--
Thom Brown
Twitter: @darkixion
IRC (freenode): dark_ixion
Registered Linux user: #516935
On Wed, Feb 9, 2011 at 4:53 AM, Simon Riggs <simon@2ndquadrant.com> wrote:
Committed. Thanks for the patch and the review.
- * We also track the timestamp of the latest applied COMMIT/ABORT record
- * in XLogCtl->recoveryLastXTime, for logging purposes.
+ * We also track the timestamp of the latest applied COMMIT/ABORT/RESTORE POINT
+ * record in XLogCtl->recoveryLastXTime, for logging purposes.
Tracking the timestamp of the restore point record in recoveryLastXTime
messes up pg_last_xact_replay_timestamp which uses recoveryLastXTime.
The timestamp of the restore point is wrongly returned as that of the latest
transaction, by the function.
As far as I read the patch, I don't think that it's necessary to track the
timestamp of the restore point. The attached patch changes the code so
that it doesn't track the timestamp of the restore point.
+ if (strlen(restore_name_str) >= MAXFNAMELEN)
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+ errmsg("value too long for restore point")));
I think that logging the maximum length of the name is useful as follows:
ERROR: value too long for restore point (max 63 characters)
So the attached patch also changes the log message that way.
Regards,
--
Fujii Masao
NIPPON TELEGRAPH AND TELEPHONE CORPORATION
NTT Open Source Software Center
Attachments:
restore_name_timestamp_v1.patchapplication/octet-stream; name=restore_name_timestamp_v1.patchDownload
*** a/src/backend/access/transam/xlog.c
--- b/src/backend/access/transam/xlog.c
***************
*** 5228,5234 **** readRecoveryCommandFile(void)
if (strlen(recoveryTargetName) >= MAXFNAMELEN)
ereport(FATAL,
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
! errmsg("recovery_target_name is too long")));
ereport(DEBUG2,
(errmsg("recovery_target_name = '%s'",
--- 5228,5234 ----
if (strlen(recoveryTargetName) >= MAXFNAMELEN)
ereport(FATAL,
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
! errmsg("recovery_target_name is too long (max %d characters)", MAXFNAMELEN - 1)));
ereport(DEBUG2,
(errmsg("recovery_target_name = '%s'",
***************
*** 5448,5454 **** exitArchiveRecovery(TimeLineID endTLI, uint32 endLogId, uint32 endLogSeg)
* Returns TRUE if we are stopping, FALSE otherwise. On TRUE return,
* *includeThis is set TRUE if we should apply this record before stopping.
*
! * We also track the timestamp of the latest applied COMMIT/ABORT/RESTORE POINT
* record in XLogCtl->recoveryLastXTime, for logging purposes.
* Also, some information is saved in recoveryStopXid et al for use in
* annotating the new timeline's history file.
--- 5448,5454 ----
* Returns TRUE if we are stopping, FALSE otherwise. On TRUE return,
* *includeThis is set TRUE if we should apply this record before stopping.
*
! * We also track the timestamp of the latest applied COMMIT/ABORT
* record in XLogCtl->recoveryLastXTime, for logging purposes.
* Also, some information is saved in recoveryStopXid et al for use in
* annotating the new timeline's history file.
***************
*** 5493,5499 **** recoveryStopsHere(XLogRecord *record, bool *includeThis)
/* Do we have a PITR target at all? */
if (recoveryTarget == RECOVERY_TARGET_UNSET)
{
! SetLatestXTime(recordXtime);
return false;
}
--- 5493,5504 ----
/* Do we have a PITR target at all? */
if (recoveryTarget == RECOVERY_TARGET_UNSET)
{
! /*
! * Save timestamp of latest transaction commit/abort if this is
! * a transaction record
! */
! if (record->xl_rmid == RM_XACT_ID)
! SetLatestXTime(recordXtime);
return false;
}
***************
*** 5583,5592 **** recoveryStopsHere(XLogRecord *record, bool *includeThis)
timestamptz_to_str(recoveryStopTime))));
}
! if (recoveryStopAfter)
SetLatestXTime(recordXtime);
}
! else
SetLatestXTime(recordXtime);
return stopsHere;
--- 5588,5597 ----
timestamptz_to_str(recoveryStopTime))));
}
! if (record->xl_rmid == RM_XACT_ID && recoveryStopAfter)
SetLatestXTime(recordXtime);
}
! else if (record->xl_rmid == RM_XACT_ID)
SetLatestXTime(recordXtime);
return stopsHere;
***************
*** 9220,9226 **** pg_create_restore_point(PG_FUNCTION_ARGS)
if (strlen(restore_name_str) >= MAXFNAMELEN)
ereport(ERROR,
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
! errmsg("value too long for restore point")));
restorepoint = XLogRestorePoint(restore_name_str);
--- 9225,9231 ----
if (strlen(restore_name_str) >= MAXFNAMELEN)
ereport(ERROR,
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
! errmsg("value too long for restore point (max %d characters)", MAXFNAMELEN - 1)));
restorepoint = XLogRestorePoint(restore_name_str);
Em 08-02-2011 17:35, Thom Brown escreveu:
This could do with a bit more documentation about usage. Below the
Backup Control Functions table
(http://developer.postgresql.org/pgdocs/postgres/functions-admin.html#FUNCTIONS-ADMIN-BACKUP-TABLE),
each function has a paragraph detailing what it does.
I forgot to check it.
Also, I notice you can easily write over a label. The case I'm
thinking of is someone in psql creating a named restore point, then
later on, they go in again, accidentally cursor up and select the
previous statement and create it again. Would this mean that the
previous label is lost, or would it be the case that any subsequent
duplicate labels would have no effect unless the WAL files with the
original label in were consumed? In either case, a note in the docs
about this would be useful.
This is a limitation that I pointed out [1]http://archives.postgresql.org/message-id/4D48209C.7050109@timbira.com but people decided to postpone
named restore point management. The first one is used as restore point. I
added it in the attached patch.
And I don't see these label creations getting logged either. Could we
output that to the log because at least then users can grep the
directory for labels, and, in most cases, the time they occurred?
Good point. I included location instead of time; time is already supplied by
log file.
The following patch implements the Thom's suggestions.
[1]: http://archives.postgresql.org/message-id/4D48209C.7050109@timbira.com
--
Euler Taveira de Oliveira
http://www.timbira.com/
Attachments:
nrp.difftext/x-patch; name=nrp.diffDownload
diff --git a/doc/src/sgml/func.sgml b/doc/src/sgml/func.sgml
index 736eb67..fe7e42b 100644
*** a/doc/src/sgml/func.sgml
--- b/doc/src/sgml/func.sgml
*************** postgres=# select pg_start_backup('label
*** 14070,14075 ****
--- 14070,14084 ----
</para>
<para>
+ <function>pg_create_restore_point</> creates a named transaction log record
+ that can be used as recovery point, and then returns the transaction log
+ record location. The given name can be used in <xref
+ linkend="recovery-target-name"> that specifies the point up to which recovery
+ will proceed. Avoid creating restore points that have the same name, recovery
+ stops at the first one.
+ </para>
+
+ <para>
<function>pg_current_xlog_location</> displays the current transaction log write
location in the same format used by the above functions. Similarly,
<function>pg_current_xlog_insert_location</> displays the current transaction log
diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c
index 3ba1f29..b4eb4ac 100644
*** a/src/backend/access/transam/xlog.c
--- b/src/backend/access/transam/xlog.c
*************** XLogRestorePoint(const char *rpName)
*** 8144,8149 ****
--- 8144,8153 ----
RecPtr = XLogInsert(RM_XLOG_ID, XLOG_RESTORE_POINT, &rdata);
+ ereport(LOG,
+ (errmsg("restore point \"%s\" created at %X/%X",
+ rpName, RecPtr.xlogid, RecPtr.xrecoff)));
+
return RecPtr;
}
On Thu, Feb 24, 2011 at 10:28 AM, Euler Taveira de Oliveira
<euler@timbira.com> wrote:
The following patch implements the Thom's suggestions.
[1] http://archives.postgresql.org/message-id/4D48209C.7050109@timbira.com
Committed with some additional wordsmithing.
--
Robert Haas
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company