From 71c6a8e375b138af8aa46d80226ea9e98f2b94bc Mon Sep 17 00:00:00 2001
From: Heikki Linnakangas <heikki.linnakangas@iki.fi>
Date: Sat, 25 Jan 2014 17:34:04 +0200
Subject: [PATCH] Add recovery_target='immediate' option.

This allows ending recovery as a consistent state has been reached. Without
this, there was no easy way to e.g restore an online backup, without
replaying any extra WAL after the backup ended.

MauMau and me.
---
 doc/src/sgml/backup.sgml                      |  2 +-
 doc/src/sgml/recovery-config.sgml             | 44 +++++++++++++-----
 .../access/transam/recovery.conf.sample       |  6 +++
 src/backend/access/transam/xlog.c             | 46 ++++++++++++++++++-
 src/include/access/xlog.h                     |  3 +-
 5 files changed, 86 insertions(+), 15 deletions(-)

diff --git a/doc/src/sgml/backup.sgml b/doc/src/sgml/backup.sgml
index a2361d780fb..854b5fde41c 100644
--- a/doc/src/sgml/backup.sgml
+++ b/doc/src/sgml/backup.sgml
@@ -1124,7 +1124,7 @@ restore_command = 'cp /mnt/server/archivedir/%f %p'
    <para>
     If you want to recover to some previous point in time (say, right before
     the junior DBA dropped your main transaction table), just specify the
-    required stopping point in <filename>recovery.conf</>.  You can specify
+    required <link linkend="recovery-target-settings">stopping point</link> in <filename>recovery.conf</>.  You can specify
     the stop point, known as the <quote>recovery target</>, either by
     date/time, named restore point or by completion of a specific transaction
     ID.  As of this writing only the date/time and named restore point options
diff --git a/doc/src/sgml/recovery-config.sgml b/doc/src/sgml/recovery-config.sgml
index 550cdce6f99..b818197299d 100644
--- a/doc/src/sgml/recovery-config.sgml
+++ b/doc/src/sgml/recovery-config.sgml
@@ -199,8 +199,33 @@ restore_command = 'copy "C:\\server\\archivedir\\%f" "%p"'  # Windows
   <sect1 id="recovery-target-settings">
 
     <title>Recovery Target Settings</title>
+     <para>
+      By default, recovery will recover to the end of the WAL log. The
+      following parameters can be used to specify an earlier stopping point.
+      At most one of <varname>recovery_target</>,
+      <varname>recovery_target_name</>, <varname>recovery_target_time</>, or
+      <varname>recovery_target_xid</> can be specified. 
+     </para>
      <variablelist>
 
+     <varlistentry id="recovery-target" xreflabel="recovery_target_name">
+      <term><varname>recovery_target</varname><literal> = 'immediate'</literal></term>
+      <indexterm>
+        <primary><varname>recovery_target</> recovery parameter</primary>
+      </indexterm>
+      <listitem>
+       <para>
+        This parameter specifies that recovery should end as soon as a
+        consistency is reached, ie. as early as possible. When restoring from an
+        online backup, this means the point where taking the backup ended.
+       </para>
+       <para>
+        Technically, this is a string parameter, but <literal>'immediate'</>
+        is currently the only allowed value.
+       </para>
+      </listitem>
+     </varlistentry>
+
      <varlistentry id="recovery-target-name" xreflabel="recovery_target_name">
       <term><varname>recovery_target_name</varname>
            (<type>string</type>)
@@ -212,10 +237,6 @@ restore_command = 'copy "C:\\server\\archivedir\\%f" "%p"'  # Windows
        <para>
         This parameter specifies the 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.
        </para>
       </listitem>
      </varlistentry>
@@ -231,10 +252,6 @@ restore_command = 'copy "C:\\server\\archivedir\\%f" "%p"'  # Windows
        <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
         <xref linkend="recovery-target-inclusive">.
        </para>
@@ -254,15 +271,18 @@ restore_command = 'copy "C:\\server\\archivedir\\%f" "%p"'  # Windows
         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
         <xref linkend="recovery-target-inclusive">.
        </para>
       </listitem>
      </varlistentry>
+     </variablelist>
+     <para>
+       The following options further specify the recovery target, and affect
+       what happens when the target is reached:
+     </para>
+
+     <variablelist>
 
      <varlistentry id="recovery-target-inclusive"
                    xreflabel="recovery_target_inclusive">
diff --git a/src/backend/access/transam/recovery.conf.sample b/src/backend/access/transam/recovery.conf.sample
index 673605cfc66..6f7b38eb197 100644
--- a/src/backend/access/transam/recovery.conf.sample
+++ b/src/backend/access/transam/recovery.conf.sample
@@ -81,6 +81,12 @@
 #recovery_target_inclusive = true
 #
 #
+# Alternatively, you can request stopping as soon as a consistent state
+# is reached, by uncommenting this option.
+#
+#recovery_target = 'immediate'
+#
+#
 # If you want to recover into a timeline other than the "main line" shown in
 # pg_control, specify the timeline number here, or write 'latest' to get
 # the latest branch for which there's a history file.
diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c
index 9559d6d6aef..b333d820c72 100644
--- a/src/backend/access/transam/xlog.c
+++ b/src/backend/access/transam/xlog.c
@@ -5434,6 +5434,19 @@ readRecoveryCommandFile(void)
 					(errmsg_internal("recovery_target_name = '%s'",
 									 recoveryTargetName)));
 		}
+		else if (strcmp(item->name, "recovery_target") == 0)
+		{
+			if (strcmp(item->value, "immediate") == 0)
+				recoveryTarget = RECOVERY_TARGET_IMMEDIATE;
+			else
+				ereport(ERROR,
+						(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+						 errmsg("invalid recovery_target parameter"),
+						 errhint("The only allowed value is 'immediate'")));
+			ereport(DEBUG2,
+					(errmsg_internal("recovery_target = '%s'",
+									 item->value)));
+		}
 		else if (strcmp(item->name, "recovery_target_inclusive") == 0)
 		{
 			/*
@@ -5676,7 +5689,20 @@ recoveryStopsBefore(XLogRecord *record)
 	bool		isCommit;
 	TimestampTz recordXtime = 0;
 
-	/* We only consider stopping before COMMIT or ABORT records. */
+	/* Check if we should stop as soon as reaching consistency */
+	if (recoveryTarget == RECOVERY_TARGET_IMMEDIATE && reachedConsistency)
+	{
+		ereport(LOG,
+				(errmsg("recovery stopping after reaching consistency")));
+
+		recoveryStopAfter = false;
+		recoveryStopXid = InvalidTransactionId;
+		recoveryStopTime = 0;
+		recoveryStopName[0] = '\0';
+		return true;
+	}
+
+	/* Otherwise we only consider stopping before COMMIT or ABORT records. */
 	if (record->xl_rmid != RM_XACT_ID)
 		return false;
 	record_info = record->xl_info & ~XLR_INFO_MASK;
@@ -5825,6 +5851,19 @@ recoveryStopsAfter(XLogRecord *record)
 		}
 	}
 
+	/* Check if we should stop as soon as reaching consistency */
+	if (recoveryTarget == RECOVERY_TARGET_IMMEDIATE && reachedConsistency)
+	{
+		ereport(LOG,
+				(errmsg("recovery stopping after reaching consistency")));
+
+		recoveryStopAfter = true;
+		recoveryStopXid = InvalidTransactionId;
+		recoveryStopTime = 0;
+		recoveryStopName[0] = '\0';
+		return true;
+	}
+
 	return false;
 }
 
@@ -6246,6 +6285,9 @@ StartupXLOG(void)
 			ereport(LOG,
 					(errmsg("starting point-in-time recovery to \"%s\"",
 							recoveryTargetName)));
+		else if (recoveryTarget == RECOVERY_TARGET_IMMEDIATE)
+			ereport(LOG,
+					(errmsg("starting point-in-time recovery to earliest consistent point")));
 		else
 			ereport(LOG,
 					(errmsg("starting archive recovery")));
@@ -7125,6 +7167,8 @@ StartupXLOG(void)
 			snprintf(reason, sizeof(reason),
 					 "at restore point \"%s\"",
 					 recoveryStopName);
+		else if (recoveryTarget == RECOVERY_TARGET_IMMEDIATE)
+			snprintf(reason, sizeof(reason), "reached consistency");
 		else
 			snprintf(reason, sizeof(reason), "no recovery target specified");
 
diff --git a/src/include/access/xlog.h b/src/include/access/xlog.h
index 281f51629e4..47e302276b4 100644
--- a/src/include/access/xlog.h
+++ b/src/include/access/xlog.h
@@ -173,7 +173,8 @@ typedef enum
 	RECOVERY_TARGET_UNSET,
 	RECOVERY_TARGET_XID,
 	RECOVERY_TARGET_TIME,
-	RECOVERY_TARGET_NAME
+	RECOVERY_TARGET_NAME,
+	RECOVERY_TARGET_IMMEDIATE
 } RecoveryTargetType;
 
 extern XLogRecPtr XactLastRecEnd;
-- 
GitLab