On Fri, Dec 6, 2013 at 1:36 PM, Robert Haas <robertmh...@gmail.com> wrote:
> On Thu, Dec 5, 2013 at 11:07 PM, Fabrízio de Royes Mello > <fabriziome...@gmail.com> wrote: > > On Tue, Dec 3, 2013 at 5:33 PM, Simon Riggs <si...@2ndquadrant.com> > wrote: > >> > >> > - compute recoveryUntilDelayTime in XLOG_XACT_COMMIT and > >> > XLOG_XACT_COMMIT_COMPACT checks > >> > >> Why just those? Why not aborts and restore points also? > >> > > > > I think make no sense execute the delay after aborts and/or restore > points, > > because it not change data in a standby server. > > I see no reason to pause for aborts. Aside from the fact that it > wouldn't be reliable in corner cases, as Fabrízio says, there's no > user-visible effect, just as there's no user-visible effect from > replaying a transaction up until just prior to the point where it > commits (which we also do). > > Waiting for restore points seems like it potentially makes sense. If > the standby is delayed by an hour, and you create a restore point and > wait 55 minutes, you might expect that that you can still kill the > standby and recover it to that restore point. > > Makes sense. Fixed. Regards, -- Fabrízio de Royes Mello Consultoria/Coaching PostgreSQL >> Timbira: http://www.timbira.com.br >> Blog sobre TI: http://fabriziomello.blogspot.com >> Perfil Linkedin: http://br.linkedin.com/in/fabriziomello >> Twitter: http://twitter.com/fabriziomello
diff --git a/doc/src/sgml/recovery-config.sgml b/doc/src/sgml/recovery-config.sgml index 9d80256..12aa917 100644 --- a/doc/src/sgml/recovery-config.sgml +++ b/doc/src/sgml/recovery-config.sgml @@ -142,6 +142,31 @@ restore_command = 'copy "C:\\server\\archivedir\\%f" "%p"' # Windows </listitem> </varlistentry> + <varlistentry id="min-standby-apply-delay" xreflabel="min_standby_apply_delay"> + <term><varname>min_standby_apply_delay</varname> (<type>integer</type>)</term> + <indexterm> + <primary><varname>min_standby_apply_delay</> recovery parameter</primary> + </indexterm> + <listitem> + <para> + Specifies the amount of time (in milliseconds, if no unit is specified) + which recovery of transaction commits should lag the master. This + parameter allows creation of a time-delayed standby. For example, if + you set this parameter to <literal>5min</literal>, the standby will + replay each transaction commit only when the system time on the standby + is at least five minutes past the commit time reported by the master. + </para> + <para> + Note that if the master and standby system clocks are not synchronized, + this might lead to unexpected results. + </para> + <para> + This parameter works only for streaming replication deployments. Synchronous + replicas and PITR has not affected. + </para> + </listitem> + </varlistentry> + </variablelist> </sect1> diff --git a/src/backend/access/transam/recovery.conf.sample b/src/backend/access/transam/recovery.conf.sample index 5acfa57..e8617db 100644 --- a/src/backend/access/transam/recovery.conf.sample +++ b/src/backend/access/transam/recovery.conf.sample @@ -123,6 +123,17 @@ # #trigger_file = '' # +# min_standby_apply_delay +# +# By default, a standby server keeps restoring XLOG records from the +# primary as soon as possible. If you want to delay the replay of +# commited transactions from the master, specify a recovery time delay. +# For example, if you set this parameter to 5min, the standby will replay +# each transaction commit only when the system time on the standby is least +# five minutes past the commit time reported by the master. +# +#min_standby_apply_delay = 0 +# #--------------------------------------------------------------------------- # HOT STANDBY PARAMETERS #--------------------------------------------------------------------------- diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c index b68230d..7ca2f9b 100755 --- a/src/backend/access/transam/xlog.c +++ b/src/backend/access/transam/xlog.c @@ -218,6 +218,8 @@ static bool recoveryPauseAtTarget = true; static TransactionId recoveryTargetXid; static TimestampTz recoveryTargetTime; static char *recoveryTargetName; +static int min_standby_apply_delay = 0; +static TimestampTz recoveryDelayUntilTime; /* options taken from recovery.conf for XLOG streaming */ static bool StandbyModeRequested = false; @@ -730,6 +732,7 @@ static void readRecoveryCommandFile(void); static void exitArchiveRecovery(TimeLineID endTLI, XLogSegNo endLogSegNo); static bool recoveryStopsHere(XLogRecord *record, bool *includeThis); static void recoveryPausesHere(void); +static void recoveryDelay(void); static void SetLatestXTime(TimestampTz xtime); static void SetCurrentChunkStartTime(TimestampTz xtime); static void CheckRequiredParameterValues(void); @@ -5474,6 +5477,19 @@ readRecoveryCommandFile(void) (errmsg_internal("trigger_file = '%s'", TriggerFile))); } + else if (strcmp(item->name, "min_standby_apply_delay") == 0) + { + const char *hintmsg; + + if (!parse_int(item->value, &min_standby_apply_delay, GUC_UNIT_MS, + &hintmsg)) + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("parameter \"%s\" requires a temporal value", "min_standby_apply_delay"), + hintmsg ? errhint("%s", _(hintmsg)) : 0)); + ereport(DEBUG2, + (errmsg("min_standby_apply_delay = '%s'", item->value))); + } else ereport(FATAL, (errmsg("unrecognized recovery parameter \"%s\"", @@ -5623,7 +5639,8 @@ exitArchiveRecovery(TimeLineID endTLI, XLogSegNo endLogSegNo) * 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. + * annotating the new timeline's history file; and recoveryDelayUntilTime + * is updated, for time-delayed standbys. */ static bool recoveryStopsHere(XLogRecord *record, bool *includeThis) @@ -5643,6 +5660,11 @@ recoveryStopsHere(XLogRecord *record, bool *includeThis) recordXactCommitData = (xl_xact_commit_compact *) XLogRecGetData(record); recordXtime = recordXactCommitData->xact_time; + + if (min_standby_apply_delay > 0) + recoveryDelayUntilTime = + TimestampTzPlusMilliseconds(recordXactCommitData->xact_time, + min_standby_apply_delay); } else if (record->xl_rmid == RM_XACT_ID && record_info == XLOG_XACT_COMMIT) { @@ -5650,6 +5672,11 @@ recoveryStopsHere(XLogRecord *record, bool *includeThis) recordXactCommitData = (xl_xact_commit *) XLogRecGetData(record); recordXtime = recordXactCommitData->xact_time; + + if (min_standby_apply_delay > 0) + recoveryDelayUntilTime = + TimestampTzPlusMilliseconds(recordXactCommitData->xact_time, + min_standby_apply_delay); } else if (record->xl_rmid == RM_XACT_ID && record_info == XLOG_XACT_ABORT) { @@ -5665,6 +5692,11 @@ recoveryStopsHere(XLogRecord *record, bool *includeThis) recordRestorePointData = (xl_restore_point *) XLogRecGetData(record); recordXtime = recordRestorePointData->rp_time; strncpy(recordRPName, recordRestorePointData->rp_name, MAXFNAMELEN); + + if (min_standby_apply_delay > 0) + recoveryDelayUntilTime = + TimestampTzPlusMilliseconds(recordRestorePointData->rp_time, + min_standby_apply_delay); } else return false; @@ -5832,6 +5864,46 @@ SetRecoveryPause(bool recoveryPause) } /* + * When min_standby_apply_delay is set, we wait long enough to make sure we are + * at least that far behind the master. + */ +static void +recoveryDelay(void) +{ + /* main delay loop */ + while (true) + { + long secs; + int microsecs; + + ResetLatch(&XLogCtl->recoveryWakeupLatch); + + /* might change the trigger file's location */ + HandleStartupProcInterrupts(); + + if (CheckForStandbyTrigger()) + break; + + /* + * Wait for difference between GetCurrentTimestamp() and + * recoveryDelayUntilTime + */ + TimestampDifference(GetCurrentTimestamp(), recoveryDelayUntilTime, + &secs, µsecs); + + if (secs <= 0 && microsecs <=0) + break; + + elog(DEBUG2, "recovery delay %ld seconds, %d milliseconds", + secs, microsecs / 1000); + + WaitLatch(&XLogCtl->recoveryWakeupLatch, + WL_LATCH_SET | WL_TIMEOUT | WL_POSTMASTER_DEATH, + secs * 1000L + microsecs / 1000); + } +} + +/* * Save timestamp of latest processed commit/abort record. * * We keep this in XLogCtl, not a simple static variable, so that it can be @@ -6732,6 +6804,21 @@ StartupXLOG(void) break; } + /* + * If we've been asked to lag the master, pause until enough + * time has passed. + */ + if (min_standby_apply_delay > 0) + { + recoveryDelay(); + + if (xlogctl->recoveryPause) + recoveryPausesHere(); + + if (CheckForStandbyTrigger()) + break; + } + /* Setup error traceback support for ereport() */ errcallback.callback = rm_redo_error_callback; errcallback.arg = (void *) record;
-- Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org) To make changes to your subscription: http://www.postgresql.org/mailpref/pgsql-hackers