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.


> > - don't care about clockdrift because it's an admin problem.
>
> Few minor points on things
>
> * The code with comment "Clear any previous recovery delay time" is in
> wrong place, move down or remove completely. Setting the delay to zero
> doesn't prevent calling recoveryDelay(), so that logic looks wrong
> anyway.
>

Fixed.


> * The loop exit in recoveryDelay() is inelegant, should break if <= 0
>

Fixed.


> * There's a spelling mistake in sample
>

Fixed.


> * The patch has whitespace in one place
>

Fixed.


> and one important point...
>
> * The delay loop happens AFTER we check for a pause. Which means if
> the user notices a problem on a commit, then hits pause button on the
> standby, the pause will have no effect and the next commit will be
> applied anyway. Maybe just one commit, but its an off by one error
> that removes the benefit of the patch. So I think we need to test this
> again after we finish delaying
>
> if (xlogctl->recoveryPause)
>       recoveryPausesHere();
>

Fixed.


>
> We need to explain in the docs that this is intended only for use in a
> live streaming deployment. It will have little or no meaning in a
> PITR.
>

Fixed.


> I think recovery_time_delay should be called
>     <something>_apply_delay
> to highlight the point that it is the apply of records that is
> delayed, not the receipt. And hence the need to document that sync rep
> is NOT slowed down by setting this value.
>

Fixed.


> And to make the name consistent with other parameters, I suggest
>     min_standby_apply_delay
>

I agree. Fixed!


> We also need to document caveats about the patch, in that it only
> delays on timestamped WAL records and other records may be applied
> sooner than the delay in some circumstances, so it is not a way to
> avoid all cancellations.
>
> We also need to document the behaviour of the patch is to apply all
> data received as quickly as possible once triggered, so the specified
> delay does not slow down promoting the server to a master. That might
> also be seen as a negative behaviour, since promoting the master
> effectively sets recovery_time_delay to zero.
>
> I will handle the additional documentation, if you can update the
> patch with the main review comments. Thanks.
>

Thanks, your help is welcome.

Att,

--
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..9d43a72 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)
 	{
@@ -5832,6 +5859,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, &microsecs);
+
+		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 +6799,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

Reply via email to