Proposal for changes to recovery.conf API

Started by Simon Riggsover 9 years ago102 messages
#1Simon Riggs
simon@2ndquadrant.com
1 attachment(s)

This is a summary of proposed changes to the recovery.conf API for
v10. These are based in part on earlier discussions, and represent a
minimal modification to current usage.

Proposed changes (with reference to patches from Abhijit Menon-Sen and myself)

* pg_ctl start -M (normal|recover|continue)
pg_ctl can now start the server directly as a standby, similar to the
way pg_ctl promote works. The internal implementation is also similar,
with pg_ctl writing a "recovery.trigger" file that initiates recovery
in the same way recovery.conf used to do. It is still possible to
manually add a file called "recovery.trigger" and have that work if
users desire that mechanism.
Different startup methods can be selected with the <option>-M</option>
option. <quote>Normal</quote> mode starts the server for read/write,
overriding any previous use in Recover mode.
<quote>Recover</quote> mode starts the server as a standby server which
expects to receive changes from a primary/master server using physical
streaming replication or is used for
performing a recovery from backup. <quote>Continue</quote> mode is
the default and will startup the server in whatever mode it was in at
last proper shutdown, or as modified by any trigger files present.
(Patch: recovery_startup_r10_api.v1b.patch)

* $DATADIR/recovery.conf no longer triggers recovery
(Patch: recovery_startup_r10_api.v1b.patch)

* Recovery parameters would now be part of the main postgresql.conf
infrastructure
Any parameters set in $DATADIR/recovery.conf will be read after the
main parameter file, similar to the way that postgresql.conf.auto is
read.
(Abhijit)

* pg_basebackup -R will continue to generate a parameter file called
recovery.conf as it does now, but will also create a file named
recovery.trigger.
(This part is WIP; patch doesn't include implementation for tar format yet)

* Parameters
All of the parameters formerly set in recovery.conf can be set in
postgresql.conf using RELOAD
These parameters will have no defaults in postgresql.conf.sample
Setting them has no effect during normal running, or once recovery ends.
https://www.postgresql.org/docs/devel/static/archive-recovery-settings.html
https://www.postgresql.org/docs/devel/static/recovery-target-settings.html
https://www.postgresql.org/docs/devel/static/standby-settings.html
(Abhijit)

Related cleanup
* Promotion signal file is now called "promote.trigger" rather than
just "promote"
* Remove user configurable "trigger_file" mechanism - use
"promote.trigger" for all cases
* Remove Fallback promote mechanism, so all promotion is now "fast" in xlog.c
* Rename CheckForStandbyTrigger() to CheckForPromoteTrigger() for clarity
(Patch: recovery_startup_r10_api.v1b.patch)

--
Simon Riggs http://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services

Attachments:

recovery_startup_r10_api.v1b.patchapplication/octet-stream; name=recovery_startup_r10_api.v1b.patchDownload
diff --git a/doc/src/sgml/ref/pg_ctl-ref.sgml b/doc/src/sgml/ref/pg_ctl-ref.sgml
index 6ceb781..db70628 100644
--- a/doc/src/sgml/ref/pg_ctl-ref.sgml
+++ b/doc/src/sgml/ref/pg_ctl-ref.sgml
@@ -39,6 +39,13 @@ PostgreSQL documentation
    <arg choice="opt"><option>-o</option> <replaceable>options</replaceable></arg>
    <arg choice="opt"><option>-p</option> <replaceable>path</replaceable></arg>
    <arg choice="opt"><option>-c</option></arg>
+   <arg choice="opt"><option>-M</option>
+     <group choice="plain">
+       <arg choice="plain"><option>c[ontinue]</option></arg>
+       <arg choice="plain"><option>n[ormal]</option></arg>
+       <arg choice="plain"><option>r[ecover]</option></arg>
+     </group>
+   </arg>
   </cmdsynopsis>
 
   <cmdsynopsis>
@@ -72,6 +79,14 @@ PostgreSQL documentation
        <arg choice="plain"><option>i[mmediate]</option></arg>
      </group>
    </arg>
+   <arg choice="opt"><option>-M</option>
+     <group choice="plain">
+       <arg choice="plain"><option>c[ontinue]</option></arg>
+       <arg choice="plain"><option>n[ormal]</option></arg>
+       <arg choice="plain"><option>r[ecover]</option></arg>
+     </group>
+   </arg>
+  </cmdsynopsis>
    <arg choice="opt"><option>-o</option> <replaceable>options</replaceable></arg>
   </cmdsynopsis>
 
@@ -167,6 +182,15 @@ PostgreSQL documentation
    are sent to the terminal.  These default behaviors can be changed
    by using <option>-l</option> to append the server's output to a log file.
    Use of either <option>-l</option> or output redirection is recommended.
+   Different startup methods can be selected with the <option>-M</option>
+   option. <quote>Normal</quote> mode starts the server for read/write,
+   overriding any previous use in Recover mode.
+   <quote>Recover</quote> mode starts the server as a standby server which
+   expects to receive changes from a primary/master server using physical
+   streaming replication or for performing a recovery from backup.
+   <quote>Continue</quote> mode is
+   the default and will startup the server in whatever mode it was in at
+   last proper shutdown, or as modified by any trigger files present.
   </para>
 
   <para>
@@ -215,7 +239,7 @@ PostgreSQL documentation
   <para>
    In <option>promote</option> mode, the standby server that is
    running in the specified data directory is commanded to exit
-   recovery and begin read-write operations.
+   recovery and begin read-write operations (normal mode).
   </para>
 
   <para>
@@ -292,7 +316,20 @@ PostgreSQL documentation
         Specifies the shutdown mode.  <replaceable>mode</replaceable>
         can be <literal>smart</literal>, <literal>fast</literal>, or
         <literal>immediate</literal>, or the first letter of one of
-        these three.  If this is omitted, <literal>fast</literal> is used.
+        these.  If this is omitted, <literal>fast</literal> is used.
+       </para>
+      </listitem>
+     </varlistentry>
+
+     <varlistentry>
+      <term><option>-M <replaceable class="parameter">start-mode</replaceable></option></term>
+      <term><option>--start-mode <replaceable class="parameter">start-mode</replaceable></option></term>
+      <listitem>
+       <para>
+        Specifies the start mode.  <replaceable>start-mode</replaceable>
+        can be <literal>normal</literal>, or <literal>recover</literal>,
+        or <literal>continue</literal>, or the first letter of one of
+        these.  If this is omitted, <literal>continue</literal> is used.
        </para>
       </listitem>
      </varlistentry>
diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c
index f13f9c1..552200e 100644
--- a/src/backend/access/transam/xlog.c
+++ b/src/backend/access/transam/xlog.c
@@ -77,9 +77,8 @@ extern uint32 bootstrap_data_checksum_version;
 
 /* File path names (all relative to $PGDATA) */
 #define RECOVERY_COMMAND_FILE	"recovery.conf"
-#define RECOVERY_COMMAND_DONE	"recovery.done"
-#define PROMOTE_SIGNAL_FILE		"promote"
-#define FALLBACK_PROMOTE_SIGNAL_FILE "fallback_promote"
+#define RECOVERY_SIGNAL_FILE	"recovery.trigger"
+#define PROMOTE_SIGNAL_FILE		"promote.trigger"
 
 
 /* User-settable parameters */
@@ -229,7 +228,7 @@ static int	LocalXLogInsertAllowed = -1;
 
 /*
  * When ArchiveRecoveryRequested is set, archive recovery was requested,
- * ie. recovery.conf file was present. When InArchiveRecovery is set, we are
+ * ie. recovery.trigger file was present. When InArchiveRecovery is set, we are
  * currently recovering using offline XLOG archives. These variables are only
  * valid in the startup process.
  *
@@ -261,14 +260,10 @@ static TimestampTz recoveryDelayUntilTime;
 static bool StandbyModeRequested = false;
 static char *PrimaryConnInfo = NULL;
 static char *PrimarySlotName = NULL;
-static char *TriggerFile = NULL;
 
 /* are we currently in standby mode? */
 bool		StandbyMode = false;
 
-/* whether request for fast promotion has been made yet */
-static bool fast_promote = false;
-
 /*
  * if recoveryStopsBefore/After returns true, it saves information of the stop
  * point here
@@ -835,7 +830,8 @@ static bool rescanLatestTimeLine(void);
 static void WriteControlFile(void);
 static void ReadControlFile(void);
 static char *str_time(pg_time_t tnow);
-static bool CheckForStandbyTrigger(void);
+static bool CheckForRecoveryTrigger(void);
+static bool CheckForPromoteTrigger(void);
 
 #ifdef WAL_DEBUG
 static void xlog_outrec(StringInfo buf, XLogReaderState *record);
@@ -4069,7 +4065,7 @@ ReadRecord(XLogReaderState *xlogreader, XLogRecPtr RecPtr, int emode,
 			}
 
 			/* In standby mode, loop back to retry. Otherwise, give up. */
-			if (StandbyMode && !CheckForStandbyTrigger())
+			if (StandbyMode && !CheckForPromoteTrigger())
 				continue;
 			else
 				return NULL;
@@ -4932,7 +4928,7 @@ str_time(pg_time_t tnow)
 }
 
 /*
- * See if there is a recovery command file (recovery.conf), and if so
+ * See if there is a recovery command file (recovery.trigger), and if so
  * read in parameters for archive recovery and XLOG streaming.
  *
  * The file is parsed using the main configuration parser.
@@ -5132,13 +5128,6 @@ readRecoveryCommandFile(void)
 					(errmsg_internal("primary_slot_name = '%s'",
 									 PrimarySlotName)));
 		}
-		else if (strcmp(item->name, "trigger_file") == 0)
-		{
-			TriggerFile = pstrdup(item->value);
-			ereport(DEBUG2,
-					(errmsg_internal("trigger_file = '%s'",
-									 TriggerFile)));
-		}
 		else if (strcmp(item->name, "recovery_min_apply_delay") == 0)
 		{
 			const char *hintmsg;
@@ -5318,11 +5307,10 @@ exitArchiveRecovery(TimeLineID endTLI, XLogRecPtr endOfLog)
 	unlink(recoveryPath);		/* ignore any error */
 
 	/*
-	 * Rename the config file out of the way, so that we don't accidentally
+	 * Remove the recovery.trigger file out of the way, so that we don't accidentally
 	 * re-enter archive recovery mode in a subsequent crash.
 	 */
-	unlink(RECOVERY_COMMAND_DONE);
-	durable_rename(RECOVERY_COMMAND_FILE, RECOVERY_COMMAND_DONE, FATAL);
+	unlink(RECOVERY_SIGNAL_FILE);
 
 	ereport(LOG,
 			(errmsg("archive recovery complete")));
@@ -5737,7 +5725,7 @@ recoveryApplyDelay(XLogReaderState *record)
 		/* might change the trigger file's location */
 		HandleStartupProcInterrupts();
 
-		if (CheckForStandbyTrigger())
+		if (CheckForPromoteTrigger())
 			break;
 
 		/*
@@ -5928,7 +5916,6 @@ StartupXLOG(void)
 	DBState		dbstate_at_startup;
 	XLogReaderState *xlogreader;
 	XLogPageReadPrivate private;
-	bool		fast_promoted = false;
 	struct stat st;
 
 	/*
@@ -6013,10 +6000,11 @@ StartupXLOG(void)
 		recoveryTargetTLI = ControlFile->checkPointCopy.ThisTimeLineID;
 
 	/*
-	 * Check for recovery control file, and if so set up state for offline
+	 * Check for recovery trigger file, and if so set up state for offline
 	 * recovery
 	 */
-	readRecoveryCommandFile();
+	CheckForRecoveryTrigger();
+	readRecoveryCommandFile(); // remove this later
 
 	/*
 	 * Save archive_cleanup_command in shared memory so that other processes
@@ -6189,7 +6177,7 @@ StartupXLOG(void)
 		 * This can happen for example if a base backup is taken from a
 		 * running server using an atomic filesystem snapshot, without calling
 		 * pg_start/stop_backup. Or if you just kill a running master server
-		 * and put it into archive recovery by creating a recovery.conf file.
+		 * and put it into archive recovery by creating a recovery.trigger file.
 		 *
 		 * Our strategy in that case is to perform crash recovery first,
 		 * replaying all the WAL present in pg_xlog, and only enter archive
@@ -6419,7 +6407,7 @@ StartupXLOG(void)
 
 	/*
 	 * Check whether we need to force recovery from WAL.  If it appears to
-	 * have been a clean shutdown and we did not have a recovery.conf file,
+	 * have been a clean shutdown and we did not have a recovery.trigger file,
 	 * then assume no recovery needed.
 	 */
 	if (checkPoint.redo < RecPtr)
@@ -6433,7 +6421,7 @@ StartupXLOG(void)
 		InRecovery = true;
 	else if (ArchiveRecoveryRequested)
 	{
-		/* force recovery due to presence of recovery.conf */
+		/* force recovery due to presence of recovery.trigger */
 		InRecovery = true;
 	}
 
@@ -7221,20 +7209,16 @@ StartupXLOG(void)
 		 */
 		if (bgwriterLaunched)
 		{
-			if (fast_promote)
-			{
-				checkPointLoc = ControlFile->prevCheckPoint;
-
-				/*
-				 * Confirm the last checkpoint is available for us to recover
-				 * from if we fail. Note that we don't check for the secondary
-				 * checkpoint since that isn't available in most base backups.
-				 */
-				record = ReadCheckpointRecord(xlogreader, checkPointLoc, 1, false);
-				if (record != NULL)
-				{
-					fast_promoted = true;
+			checkPointLoc = ControlFile->prevCheckPoint;
 
+			/*
+			 * Confirm the last checkpoint is available for us to recover
+			 * from if we fail. Note that we don't check for the secondary
+			 * checkpoint since that isn't available in most base backups.
+			 */
+			record = ReadCheckpointRecord(xlogreader, checkPointLoc, 1, false);
+			if (record != NULL)
+			{
 					/*
 					 * Insert a special WAL record to mark the end of
 					 * recovery, since we aren't doing a checkpoint. That
@@ -7247,10 +7231,8 @@ StartupXLOG(void)
 					 * checkpoint later anyway, just for safety.
 					 */
 					CreateEndOfRecoveryRecord();
-				}
 			}
-
-			if (!fast_promoted)
+			else
 				RequestCheckpoint(CHECKPOINT_END_OF_RECOVERY |
 								  CHECKPOINT_IMMEDIATE |
 								  CHECKPOINT_WAIT);
@@ -7433,8 +7415,7 @@ StartupXLOG(void)
 	 * back, and in case of a crash, recovering from it might take a longer
 	 * than is appropriate now that we're not in standby mode anymore.
 	 */
-	if (fast_promoted)
-		RequestCheckpoint(CHECKPOINT_FORCE);
+	RequestCheckpoint(CHECKPOINT_FORCE);
 }
 
 /*
@@ -11177,7 +11158,7 @@ WaitForWALToBecomeAvailable(XLogRecPtr RecPtr, bool randAccess,
 					 * trigger file, we still finish replaying as much as we
 					 * can from archive and pg_xlog before failover.
 					 */
-					if (StandbyMode && CheckForStandbyTrigger())
+					if (StandbyMode && CheckForPromoteTrigger())
 					{
 						ShutdownWalRcv();
 						return false;
@@ -11436,7 +11417,7 @@ WaitForWALToBecomeAvailable(XLogRecPtr RecPtr, bool randAccess,
 					 * Data not here yet. Check for trigger, then wait for
 					 * walreceiver to wake us up when new WAL arrives.
 					 */
-					if (CheckForStandbyTrigger())
+					if (CheckForPromoteTrigger())
 					{
 						/*
 						 * Note that we don't "return false" immediately here.
@@ -11510,11 +11491,10 @@ emode_for_corrupt_record(int emode, XLogRecPtr RecPtr)
 }
 
 /*
- * Check to see whether the user-specified trigger file exists and whether a
- * promote request has arrived.  If either condition holds, return true.
+ * Check to see whether a promote request has arrived.  If so, return true.
  */
 static bool
-CheckForStandbyTrigger(void)
+CheckForPromoteTrigger(void)
 {
 	struct stat stat_buf;
 	static bool triggered = false;
@@ -11527,21 +11507,10 @@ CheckForStandbyTrigger(void)
 		/*
 		 * In 9.1 and 9.2 the postmaster unlinked the promote file inside the
 		 * signal handler. It now leaves the file in place and lets the
-		 * Startup process do the unlink. This allows Startup to know whether
-		 * it should create a full checkpoint before starting up (fallback
-		 * mode). Fast promotion takes precedence.
+		 * Startup process do the unlink.
 		 */
 		if (stat(PROMOTE_SIGNAL_FILE, &stat_buf) == 0)
-		{
 			unlink(PROMOTE_SIGNAL_FILE);
-			unlink(FALLBACK_PROMOTE_SIGNAL_FILE);
-			fast_promote = true;
-		}
-		else if (stat(FALLBACK_PROMOTE_SIGNAL_FILE, &stat_buf) == 0)
-		{
-			unlink(FALLBACK_PROMOTE_SIGNAL_FILE);
-			fast_promote = false;
-		}
 
 		ereport(LOG, (errmsg("received promote request")));
 
@@ -11550,23 +11519,22 @@ CheckForStandbyTrigger(void)
 		return true;
 	}
 
-	if (TriggerFile == NULL)
-		return false;
+	return false;
+}
 
-	if (stat(TriggerFile, &stat_buf) == 0)
+/*
+ * Check to see recovery is requested.  If so, return true.
+ */
+static bool
+CheckForRecoveryTrigger(void)
+{
+	struct stat stat_buf;
+
+	if (stat(RECOVERY_SIGNAL_FILE, &stat_buf) == 0)
 	{
-		ereport(LOG,
-				(errmsg("trigger file found: %s", TriggerFile)));
-		unlink(TriggerFile);
-		triggered = true;
-		fast_promote = true;
+		ArchiveRecoveryRequested = true;
 		return true;
 	}
-	else if (errno != ENOENT)
-		ereport(ERROR,
-				(errcode_for_file_access(),
-				 errmsg("could not stat trigger file \"%s\": %m",
-						TriggerFile)));
 
 	return false;
 }
@@ -11578,7 +11546,6 @@ void
 RemovePromoteSignalFiles(void)
 {
 	unlink(PROMOTE_SIGNAL_FILE);
-	unlink(FALLBACK_PROMOTE_SIGNAL_FILE);
 }
 
 /*
@@ -11590,8 +11557,7 @@ CheckPromoteSignal(void)
 {
 	struct stat stat_buf;
 
-	if (stat(PROMOTE_SIGNAL_FILE, &stat_buf) == 0 ||
-		stat(FALLBACK_PROMOTE_SIGNAL_FILE, &stat_buf) == 0)
+	if (stat(PROMOTE_SIGNAL_FILE, &stat_buf) == 0)
 		return true;
 
 	return false;
diff --git a/src/backend/commands/extension.c b/src/backend/commands/extension.c
index 518fefc..f01f321 100644
--- a/src/backend/commands/extension.c
+++ b/src/backend/commands/extension.c
@@ -9,7 +9,7 @@
  * dependent objects can be associated with it.  An extension is created by
  * populating the pg_extension catalog from a "control" file.
  * The extension control file is parsed with the same parser we use for
- * postgresql.conf and recovery.conf.  An extension also has an installation
+ * postgresql.conf and related files.  An extension also has an installation
  * script file, containing SQL commands to create the extension's objects.
  *
  * Portions Copyright (c) 1996-2016, PostgreSQL Global Development Group
diff --git a/src/bin/pg_basebackup/pg_basebackup.c b/src/bin/pg_basebackup/pg_basebackup.c
index 351a420..869a224 100644
--- a/src/bin/pg_basebackup/pg_basebackup.c
+++ b/src/bin/pg_basebackup/pg_basebackup.c
@@ -1497,6 +1497,7 @@ GenerateRecoveryConf(PGconn *conn)
 /*
  * Write a recovery.conf file into the directory specified in basedir,
  * with the contents already collected in memory.
+ * For 10.x also add recovery.trigger
  */
 static void
 WriteRecoveryConf(void)
@@ -1522,6 +1523,17 @@ WriteRecoveryConf(void)
 	}
 
 	fclose(cf);
+
+	sprintf(filename, "%s/recovery.trigger", basedir);
+
+	cf = fopen(filename, "w");
+	if (cf == NULL)
+	{
+		fprintf(stderr, _("%s: could not create file \"%s\": %s\n"), progname, filename, strerror(errno));
+		disconnect_and_exit(1);
+	}
+
+	fclose(cf);
 }
 
 
diff --git a/src/bin/pg_ctl/pg_ctl.c b/src/bin/pg_ctl/pg_ctl.c
index efc0729..09e0b6e 100644
--- a/src/bin/pg_ctl/pg_ctl.c
+++ b/src/bin/pg_ctl/pg_ctl.c
@@ -50,6 +50,13 @@ typedef enum
 	IMMEDIATE_MODE
 } ShutdownMode;
 
+typedef enum
+{
+	START_CONTINUE,
+	START_NORMAL,
+	START_RECOVER
+} StartMode;
+
 
 typedef enum
 {
@@ -75,6 +82,7 @@ static int	wait_seconds = DEFAULT_WAIT;
 static bool wait_seconds_arg = false;
 static bool silent_mode = false;
 static ShutdownMode shutdown_mode = FAST_MODE;
+static StartMode start_mode = START_CONTINUE;
 static int	sig = SIGINT;		/* default */
 static CtlCommand ctl_command = NO_COMMAND;
 static char *pg_data = NULL;
@@ -115,6 +123,7 @@ static void write_stderr(const char *fmt,...) pg_attribute_printf(1, 2);
 static void do_advice(void);
 static void do_help(void);
 static void set_mode(char *modeopt);
+static void set_start_mode(char *modeopt);
 static void set_sig(char *signame);
 static void do_init(void);
 static void do_start(void);
@@ -867,6 +876,8 @@ do_start(void)
 {
 	pgpid_t		old_pid = 0;
 	pgpid_t		pm_pid;
+	struct stat statbuf;
+	FILE	   *prmfile;
 
 	if (ctl_command != RESTART_COMMAND)
 	{
@@ -906,6 +917,43 @@ do_start(void)
 	}
 #endif
 
+	/*
+	 * if start_mode != START_CONTINUE then prepare signal files
+	 */
+	if (start_mode == START_NORMAL)
+	{
+		/*
+		 * Remove the recovery signal file
+		 */
+		if (stat(recovery_file, &statbuf) == 0)
+		{
+			if (unlink(recovery_file) != 0)
+				write_stderr(_("%s: could not remove recovery signal file \"%s\": %s\n"),
+						 progname, recovery_file, strerror(errno));
+		}
+	}
+	else if (start_mode == START_RECOVER)
+	{
+		if (stat(recovery_file, &statbuf) != 0)
+		{
+			/*
+			 * Write the recovery signal file
+			 */
+			if ((prmfile = fopen(recovery_file, "w")) == NULL)
+			{
+				write_stderr(_("%s: could not create recovery signal file \"%s\": %s\n"),
+							 progname, recovery_file, strerror(errno));
+				exit(1);
+			}
+			if (fclose(prmfile))
+			{
+				write_stderr(_("%s: could not write recovery signal file \"%s\": %s\n"),
+							 progname, recovery_file, strerror(errno));
+				exit(1);
+			}
+		}
+	}
+
 	pm_pid = start_postmaster();
 
 	if (do_wait)
@@ -988,7 +1036,7 @@ do_stop(void)
 		/*
 		 * If backup_label exists, an online backup is running. Warn the user
 		 * that smart shutdown will wait for it to finish. However, if
-		 * recovery.conf is also present, we're recovering from an online
+		 * recovery.trigger is also present, we're recovering from an online
 		 * backup instead of performing one.
 		 */
 		if (shutdown_mode == SMART_MODE &&
@@ -1076,7 +1124,7 @@ do_restart(void)
 		/*
 		 * If backup_label exists, an online backup is running. Warn the user
 		 * that smart shutdown will wait for it to finish. However, if
-		 * recovery.conf is also present, we're recovering from an online
+		 * recovery.trigger is also present, we're recovering from an online
 		 * backup instead of performing one.
 		 */
 		if (shutdown_mode == SMART_MODE &&
@@ -1187,7 +1235,7 @@ do_promote(void)
 		exit(1);
 	}
 
-	/* If recovery.conf doesn't exist, the server is not in standby mode */
+	/* If recovery.trigger doesn't exist, the server cannot be promoted */
 	if (stat(recovery_file, &statbuf) != 0)
 	{
 		write_stderr(_("%s: cannot promote server; "
@@ -1197,12 +1245,8 @@ do_promote(void)
 	}
 
 	/*
-	 * For 9.3 onwards, "fast" promotion is performed. Promotion with a full
-	 * checkpoint is still possible by writing a file called
-	 * "fallback_promote" instead of "promote"
+	 * Write the promote signal file
 	 */
-	snprintf(promote_file, MAXPGPATH, "%s/promote", pg_data);
-
 	if ((prmfile = fopen(promote_file, "w")) == NULL)
 	{
 		write_stderr(_("%s: could not create promote signal file \"%s\": %s\n"),
@@ -1909,10 +1953,11 @@ do_help(void)
 	printf(_("%s is a utility to initialize, start, stop, or control a PostgreSQL server.\n\n"), progname);
 	printf(_("Usage:\n"));
 	printf(_("  %s init[db]               [-D DATADIR] [-s] [-o \"OPTIONS\"]\n"), progname);
-	printf(_("  %s start   [-w] [-t SECS] [-D DATADIR] [-s] [-l FILENAME] [-o \"OPTIONS\"]\n"), progname);
+	printf(_("  %s start   [-w] [-t SECS] [-D DATADIR] [-s] [-l FILENAME] [-o \"OPTIONS\"]\n"
+			 "                 [-M START-MODE] [-o \"OPTIONS\"]\n"), progname);
 	printf(_("  %s stop    [-W] [-t SECS] [-D DATADIR] [-s] [-m SHUTDOWN-MODE]\n"), progname);
 	printf(_("  %s restart [-w] [-t SECS] [-D DATADIR] [-s] [-m SHUTDOWN-MODE]\n"
-			 "                 [-o \"OPTIONS\"]\n"), progname);
+			 "                 [-M START-MODE] [-o \"OPTIONS\"]\n"), progname);
 	printf(_("  %s reload  [-D DATADIR] [-s]\n"), progname);
 	printf(_("  %s status  [-D DATADIR]\n"), progname);
 	printf(_("  %s promote [-D DATADIR] [-s]\n"), progname);
@@ -1949,12 +1994,18 @@ do_help(void)
 	printf(_("  -p PATH-TO-POSTGRES    normally not necessary\n"));
 	printf(_("\nOptions for stop or restart:\n"));
 	printf(_("  -m, --mode=MODE        MODE can be \"smart\", \"fast\", or \"immediate\"\n"));
+	printf(_("  -m, --start-mode=MODE  MODE can be \"continue\", \"normal\", or \"recover\"\n"));
 
 	printf(_("\nShutdown modes are:\n"));
 	printf(_("  smart       quit after all clients have disconnected\n"));
 	printf(_("  fast        quit directly, with proper shutdown\n"));
 	printf(_("  immediate   quit without complete shutdown; will lead to recovery on restart\n"));
 
+	printf(_("\nStartup modes are:\n"));
+	printf(_("  continue    start in same mode as previously\n"));
+	printf(_("  normal      force start in normal mode\n"));
+	printf(_("  recover     force start in recovery mode\n"));
+
 	printf(_("\nAllowed signal names for kill:\n"));
 	printf("  ABRT HUP INT QUIT TERM USR1 USR2\n");
 
@@ -2002,6 +2053,22 @@ set_mode(char *modeopt)
 }
 
 
+static void
+set_start_mode(char *modeopt)
+{
+	if (strcmp(modeopt, "c") == 0 || strcmp(modeopt, "continue") == 0)
+		start_mode = START_CONTINUE;
+	else if (strcmp(modeopt, "n") == 0 || strcmp(modeopt, "normal") == 0)
+		start_mode = START_NORMAL;
+	else if (strcmp(modeopt, "r") == 0 || strcmp(modeopt, "recover") == 0)
+		start_mode = START_RECOVER;
+	else
+	{
+		write_stderr(_("%s: unrecognized start mode \"%s\"\n"), progname, modeopt);
+		do_advice();
+		exit(1);
+	}
+}
 
 static void
 set_sig(char *signame)
@@ -2123,6 +2190,7 @@ main(int argc, char **argv)
 		{"version", no_argument, NULL, 'V'},
 		{"log", required_argument, NULL, 'l'},
 		{"mode", required_argument, NULL, 'm'},
+		{"start-mode", required_argument, NULL, 'M'},
 		{"pgdata", required_argument, NULL, 'D'},
 		{"silent", no_argument, NULL, 's'},
 		{"timeout", required_argument, NULL, 't'},
@@ -2196,7 +2264,7 @@ main(int argc, char **argv)
 	/* process command-line options */
 	while (optind < argc)
 	{
-		while ((c = getopt_long(argc, argv, "cD:e:l:m:N:o:p:P:sS:t:U:wW", long_options, &option_index)) != -1)
+		while ((c = getopt_long(argc, argv, "cD:e:l:m:M:N:o:p:P:sS:t:U:wW", long_options, &option_index)) != -1)
 		{
 			switch (c)
 			{
@@ -2227,6 +2295,9 @@ main(int argc, char **argv)
 				case 'm':
 					set_mode(optarg);
 					break;
+				case 'M':
+					set_start_mode(optarg);
+					break;
 				case 'N':
 					register_servicename = pg_strdup(optarg);
 					break;
@@ -2401,7 +2472,8 @@ main(int argc, char **argv)
 		snprintf(version_file, MAXPGPATH, "%s/PG_VERSION", pg_data);
 		snprintf(pid_file, MAXPGPATH, "%s/postmaster.pid", pg_data);
 		snprintf(backup_file, MAXPGPATH, "%s/backup_label", pg_data);
-		snprintf(recovery_file, MAXPGPATH, "%s/recovery.conf", pg_data);
+		snprintf(recovery_file, MAXPGPATH, "%s/recovery.trigger", pg_data);
+		snprintf(promote_file, MAXPGPATH, "%s/promote.trigger", pg_data);
 	}
 
 	switch (ctl_command)
#2Abhijit Menon-Sen
ams@2ndQuadrant.com
In reply to: Simon Riggs (#1)
1 attachment(s)
Re: Proposal for changes to recovery.conf API

At 2016-08-31 17:15:59 +0100, simon@2ndquadrant.com wrote:

* Recovery parameters would now be part of the main postgresql.conf
infrastructure
Any parameters set in $DATADIR/recovery.conf will be read after the
main parameter file, similar to the way that postgresql.conf.auto is
read.
(Abhijit)

* Parameters
All of the parameters formerly set in recovery.conf can be set in
postgresql.conf using RELOAD
These parameters will have no defaults in postgresql.conf.sample
Setting them has no effect during normal running, or once recovery ends.
https://www.postgresql.org/docs/devel/static/archive-recovery-settings.html
https://www.postgresql.org/docs/devel/static/recovery-target-settings.html
https://www.postgresql.org/docs/devel/static/standby-settings.html
(Abhijit)

I've attached a WIP patch for the above (recovery_guc_v20160831.patch).
This was based on the unite_recoveryconf_postgresqlconf_v3.patch posted
by Fujii Masao.

Unfortunately, some parts conflict with the patch that Simon just posted
(e.g., his patch removes trigger_file altogether, whereas mine converts
it into a GUC along the lines of the original patch). Rather than trying
to untangle that right now, I'm posting what I have as-is, and I'll post
an updated version tomorrow.

-- Abhijit

Attachments:

recovery_guc_v20160831.patchtext/x-diff; charset=us-asciiDownload
commit 999c0c2632f0d4c20d20b9ac30cd258305f74bac
Author: Abhijit Menon-Sen <ams@2ndQuadrant.com>
Date:   Wed Aug 31 22:18:07 2016 +0530

    Convert recovery.conf settings into GUCs
    
    Based on unite_recoveryconf_postgresqlconf_v3.patch by Fujii Masao.

diff --git a/contrib/pg_standby/pg_standby.c b/contrib/pg_standby/pg_standby.c
index e4136f9..8fcb85c 100644
--- a/contrib/pg_standby/pg_standby.c
+++ b/contrib/pg_standby/pg_standby.c
@@ -520,7 +520,7 @@ usage(void)
 	printf("  -w MAXWAITTIME     max seconds to wait for a file (0=no limit) (default=0)\n");
 	printf("  -?, --help         show this help, then exit\n");
 	printf("\n"
-		   "Main intended use as restore_command in recovery.conf:\n"
+		   "Main intended use as restore_command in postgresql.conf:\n"
 		   "  restore_command = 'pg_standby [OPTION]... ARCHIVELOCATION %%f %%p %%r'\n"
 		   "e.g.\n"
 	"  restore_command = 'pg_standby /mnt/server/archiverdir %%f %%p %%r'\n");
diff --git a/doc/src/sgml/backup.sgml b/doc/src/sgml/backup.sgml
index 0f09d82..f43e41e 100644
--- a/doc/src/sgml/backup.sgml
+++ b/doc/src/sgml/backup.sgml
@@ -1174,8 +1174,15 @@ SELECT pg_stop_backup();
    </listitem>
    <listitem>
     <para>
-     Create a recovery command file <filename>recovery.conf</> in the cluster
-     data directory (see <xref linkend="recovery-config">). You might
+     Set up recovery parameters in <filename>postgresql.conf</> (see
+     <xref linkend="runtime-config-wal-archive-recovery"> and
+     <xref linkend="runtime-config-wal-recovery-target">).
+    </para>
+   </listitem>
+   <listitem>
+    <para>
+     Create a recovery trigger file <filename>recovery.trigger</>
+     in the cluster data directory. You might
      also want to temporarily modify <filename>pg_hba.conf</> to prevent
      ordinary users from connecting until you are sure the recovery was successful.
     </para>
@@ -1187,7 +1194,7 @@ SELECT pg_stop_backup();
      recovery be terminated because of an external error, the server can
      simply be restarted and it will continue recovery.  Upon completion
      of the recovery process, the server will rename
-     <filename>recovery.conf</> to <filename>recovery.done</> (to prevent
+     <filename>recovery.trigger</> to <filename>recovery.done</> (to prevent
      accidentally re-entering recovery mode later) and then
      commence normal database operations.
     </para>
@@ -1203,12 +1210,11 @@ SELECT pg_stop_backup();
    </para>
 
    <para>
-    The key part of all this is to set up a recovery configuration file that
-    describes how you want to recover and how far the recovery should
-    run.  You can use <filename>recovery.conf.sample</> (normally
-    located in the installation's <filename>share/</> directory) as a
-    prototype.  The one thing that you absolutely must specify in
-    <filename>recovery.conf</> is the <varname>restore_command</>,
+    The key part of all this is to set up recovery parameters that
+    specify how you want to recover and how far the recovery should
+    run. The one thing that you absolutely must specify in
+    <filename>postgresql.conf</> to recover from the backup is
+    the <varname>restore_command</>,
     which tells <productname>PostgreSQL</> how to retrieve archived
     WAL file segments.  Like the <varname>archive_command</>, this is
     a shell command string.  It can contain <literal>%f</>, which is
@@ -1270,7 +1276,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 <link linkend="recovery-target-settings">stopping point</link> in <filename>recovery.conf</>.  You can specify
+    required <link linkend="recovery-target-settings">stopping point</link> in <filename>postgresql.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
@@ -1367,8 +1373,9 @@ restore_command = 'cp /mnt/server/archivedir/%f %p'
     The default behavior of recovery is to recover along the same timeline
     that was current when the base backup was taken.  If you wish to recover
     into some child timeline (that is, you want to return to some state that
-    was itself generated after a recovery attempt), you need to specify the
-    target timeline ID in <filename>recovery.conf</>.  You cannot recover into
+    was itself generated after a recovery attempt), you need to set
+    <xref linkend="guc-recovery-target-timeline"> to the
+    target timeline ID in <filename>postgresql.conf</>.  You cannot recover into
     timelines that branched off earlier than the base backup.
    </para>
   </sect2>
diff --git a/doc/src/sgml/config.sgml b/doc/src/sgml/config.sgml
index 7c483c6..3daf587 100644
--- a/doc/src/sgml/config.sgml
+++ b/doc/src/sgml/config.sgml
@@ -2840,6 +2840,278 @@ include_dir 'conf.d'
      </variablelist>
     </sect2>
 
+    <sect2 id="runtime-config-wal-archive-recovery">
+     <title>Archive Recovery</title>
+
+     <variablelist>
+      <varlistentry id="guc-restore-command" xreflabel="restore_command">
+       <term><varname>restore_command</varname> (<type>string</type>)</term>
+       <indexterm>
+        <primary><varname>restore_command</> configuration parameter</primary>
+       </indexterm>
+       <listitem>
+        <para>
+         The shell command to execute to retrieve an archived segment of
+         the WAL file series. This parameter is required for archive recovery,
+         but optional for streaming replication.
+         Any <literal>%f</> in the string is
+         replaced by the name of the file to retrieve from the archive,
+         and any <literal>%p</> is replaced by the copy destination path name
+         on the server.
+         (The path name is relative to the current working directory,
+         i.e., the cluster's data directory.)
+         Any <literal>%r</> is replaced by the name of the file containing the
+         last valid restart point. That is the earliest file that must be kept
+         to allow a restore to be restartable, so this information can be used
+         to truncate the archive to just the minimum required to support
+         restarting from the current restore. <literal>%r</> is typically only
+         used by warm-standby configurations
+         (see <xref linkend="warm-standby">).
+         Write <literal>%%</> to embed an actual <literal>%</> character.
+        </para>
+        <para>
+         It is important for the command to return a zero exit status
+         only if it succeeds.  The command <emphasis>will</> be asked for file
+         names that are not present in the archive; it must return nonzero
+         when so asked.  Examples:
+<programlisting>
+restore_command = 'cp /mnt/server/archivedir/%f "%p"'
+restore_command = 'copy "C:\\server\\archivedir\\%f" "%p"'  # Windows
+</programlisting>
+        </para>
+        <para>
+         This parameter can only be set in the <filename>postgresql.conf</>
+         file or on the server command line. It only has effect during archive
+         recovery or in standby mode.
+        </para>
+       </listitem>
+      </varlistentry>
+
+      <varlistentry id="guc-archive-cleanup-command" xreflabel="archive_cleanup_command">
+       <term><varname>archive_cleanup_command</varname> (<type>string</type>)</term>
+       <indexterm>
+         <primary><varname>archive_cleanup_command</> configuration parameter</primary>
+       </indexterm>
+       <listitem>
+        <para>
+         The shell command that will be executed at every restartpoint.
+         The purpose of <varname>archive_cleanup_command</> is to
+         provide a mechanism for cleaning up old archived WAL files that
+         are no longer needed by the standby server.
+         Any <literal>%r</> is replaced by the name of the file containing the
+         last valid restart point.
+         That is the earliest file that must be <emphasis>kept</> to allow a
+         restore to be restartable, and so all files earlier than <literal>%r</>
+         may be safely removed.
+         This information can be used to truncate the archive to just the
+         minimum required to support restart from the current restore.
+         The <xref linkend="pgarchivecleanup"> module
+         is often used in <varname>archive_cleanup_command</> for
+         single-standby configurations, for example:
+<programlisting>archive_cleanup_command = 'pg_archivecleanup /mnt/server/archivedir %r'</programlisting>
+         Note however that if multiple standby servers are restoring from the
+         same archive directory, you will need to ensure that you do not delete
+         WAL files until they are no longer needed by any of the servers.
+         <varname>archive_cleanup_command</> would typically be used in a
+         warm-standby configuration (see <xref linkend="warm-standby">).
+         Write <literal>%%</> to embed an actual <literal>%</> character in the
+         command.
+        </para>
+        <para>
+         If the command returns a non-zero exit status then a WARNING log
+         message will be written.
+        </para>
+        <para>
+         This parameter can only be set in the <filename>postgresql.conf</>
+         file or on the server command line. It only has effect during archive
+         recovery or in standby mode.
+        </para>
+       </listitem>
+      </varlistentry>
+
+      <varlistentry id="guc-recovery-end-command" xreflabel="recovery_end_command">
+       <term><varname>recovery_end_command</varname> (<type>string</type>)</term>
+       <indexterm>
+         <primary><varname>recovery_end_command</> configuration parameter</primary>
+       </indexterm>
+       <listitem>
+        <para>
+         The shell command that will be executed once only
+         at the end of recovery. This parameter is optional. The purpose of the
+         <varname>recovery_end_command</> is to provide a mechanism for cleanup
+         following replication or recovery.
+         Any <literal>%r</> is replaced by the name of the file containing the
+         last valid restart point, like in <varname>archive_cleanup_command</>.
+        </para>
+        <para>
+         If the command returns a non-zero exit status then a WARNING log
+         message will be written and the database will proceed to start up
+         anyway.  An exception is that if the command was terminated by a
+         signal, the database will not proceed with startup.
+        </para>
+        <para>
+         This parameter can only be set in the <filename>postgresql.conf</>
+         file or on the server command line. It only has effect during archive
+         recovery or in standby mode.
+        </para>
+       </listitem>
+      </varlistentry>
+     </variablelist>
+    </sect2>
+
+    <sect2 id="runtime-config-wal-recovery-target">
+     <title>Recovery Target</title>
+
+     <variablelist>
+      <varlistentry id="guc-recovery-target-name" xreflabel="recovery_target_name">
+       <term><varname>recovery_target_name</varname> (<type>string</type>)</term>
+       <indexterm>
+        <primary><varname>recovery_target_name</> configuration parameter</primary>
+       </indexterm>
+       <listitem>
+        <para>
+         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</>,
+         <varname>recovery_target_time</> or
+         <varname>recovery_target_xid</> can be specified.  The default
+         value is an empty string, which will recover to the end of the WAL log.
+        </para>
+        <para>
+         This parameter can only be set in the <filename>postgresql.conf</>
+         file or on the server command line. It only has effect during archive
+         recovery or in standby mode.
+        </para>
+       </listitem>
+      </varlistentry>
+
+      <varlistentry id="guc-recovery-target-time" xreflabel="recovery_target_time">
+       <term><varname>recovery_target_time</varname> (<type>string</type>)</term>
+       <indexterm>
+        <primary><varname>recovery_target_time</> configuration parameter</primary>
+       </indexterm>
+       <listitem>
+        <para>
+         Specifies the time stamp up to which recovery will proceed.
+         This parameter must be specified in the date/time format
+         (see <xref linkend="datatype-datetime-input"> for details).
+         At most one of <varname>recovery_target_time</>,
+         <varname>recovery_target_name</> or
+         <varname>recovery_target_xid</> can be specified.
+         The default value is an empty string, which will recover to
+         the end of the WAL log. The precise stopping point is also
+         influenced by <varname>recovery_target_inclusive</>.
+        </para>
+        <para>
+         This parameter can only be set in the <filename>postgresql.conf</>
+         file or on the server command line. It only has effect during archive
+         recovery or in standby mode.
+        </para>
+       </listitem>
+      </varlistentry>
+
+      <varlistentry id="guc-recovery-target-xid" xreflabel="recovery_target_xid">
+       <term><varname>recovery_target_xid</varname> (<type>string</type>)</term>
+       <indexterm>
+        <primary><varname>recovery_target_xid</> configuration parameter</primary>
+       </indexterm>
+       <listitem>
+        <para>
+         Specifies the transaction ID up to which recovery will proceed.
+         Keep in mind that while transaction IDs are assigned sequentially
+         at transaction 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</>,
+         <varname>recovery_target_name</> or
+         <varname>recovery_target_time</> can be specified.
+         The default value is an empty string, which will recover to the end of
+         the WAL log. The precise stopping point is also influenced by
+         <varname>recovery_target_inclusive</>.
+        </para>
+        <para>
+         This parameter can only be set in the <filename>postgresql.conf</>
+         file or on the server command line. It only has effect during archive
+         recovery or in standby mode.
+        </para>
+       </listitem>
+      </varlistentry>
+
+      <varlistentry id="guc-recovery-target-inclusive" xreflabel="recovery_target_inclusive">
+       <term><varname>recovery_target_inclusive</varname> (<type>boolean</type>)</term>
+       <indexterm>
+        <primary><varname>recovery_target_inclusive</> configuration parameter</primary>
+       </indexterm>
+       <listitem>
+        <para>
+         Specifies whether we stop just after the specified recovery target
+         (<literal>on</>), or just before the recovery target (<literal>off</>).
+         Applies to both <varname>recovery_target_time</>
+         and <varname>recovery_target_xid</>, whichever one is
+         specified for this recovery.  This indicates whether transactions
+         having exactly the target commit time or ID, respectively, will
+         be included in the recovery.  Default is <literal>on</>.
+        </para>
+        <para>
+         This parameter can only be set in the <filename>postgresql.conf</>
+         file or on the server command line. It only has effect during archive
+         recovery or in standby mode.
+        </para>
+       </listitem>
+      </varlistentry>
+
+      <varlistentry id="guc-recovery-target-timeline" xreflabel="recovery_target_timeline">
+       <term><varname>recovery_target_timeline</varname> (<type>string</type>)</term>
+       <indexterm>
+        <primary><varname>recovery_target_timeline</> configuration parameter</primary>
+       </indexterm>
+       <listitem>
+        <para>
+         Specifies recovering into a particular timeline.  The default value is
+         an empty string, which will recover along the same timeline that was
+         current when the base backup was taken. Setting this to
+         <literal>latest</> recovers to the latest timeline found in the archive,
+         which is useful in a standby server. Other than that you only need to
+         set this parameter in complex re-recovery situations, where you need
+         to return to a state that itself was reached after a point-in-time
+         recovery. See <xref linkend="backup-timelines"> for discussion.
+        </para>
+        <para>
+         This parameter can only be set at server start. It only has effect
+         during archive recovery or in standby mode.
+        </para>
+       </listitem>
+      </varlistentry>
+
+      <varlistentry id="guc-pause-at-recovery-target" xreflabel="pause_at_recovery_target">
+       <term><varname>pause_at_recovery_target</varname> (<type>boolean</type>)</term>
+       <indexterm>
+        <primary><varname>pause_at_recovery_target</> configuration parameter</primary>
+       </indexterm>
+       <listitem>
+        <para>
+         Specifies whether recovery should pause when the recovery target
+         is reached. The default is <literal>on</>.
+         This is intended to allow queries to be executed against the
+         database to check if this recovery target is the most desirable
+         point for recovery. The paused state can be resumed by using
+         <function>pg_xlog_replay_resume()</> (See
+         <xref linkend="functions-recovery-control-table">), which then
+         causes recovery to end. If this recovery target is not the
+         desired stopping point, then shutdown the server, change the
+         recovery target settings to a later target and restart to
+         continue recovery.
+        </para>
+        <para>
+         This parameter can only be set in the <filename>postgresql.conf</>
+         file or on the server command line. It only has effect during archive
+         recovery or in standby mode if recovery target is set.
+        </para>
+       </listitem>
+      </varlistentry>
+     </variablelist>
+     </sect2>
+
    </sect1>
 
    <sect1 id="runtime-config-replication">
@@ -3143,6 +3415,93 @@ include_dir 'conf.d'
 
     <variablelist>
 
+     <varlistentry id="guc-standby-mode" xreflabel="standby_mode">
+      <term><varname>standby_mode</varname> (<type>boolean</type>)</term>
+      <indexterm>
+       <primary><varname>standby_mode</> configuration parameter</primary>
+      </indexterm>
+      <listitem>
+       <para>
+        Specifies whether to start the <productname>PostgreSQL</> server as
+        a standby when recovery trigger file <filename>recovery.trigger</> exists.
+        The default value is <literal>off</>.
+        If this parameter is <literal>on</>, the server will not
+        stop recovery when the end of archived WAL is reached,
+        but will keep trying to continue recovery by fetching new WAL segments
+        using <varname>restore_command</> and/or by connecting to
+        the primary server as specified by the <varname>primary_conninfo</>
+        setting.
+       </para>
+       <para>
+        This parameter can only be set at server start. It only has effect
+        if recovery trigger file <filename>recovery.trigger</> exists.
+       </para>
+      </listitem>
+     </varlistentry>
+
+     <varlistentry id="guc-primary-conninfo" xreflabel="primary_conninfo">
+      <term><varname>primary_conninfo</varname> (<type>string</type>)</term>
+      <indexterm>
+        <primary><varname>primary_conninfo</> configuration parameter</primary>
+      </indexterm>
+      <listitem>
+       <para>
+        Specifies a connection string to be used for the standby server
+        to connect with the primary. This string is in the format
+        accepted by the libpq <function>PQconnectdb</function> function,
+        described in <xref linkend="libpq-connect">. If any option is
+        unspecified in this string, then the corresponding environment
+        variable (see <xref linkend="libpq-envars">) is checked. If the
+        environment variable is not set either, then defaults are used.
+        If this parameter is an empty string (the default), no attempt is
+        made to connect to the master.
+       </para>
+       <para>
+        The connection string should specify the host name (or address)
+        of the primary server, as well as the port number if it is not
+        the same as the standby server's default.
+        Also specify a user name corresponding to a role that has the
+        <literal>REPLICATION</> and <literal>LOGIN</> privileges on the
+        primary (see
+        <xref linkend="streaming-replication-authentication">).
+        A password needs to be provided too, if the primary demands password
+        authentication.  It can be provided in the
+        <varname>primary_conninfo</varname> string, or in a separate
+        <filename>~/.pgpass</> file on the standby server (use
+        <literal>replication</> as the database name).
+        Do not specify a database name in the
+        <varname>primary_conninfo</varname> string.
+       </para>
+       <para>
+        This parameter can only be set in the <filename>postgresql.conf</>
+        file or on the server command line. It only has effect in standby mode.
+       </para>
+       <para>
+        If this parameter is changed while replication is in progress,
+        the standby terminates replication, and then tries to restart
+        replication with new setting.
+       </para>
+      </listitem>
+     </varlistentry>
+
+     <varlistentry id="guc-trigger-file" xreflabel="trigger_file">
+      <term><varname>trigger_file</varname> (<type>string</type>)</term>
+      <indexterm>
+        <primary><varname>trigger_file</> configuration parameter</primary>
+      </indexterm>
+      <listitem>
+       <para>
+        Specifies a trigger file whose presence ends recovery in the
+        standby.  Even if this value is not set, you can still promote
+        the standby using <command>pg_ctl promote</>.
+       </para>
+       <para>
+        This parameter can only be set in the <filename>postgresql.conf</>
+        file or on the server command line. It only has effect in standby mode.
+       </para>
+      </listitem>
+    </varlistentry>
+
      <varlistentry id="guc-hot-standby" xreflabel="hot_standby">
       <term><varname>hot_standby</varname> (<type>boolean</type>)
       <indexterm>
diff --git a/doc/src/sgml/filelist.sgml b/doc/src/sgml/filelist.sgml
index 4383711..e9a6236 100644
--- a/doc/src/sgml/filelist.sgml
+++ b/doc/src/sgml/filelist.sgml
@@ -44,7 +44,6 @@
 <!ENTITY manage-ag     SYSTEM "manage-ag.sgml">
 <!ENTITY monitoring    SYSTEM "monitoring.sgml">
 <!ENTITY regress       SYSTEM "regress.sgml">
-<!ENTITY recovery-config SYSTEM "recovery-config.sgml">
 <!ENTITY runtime       SYSTEM "runtime.sgml">
 <!ENTITY config        SYSTEM "config.sgml">
 <!ENTITY user-manag    SYSTEM "user-manag.sgml">
diff --git a/doc/src/sgml/func.sgml b/doc/src/sgml/func.sgml
index 5148095..1a01a59 100644
--- a/doc/src/sgml/func.sgml
+++ b/doc/src/sgml/func.sgml
@@ -18092,7 +18092,7 @@ postgres=# select pg_start_backup('label_goes_here');
     <function>pg_create_restore_point</> creates a named transaction log
     record that can be used as recovery target, and returns the corresponding
     transaction log location.  The given name can then be used with
-    <xref linkend="recovery-target-name"> to specify the point up to which
+    <xref linkend="guc-recovery-target-name"> to specify the point up to which
     recovery will proceed.  Avoid creating multiple restore points with the
     same name, since recovery will stop at the first one whose name matches
     the recovery target.
diff --git a/doc/src/sgml/high-availability.sgml b/doc/src/sgml/high-availability.sgml
index 06f49db..65ae17f 100644
--- a/doc/src/sgml/high-availability.sgml
+++ b/doc/src/sgml/high-availability.sgml
@@ -591,7 +591,7 @@ protocol to make nodes agree on a serializable transactional order.
    <para>
     In standby mode, the server continuously applies WAL received from the
     master server. The standby server can read WAL from a WAL archive
-    (see <xref linkend="restore-command">) or directly from the master
+    (see <xref linkend="guc-restore-command">) or directly from the master
     over a TCP connection (streaming replication). The standby server will
     also attempt to restore any WAL found in the standby cluster's
     <filename>pg_xlog</> directory. That typically happens after a server
@@ -660,8 +660,8 @@ protocol to make nodes agree on a serializable transactional order.
    <para>
     To set up the standby server, restore the base backup taken from primary
     server (see <xref linkend="backup-pitr-recovery">). Create a recovery
-    command file <filename>recovery.conf</> in the standby's cluster data
-    directory, and turn on <varname>standby_mode</>. Set
+    trigger file <filename>recovery.trigger</> in the standby's cluster data
+    directory. Turn on <varname>standby_mode</> and set
     <varname>restore_command</> to a simple command to copy files from
     the WAL archive. If you plan to have multiple standby servers for high
     availability purposes, set <varname>recovery_target_timeline</> to
@@ -697,7 +697,7 @@ protocol to make nodes agree on a serializable transactional order.
 
    <para>
     If you're using a WAL archive, its size can be minimized using the <xref
-    linkend="archive-cleanup-command"> parameter to remove files that are no
+    linkend="guc-archive-cleanup-command"> parameter to remove files that are no
     longer required by the standby server.
     The <application>pg_archivecleanup</> utility is designed specifically to
     be used with <varname>archive_cleanup_command</> in typical single-standby
@@ -708,7 +708,7 @@ protocol to make nodes agree on a serializable transactional order.
    </para>
 
    <para>
-    A simple example of a <filename>recovery.conf</> is:
+    A simple example of standby settings is:
 <programlisting>
 standby_mode = 'on'
 primary_conninfo = 'host=192.168.1.50 port=5432 user=foo password=foopass'
@@ -766,8 +766,8 @@ archive_cleanup_command = 'pg_archivecleanup /path/to/archive %r'
     To use streaming replication, set up a file-based log-shipping standby
     server as described in <xref linkend="warm-standby">. The step that
     turns a file-based log-shipping standby into streaming replication
-    standby is setting <varname>primary_conninfo</> setting in the
-    <filename>recovery.conf</> file to point to the primary server. Set
+    standby is setting <varname>primary_conninfo</> to
+    point to the primary server. Set
     <xref linkend="guc-listen-addresses"> and authentication options
     (see <filename>pg_hba.conf</>) on the primary so that the standby server
     can connect to the <literal>replication</> pseudo-database on the primary
@@ -827,15 +827,14 @@ host    replication     foo             192.168.1.100/32        md5
     </para>
     <para>
      The host name and port number of the primary, connection user name,
-     and password are specified in the <filename>recovery.conf</> file.
+     and password are specified in <varname>primary_conninfo</>.
      The password can also be set in the <filename>~/.pgpass</> file on the
      standby (specify <literal>replication</> in the <replaceable>database</>
      field).
      For example, if the primary is running on host IP <literal>192.168.1.50</>,
      port <literal>5432</literal>, the account name for replication is
      <literal>foo</>, and the password is <literal>foopass</>, the administrator
-     can add the following line to the <filename>recovery.conf</> file on the
-     standby:
+     can set <varname>primary_conninfo</> on the standby like this:
 
 <programlisting>
 # The standby connects to the primary that is running on host 192.168.1.50
@@ -1387,8 +1386,8 @@ synchronous_standby_names = '2 (s1, s2, s3)'
    <para>
     To trigger failover of a log-shipping standby server,
     run <command>pg_ctl promote</> or create a trigger
-    file with the file name and path specified by the <varname>trigger_file</>
-    setting in <filename>recovery.conf</>. If you're planning to use
+    file with the file name and path specified by the <varname>trigger_file</>.
+    If you're planning to use
     <command>pg_ctl promote</> to fail over, <varname>trigger_file</> is
     not required. If you're setting up the reporting servers that are
     only used to offload read-only queries from the primary, not for high
@@ -1433,8 +1432,7 @@ synchronous_standby_names = '2 (s1, s2, s3)'
     The magic that makes the two loosely coupled servers work together is
     simply a <varname>restore_command</> used on the standby that,
     when asked for the next WAL file, waits for it to become available from
-    the primary. The <varname>restore_command</> is specified in the
-    <filename>recovery.conf</> file on the standby server. Normal recovery
+    the primary. Normal recovery
     processing would request a file from the WAL archive, reporting failure
     if the file was unavailable.  For standby processing it is normal for
     the next WAL file to be unavailable, so the standby must wait for
@@ -1521,8 +1519,14 @@ if (!triggered)
      </listitem>
      <listitem>
       <para>
+       Create a recovery trigger file <filename>recovery.trigger</>
+       in the standby's cluster data directory.
+      </para>
+     </listitem>
+     <listitem>
+      <para>
        Begin recovery on the standby server from the local WAL
-       archive, using a <filename>recovery.conf</> that specifies a
+       archive, specifying a
        <varname>restore_command</> that waits as described
        previously (see <xref linkend="backup-pitr-recovery">).
       </para>
@@ -2018,9 +2022,9 @@ if (!triggered)
    <title>Administrator's Overview</title>
 
    <para>
-    If <varname>hot_standby</> is turned <literal>on</> in
-    <filename>postgresql.conf</> and there is a <filename>recovery.conf</>
-    file present, the server will run in Hot Standby mode.
+    If <varname>hot_standby</> is turned <literal>on</>
+    and there is a recovery trigger file
+    <filename>recovery.trigger</> present, the server will run in Hot Standby mode.
     However, it may take some time for Hot Standby connections to be allowed,
     because the server will not accept connections until it has completed
     sufficient recovery to provide a consistent state against which queries
diff --git a/doc/src/sgml/pgstandby.sgml b/doc/src/sgml/pgstandby.sgml
index 80c6f60..c9e6185 100644
--- a/doc/src/sgml/pgstandby.sgml
+++ b/doc/src/sgml/pgstandby.sgml
@@ -46,8 +46,8 @@
 
   <para>
    To configure a standby
-   server to use <application>pg_standby</>, put this into its
-   <filename>recovery.conf</filename> configuration file:
+   server to use <application>pg_standby</>, specify
+   <xref linkend="guc-restore-command"> like this:
 <programlisting>
 restore_command = 'pg_standby <replaceable>archiveDir</> %f %p %r'
 </programlisting>
diff --git a/doc/src/sgml/postgres.sgml b/doc/src/sgml/postgres.sgml
index 0346d36..c47bc6b 100644
--- a/doc/src/sgml/postgres.sgml
+++ b/doc/src/sgml/postgres.sgml
@@ -155,7 +155,6 @@
   &maintenance;
   &backup;
   &high-availability;
-  &recovery-config;
   &monitoring;
   &diskusage;
   &wal;
diff --git a/doc/src/sgml/recovery-config.sgml b/doc/src/sgml/recovery-config.sgml
deleted file mode 100644
index 26af221..0000000
--- a/doc/src/sgml/recovery-config.sgml
+++ /dev/null
@@ -1,483 +0,0 @@
-<!-- doc/src/sgml/recovery-config.sgml -->
-
-<chapter id="recovery-config">
-  <title>Recovery Configuration</title>
-
-  <indexterm>
-   <primary>configuration</primary>
-   <secondary>of recovery</secondary>
-   <tertiary>of a standby server</tertiary>
-  </indexterm>
-
-   <para>
-    This chapter describes the settings available in the
-    <filename>recovery.conf</><indexterm><primary>recovery.conf</></>
-    file. They apply only for the duration of the
-    recovery.  They must be reset for any subsequent recovery you wish to
-    perform.  They cannot be changed once recovery has begun.
-   </para>
-
-   <para>
-     Settings in <filename>recovery.conf</> are specified in the format
-     <literal>name = 'value'</>. One parameter is specified per line.
-     Hash marks (<literal>#</literal>) designate the rest of the
-     line as a comment.  To embed a single quote in a parameter
-     value, write two quotes (<literal>''</>).
-   </para>
-
-   <para>
-    A sample file, <filename>share/recovery.conf.sample</>,
-    is provided in the installation's <filename>share/</> directory.
-   </para>
-
-  <sect1 id="archive-recovery-settings">
-
-    <title>Archive Recovery Settings</title>
-     <variablelist>
-
-     <varlistentry id="restore-command" xreflabel="restore_command">
-      <term><varname>restore_command</varname> (<type>string</type>)
-      <indexterm>
-        <primary><varname>restore_command</> recovery parameter</primary>
-      </indexterm>
-      </term>
-      <listitem>
-       <para>
-        The local shell command to execute to retrieve an archived segment of
-        the WAL file series. This parameter is required for archive recovery,
-        but optional for streaming replication.
-        Any <literal>%f</> in the string is
-        replaced by the name of the file to retrieve from the archive,
-        and any <literal>%p</> is replaced by the copy destination path name
-        on the server.
-        (The path name is relative to the current working directory,
-        i.e., the cluster's data directory.)
-        Any <literal>%r</> is replaced by the name of the file containing the
-        last valid restart point. That is the earliest file that must be kept
-        to allow a restore to be restartable, so this information can be used
-        to truncate the archive to just the minimum required to support
-        restarting from the current restore. <literal>%r</> is typically only
-        used by warm-standby configurations
-        (see <xref linkend="warm-standby">).
-        Write <literal>%%</> to embed an actual <literal>%</> character.
-       </para>
-
-       <para>
-        It is important for the command to return a zero exit status
-        only if it succeeds.  The command <emphasis>will</> be asked for file
-        names that are not present in the archive; it must return nonzero
-        when so asked.  Examples:
-<programlisting>
-restore_command = 'cp /mnt/server/archivedir/%f "%p"'
-restore_command = 'copy "C:\\server\\archivedir\\%f" "%p"'  # Windows
-</programlisting>
-        An exception is that if the command was terminated by a signal (other
-        than <systemitem>SIGTERM</systemitem>, which is used as part of a
-        database server shutdown) or an error by the shell (such as command
-        not found), then recovery will abort and the server will not start up.
-       </para>
-      </listitem>
-     </varlistentry>
-
-     <varlistentry id="archive-cleanup-command" xreflabel="archive_cleanup_command">
-      <term><varname>archive_cleanup_command</varname> (<type>string</type>)
-      <indexterm>
-        <primary><varname>archive_cleanup_command</> recovery parameter</primary>
-      </indexterm>
-      </term>
-      <listitem>
-       <para>
-        This optional parameter specifies a shell command that will be executed
-        at every restartpoint.  The purpose of
-        <varname>archive_cleanup_command</> is to provide a mechanism for
-        cleaning up old archived WAL files that are no longer needed by the
-        standby server.
-        Any <literal>%r</> is replaced by the name of the file containing the
-        last valid restart point.
-        That is the earliest file that must be <emphasis>kept</> to allow a
-        restore to be restartable, and so all files earlier than <literal>%r</>
-        may be safely removed.
-        This information can be used to truncate the archive to just the
-        minimum required to support restart from the current restore.
-        The <xref linkend="pgarchivecleanup"> module
-        is often used in <varname>archive_cleanup_command</> for
-        single-standby configurations, for example:
-<programlisting>archive_cleanup_command = 'pg_archivecleanup /mnt/server/archivedir %r'</programlisting>
-        Note however that if multiple standby servers are restoring from the
-        same archive directory, you will need to ensure that you do not delete
-        WAL files until they are no longer needed by any of the servers.
-        <varname>archive_cleanup_command</> would typically be used in a
-        warm-standby configuration (see <xref linkend="warm-standby">).
-        Write <literal>%%</> to embed an actual <literal>%</> character in the
-        command.
-       </para>
-       <para>
-        If the command returns a nonzero exit status then a warning log
-        message will be written.  An exception is that if the command was
-        terminated by a signal or an error by the shell (such as command not
-        found), a fatal error will be raised.
-       </para>
-      </listitem>
-     </varlistentry>
-
-     <varlistentry id="recovery-end-command" xreflabel="recovery_end_command">
-      <term><varname>recovery_end_command</varname> (<type>string</type>)
-      <indexterm>
-        <primary><varname>recovery_end_command</> recovery parameter</primary>
-      </indexterm>
-      </term>
-      <listitem>
-       <para>
-        This parameter specifies a shell command that will be executed once only
-        at the end of recovery. This parameter is optional. The purpose of the
-        <varname>recovery_end_command</> is to provide a mechanism for cleanup
-        following replication or recovery.
-        Any <literal>%r</> is replaced by the name of the file containing the
-        last valid restart point, like in <xref linkend="archive-cleanup-command">.
-       </para>
-       <para>
-        If the command returns a nonzero exit status then a warning log
-        message will be written and the database will proceed to start up
-        anyway.  An exception is that if the command was terminated by a
-        signal or an error by the shell (such as command not found), the
-        database will not proceed with startup.
-       </para>
-      </listitem>
-     </varlistentry>
-
-    </variablelist>
-
-  </sect1>
-
-  <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 used; if more than one of these
-      is specified in the configuration file, the last entry will be used.
-     </para>
-
-     <variablelist>
-     <varlistentry id="recovery-target" xreflabel="recovery_target">
-      <term><varname>recovery_target</varname><literal> = 'immediate'</literal>
-      <indexterm>
-        <primary><varname>recovery_target</> recovery parameter</primary>
-      </indexterm>
-      </term>
-      <listitem>
-       <para>
-        This parameter specifies that recovery should end as soon as a
-        consistent state is reached, i.e. 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>)
-      <indexterm>
-        <primary><varname>recovery_target_name</> recovery parameter</primary>
-      </indexterm>
-      </term>
-      <listitem>
-       <para>
-        This parameter specifies the named restore point (created with
-        <function>pg_create_restore_point()</>) to which recovery will proceed.
-       </para>
-      </listitem>
-     </varlistentry>
-
-     <varlistentry id="recovery-target-time" xreflabel="recovery_target_time">
-      <term><varname>recovery_target_time</varname> (<type>timestamp</type>)
-      <indexterm>
-        <primary><varname>recovery_target_time</> recovery parameter</primary>
-      </indexterm>
-      </term>
-      <listitem>
-       <para>
-        This parameter specifies the time stamp up to which recovery
-        will proceed.
-        The precise stopping point is also influenced by
-        <xref linkend="recovery-target-inclusive">.
-       </para>
-      </listitem>
-     </varlistentry>
-
-     <varlistentry id="recovery-target-xid" xreflabel="recovery_target_xid">
-      <term><varname>recovery_target_xid</varname> (<type>string</type>)
-      <indexterm>
-        <primary><varname>recovery_target_xid</> recovery parameter</primary>
-      </indexterm>
-      </term>
-      <listitem>
-       <para>
-        This parameter specifies the transaction ID up to which recovery
-        will proceed. Keep in mind
-        that while transaction IDs are assigned sequentially at transaction
-        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.
-        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">
-      <term><varname>recovery_target_inclusive</varname> (<type>boolean</type>)
-      <indexterm>
-        <primary><varname>recovery_target_inclusive</> recovery parameter</primary>
-      </indexterm>
-      </term>
-      <listitem>
-       <para>
-        Specifies whether to stop just after the specified recovery target
-        (<literal>true</literal>), or just before the recovery target
-        (<literal>false</literal>).
-        Applies when either <xref linkend="recovery-target-time">
-        or <xref linkend="recovery-target-xid"> is specified.
-        This setting controls whether transactions
-        having exactly the target commit time or ID, respectively, will
-        be included in the recovery.  Default is <literal>true</>.
-       </para>
-      </listitem>
-     </varlistentry>
-
-     <varlistentry id="recovery-target-timeline"
-                   xreflabel="recovery_target_timeline">
-      <term><varname>recovery_target_timeline</varname> (<type>string</type>)
-      <indexterm>
-        <primary><varname>recovery_target_timeline</> recovery parameter</primary>
-      </indexterm>
-      </term>
-      <listitem>
-       <para>
-        Specifies recovering into a particular timeline.  The default is
-        to recover along the same timeline that was current when the
-        base backup was taken. Setting this to <literal>latest</> recovers
-        to the latest timeline found in the archive, which is useful in
-        a standby server. Other than that you only need to set this parameter
-        in complex re-recovery situations, where you need to return to
-        a state that itself was reached after a point-in-time recovery.
-        See <xref linkend="backup-timelines"> for discussion.
-       </para>
-      </listitem>
-     </varlistentry>
-
-     <varlistentry id="recovery-target-action"
-                   xreflabel="recovery_target_action">
-      <term><varname>recovery_target_action</varname> (<type>enum</type>)
-      <indexterm>
-        <primary><varname>recovery_target_action</> recovery parameter</primary>
-      </indexterm>
-      </term>
-      <listitem>
-       <para>
-        Specifies what action the server should take once the recovery target is
-        reached. The default is <literal>pause</>, which means recovery will
-        be paused. <literal>promote</> means the recovery process will finish
-        and the server will start to accept connections.
-        Finally <literal>shutdown</> will stop the server after reaching the
-        recovery target.
-       </para>
-       <para>
-        The intended use of the <literal>pause</> setting is to allow queries
-        to be executed against the database to check if this recovery target
-        is the most desirable point for recovery.
-        The paused state can be resumed by
-        using <function>pg_xlog_replay_resume()</> (see
-        <xref linkend="functions-recovery-control-table">), which then
-        causes recovery to end. If this recovery target is not the
-        desired stopping point, then shut down the server, change the
-        recovery target settings to a later target and restart to
-        continue recovery.
-       </para>
-       <para>
-        The <literal>shutdown</> setting is useful to have the instance ready
-        at the exact replay point desired.  The instance will still be able to
-        replay more WAL records (and in fact will have to replay WAL records
-        since the last checkpoint next time it is started).
-       </para>
-       <para>
-        Note that because <filename>recovery.conf</> will not be renamed when
-        <varname>recovery_target_action</> is set to <literal>shutdown</>,
-        any subsequent start will end with immediate shutdown unless the
-        configuration is changed or the <filename>recovery.conf</> file is
-        removed manually.
-       </para>
-       <para>
-        This setting has no effect if no recovery target is set.
-        If <xref linkend="guc-hot-standby"> is not enabled, a setting of
-        <literal>pause</> will act the same as <literal>shutdown</>.
-       </para>
-      </listitem>
-     </varlistentry>
-
-    </variablelist>
-   </sect1>
-
-  <sect1 id="standby-settings">
-
-    <title>Standby Server Settings</title>
-     <variablelist>
-
-       <varlistentry id="standby-mode" xreflabel="standby_mode">
-        <term><varname>standby_mode</varname> (<type>boolean</type>)
-        <indexterm>
-          <primary><varname>standby_mode</> recovery parameter</primary>
-        </indexterm>
-        </term>
-        <listitem>
-         <para>
-          Specifies whether to start the <productname>PostgreSQL</> server as
-          a standby. If this parameter is <literal>on</>, the server will
-          not stop recovery when the end of archived WAL is reached, but
-          will keep trying to continue recovery by fetching new WAL segments
-          using <varname>restore_command</>
-          and/or by connecting to the primary server as specified by the
-          <varname>primary_conninfo</> setting.
-         </para>
-        </listitem>
-       </varlistentry>
-       <varlistentry id="primary-conninfo" xreflabel="primary_conninfo">
-        <term><varname>primary_conninfo</varname> (<type>string</type>)
-        <indexterm>
-          <primary><varname>primary_conninfo</> recovery parameter</primary>
-        </indexterm>
-        </term>
-        <listitem>
-         <para>
-          Specifies a connection string to be used for the standby server
-          to connect with the primary. This string is in the format
-          described in <xref linkend="libpq-connstring">. If any option is
-          unspecified in this string, then the corresponding environment
-          variable (see <xref linkend="libpq-envars">) is checked. If the
-          environment variable is not set either, then
-          defaults are used.
-         </para>
-         <para>
-          The connection string should specify the host name (or address)
-          of the primary server, as well as the port number if it is not
-          the same as the standby server's default.
-          Also specify a user name corresponding to a suitably-privileged role
-          on the primary (see
-          <xref linkend="streaming-replication-authentication">).
-          A password needs to be provided too, if the primary demands password
-          authentication.  It can be provided in the
-          <varname>primary_conninfo</varname> string, or in a separate
-          <filename>~/.pgpass</> file on the standby server (use
-          <literal>replication</> as the database name).
-          Do not specify a database name in the
-          <varname>primary_conninfo</varname> string.
-         </para>
-         <para>
-          This setting has no effect if <varname>standby_mode</> is <literal>off</>.
-         </para>
-        </listitem>
-       </varlistentry>
-       <varlistentry id="primary-slot-name" xreflabel="primary_slot_name">
-        <term><varname>primary_slot_name</varname> (<type>string</type>)
-        <indexterm>
-          <primary><varname>primary_slot_name</> recovery parameter</primary>
-        </indexterm>
-        </term>
-        <listitem>
-         <para>
-          Optionally specifies an existing replication slot to be used when
-          connecting to the primary via streaming replication to control
-          resource removal on the upstream node
-          (see <xref linkend="streaming-replication-slots">).
-          This setting has no effect if <varname>primary_conninfo</> is not
-          set.
-         </para>
-        </listitem>
-       </varlistentry>
-       <varlistentry id="trigger-file" xreflabel="trigger_file">
-        <term><varname>trigger_file</varname> (<type>string</type>)
-        <indexterm>
-          <primary><varname>trigger_file</> recovery parameter</primary>
-        </indexterm>
-        </term>
-        <listitem>
-         <para>
-          Specifies a trigger file whose presence ends recovery in the
-          standby.  Even if this value is not set, you can still promote
-          the standby using <command>pg_ctl promote</>.
-          This setting has no effect if <varname>standby_mode</> is <literal>off</>.
-         </para>
-        </listitem>
-       </varlistentry>
-
-     <varlistentry id="recovery-min-apply-delay" xreflabel="recovery_min_apply_delay">
-      <term><varname>recovery_min_apply_delay</varname> (<type>integer</type>)
-      <indexterm>
-        <primary><varname>recovery_min_apply_delay</> recovery parameter</primary>
-      </indexterm>
-      </term>
-      <listitem>
-       <para>
-        By default, a standby server restores WAL records from the
-        primary as soon as possible. It may be useful to have a time-delayed
-        copy of the data, offering opportunities to correct data loss errors.
-        This parameter allows you to delay recovery by a fixed period of time,
-        measured in milliseconds if no unit is specified.  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>
-        It is possible that the replication delay between servers exceeds the
-        value of this parameter, in which case no delay is added.
-        Note that the delay is calculated between the WAL time stamp as written
-        on master and the current time on the standby. Delays in transfer
-        because of network lag or cascading replication configurations
-        may reduce the actual wait time significantly. If the system
-        clocks on master and standby are not synchronized, this may lead to
-        recovery applying records earlier than expected; but that is not a
-        major issue because useful settings of this parameter are much larger
-        than typical time deviations between servers.
-       </para>
-       <para>
-        The delay occurs only on WAL records for transaction commits.
-        Other records are replayed as quickly as possible, which
-        is not a problem because MVCC visibility rules ensure their effects
-        are not visible until the corresponding commit record is applied.
-       </para>
-       <para>
-        The delay occurs once the database in recovery has reached a consistent
-        state, until the standby is promoted or triggered. After that the standby
-        will end recovery without further waiting.
-       </para>
-       <para>
-        This parameter is intended for use with streaming replication deployments;
-        however, if the parameter is specified it will be honored in all cases.
-        Synchronous replication is not affected by this setting because there is
-        not yet any setting to request synchronous apply of transaction commits.
-        <varname>hot_standby_feedback</> will be delayed by use of this feature
-        which could lead to bloat on the master; use both together with care.
-       </para>
-      </listitem>
-     </varlistentry>
-
-     </variablelist>
-   </sect1>
-
-</chapter>
diff --git a/doc/src/sgml/ref/pgarchivecleanup.sgml b/doc/src/sgml/ref/pgarchivecleanup.sgml
index abe01be..dc29854 100644
--- a/doc/src/sgml/ref/pgarchivecleanup.sgml
+++ b/doc/src/sgml/ref/pgarchivecleanup.sgml
@@ -38,8 +38,8 @@
 
   <para>
    To configure a standby
-   server to use <application>pg_archivecleanup</>, put this into its
-   <filename>recovery.conf</filename> configuration file:
+   server to use <application>pg_archivecleanup</>, specify
+   <xref linkend="guc-archive-cleanup-command"> like this:
 <programlisting>
 archive_cleanup_command = 'pg_archivecleanup <replaceable>archivelocation</> %r'
 </programlisting>
@@ -47,7 +47,7 @@ archive_cleanup_command = 'pg_archivecleanup <replaceable>archivelocation</> %r'
    files should be removed.
   </para>
   <para>
-   When used within <xref linkend="archive-cleanup-command">, all WAL files
+   When used within <varname>archive_cleanup_command</>, all WAL files
    logically preceding the value of the <literal>%r</> argument will be removed
    from <replaceable>archivelocation</>. This minimizes the number of files
    that need to be retained, while preserving crash-restart capability.  Use of
diff --git a/doc/src/sgml/release-9.1.sgml b/doc/src/sgml/release-9.1.sgml
index a66ca0d..afac09c 100644
--- a/doc/src/sgml/release-9.1.sgml
+++ b/doc/src/sgml/release-9.1.sgml
@@ -9601,7 +9601,7 @@ Branch: REL9_0_STABLE [9d6af7367] 2015-08-15 11:02:34 -0400
        <para>
         These named restore points can be specified as recovery
         targets using the new <filename>recovery.conf</> setting
-        <link linkend="recovery-target-name"><varname>recovery_target_name</></link>.
+        <link linkend="guc-recovery-target-name"><varname>recovery_target_name</></link>.
        </para>
       </listitem>
 
@@ -9633,8 +9633,7 @@ Branch: REL9_0_STABLE [9d6af7367] 2015-08-15 11:02:34 -0400
 
       <listitem>
        <para>
-        Allow <link
-        linkend="recovery-config"><filename>recovery.conf</></link>
+        Allow <filename>recovery.conf</>
         to use the same quoting behavior as <filename>postgresql.conf</>
         (Dimitri Fontaine)
        </para>
diff --git a/doc/src/sgml/release.sgml b/doc/src/sgml/release.sgml
index 472c1f6..4237a66 100644
--- a/doc/src/sgml/release.sgml
+++ b/doc/src/sgml/release.sgml
@@ -6,7 +6,7 @@ Typical markup:
 &<>                             use & escapes
 PostgreSQL                      <productname>
 postgresql.conf, pg_hba.conf,
-        recovery.conf           <filename>
+        recovery.trigger        <filename>
 [A-Z][A-Z_ ]+[A-Z_]             <command>, <literal>, <envar>, <acronym>
 [A-Za-z_][A-Za-z0-9_]+()        <function>
 -[-A-Za-z_]+                    <option>
diff --git a/src/backend/access/transam/recovery.conf.sample b/src/backend/access/transam/recovery.conf.sample
index b777400..49272d8 100644
--- a/src/backend/access/transam/recovery.conf.sample
+++ b/src/backend/access/transam/recovery.conf.sample
@@ -2,23 +2,14 @@
 # PostgreSQL recovery config file
 # -------------------------------
 #
-# Edit this file to provide the parameters that PostgreSQL needs to
-# perform an archive recovery of a database, or to act as a replication
-# standby.
+# PostgreSQL 9.2 or later, recovery.conf is no longer used. Instead,
+# specify all recovery parameters in postgresql.conf and create
+# recovery.trigger to enter archive recovery or standby mode.
 #
-# If "recovery.conf" is present in the PostgreSQL data directory, it is
-# read on postmaster startup.  After successful recovery, it is renamed
-# to "recovery.done" to ensure that we do not accidentally re-enter
-# archive recovery or standby mode.
+# If you must use recovery.conf, specify "include directives" in
+# postgresql.conf like this:
 #
-# This file consists of lines of the form:
-#
-#   name = value
-#
-# Comments are introduced with '#'.
-#
-# The complete list of option names and allowed values can be found
-# in the PostgreSQL documentation.
+#   include 'recovery.conf'
 #
 #---------------------------------------------------------------------------
 # ARCHIVE RECOVERY PARAMETERS
diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c
index 0b991bb..0bdbc3c 100644
--- a/src/backend/access/transam/xlog.c
+++ b/src/backend/access/transam/xlog.c
@@ -76,7 +76,7 @@
 extern uint32 bootstrap_data_checksum_version;
 
 /* File path names (all relative to $PGDATA) */
-#define RECOVERY_COMMAND_FILE	"recovery.conf"
+#define RECOVERY_COMMAND_READY	"recovery.trigger"
 #define RECOVERY_COMMAND_DONE	"recovery.done"
 #define PROMOTE_SIGNAL_FILE		"promote"
 #define FALLBACK_PROMOTE_SIGNAL_FILE "fallback_promote"
@@ -100,7 +100,24 @@ int			wal_level = WAL_LEVEL_MINIMAL;
 int			CommitDelay = 0;	/* precommit delay in microseconds */
 int			CommitSiblings = 5; /* # concurrent xacts needed to sleep */
 int			wal_retrieve_retry_interval = 5000;
-
+char	   *restore_command = NULL;
+char	   *archive_cleanup_command = NULL;
+char	   *recovery_end_command = NULL;
+bool		standby_mode = false;
+char	   *primary_conninfo = NULL;
+char	   *primary_slot_name = NULL;
+char	   *trigger_file = NULL;
+int			recovery_min_apply_delay = 0;
+RecoveryTargetType	recovery_target = RECOVERY_TARGET_UNSET;
+TransactionId	recovery_target_xid = InvalidTransactionId;
+TimestampTz	recovery_target_time = 0;
+char	   *recovery_target_name = NULL;
+bool		recovery_target_inclusive = true;
+bool		pause_at_recovery_target = true;
+char	   *recovery_target_timeline_string = NULL;
+TimeLineID	recovery_target_timeline = 0;
+RecoveryTargetAction recovery_target_action = RECOVERY_TARGET_ACTION_PAUSE;
+ 
 #ifdef WAL_DEBUG
 bool		XLOG_DEBUG = false;
 #endif
@@ -229,7 +246,7 @@ static int	LocalXLogInsertAllowed = -1;
 
 /*
  * When ArchiveRecoveryRequested is set, archive recovery was requested,
- * ie. recovery.conf file was present. When InArchiveRecovery is set, we are
+ * ie. recovery.trigger file was present. When InArchiveRecovery is set, we are
  * currently recovering using offline XLOG archives. These variables are only
  * valid in the startup process.
  *
@@ -244,28 +261,6 @@ bool		InArchiveRecovery = false;
 /* Was the last xlog file restored from archive, or local? */
 static bool restoredFromArchive = false;
 
-/* options taken from recovery.conf for archive recovery */
-char	   *recoveryRestoreCommand = NULL;
-static char *recoveryEndCommand = NULL;
-static char *archiveCleanupCommand = NULL;
-static RecoveryTargetType recoveryTarget = RECOVERY_TARGET_UNSET;
-static bool recoveryTargetInclusive = true;
-static RecoveryTargetAction recoveryTargetAction = RECOVERY_TARGET_ACTION_PAUSE;
-static TransactionId recoveryTargetXid;
-static TimestampTz recoveryTargetTime;
-static char *recoveryTargetName;
-static int	recovery_min_apply_delay = 0;
-static TimestampTz recoveryDelayUntilTime;
-
-/* options taken from recovery.conf for XLOG streaming */
-static bool StandbyModeRequested = false;
-static char *PrimaryConnInfo = NULL;
-static char *PrimarySlotName = NULL;
-static char *TriggerFile = NULL;
-
-/* are we currently in standby mode? */
-bool		StandbyMode = false;
-
 /* whether request for fast promotion has been made yet */
 static bool fast_promote = false;
 
@@ -273,11 +268,14 @@ static bool fast_promote = false;
  * if recoveryStopsBefore/After returns true, it saves information of the stop
  * point here
  */
+static RecoveryTargetType recoveryStopTarget;
 static TransactionId recoveryStopXid;
 static TimestampTz recoveryStopTime;
 static char recoveryStopName[MAXFNAMELEN];
 static bool recoveryStopAfter;
 
+static TimestampTz recoveryDelayUntilTime;
+
 /*
  * During normal operation, the only timeline we care about is ThisTimeLineID.
  * During recovery, however, things are more complicated.  To simplify life
@@ -579,10 +577,10 @@ typedef struct XLogCtlData
 	TimeLineID	PrevTimeLineID;
 
 	/*
-	 * archiveCleanupCommand is read from recovery.conf but needs to be in
-	 * shared memory so that the checkpointer process can access it.
+	 * archive_cleanup_command is read from recovery.conf but needs to
+	 * be in shared memory so that the checkpointer process can access it.
 	 */
-	char		archiveCleanupCommand[MAXPGPATH];
+	char		archive_cleanup_command[MAXPGPATH];
 
 	/*
 	 * SharedRecoveryInProgress indicates if we're still in crash or archive
@@ -785,7 +783,7 @@ static bool holdingAllLocks = false;
 static MemoryContext walDebugCxt = NULL;
 #endif
 
-static void readRecoveryCommandFile(void);
+static void CheckRecoveryReadyFile(void);
 static void exitArchiveRecovery(TimeLineID endTLI, XLogRecPtr endOfLog);
 static bool recoveryStopsBefore(XLogReaderState *record);
 static bool recoveryStopsAfter(XLogReaderState *record);
@@ -3980,7 +3978,7 @@ ReadRecord(XLogReaderState *xlogreader, XLogRecPtr RecPtr, int emode,
 			/*
 			 * We only end up here without a message when XLogPageRead()
 			 * failed - in that case we already logged something. In
-			 * StandbyMode that only happens if we have been triggered, so we
+			 * standby_mode that only happens if we have been triggered, so we
 			 * shouldn't loop anymore in that case.
 			 */
 			if (errormsg)
@@ -4038,8 +4036,6 @@ ReadRecord(XLogReaderState *xlogreader, XLogRecPtr RecPtr, int emode,
 				ereport(DEBUG1,
 						(errmsg_internal("reached end of WAL in pg_xlog, entering archive recovery")));
 				InArchiveRecovery = true;
-				if (StandbyModeRequested)
-					StandbyMode = true;
 
 				/* initialize minRecoveryPoint to this record */
 				LWLockAcquire(ControlFileLock, LW_EXCLUSIVE);
@@ -4069,7 +4065,7 @@ ReadRecord(XLogReaderState *xlogreader, XLogRecPtr RecPtr, int emode,
 			}
 
 			/* In standby mode, loop back to retry. Otherwise, give up. */
-			if (StandbyMode && !CheckForStandbyTrigger())
+			if (standby_mode && !CheckForStandbyTrigger())
 				continue;
 			else
 				return NULL;
@@ -4930,295 +4926,67 @@ str_time(pg_time_t tnow)
 }
 
 /*
- * See if there is a recovery command file (recovery.conf), and if so
- * read in parameters for archive recovery and XLOG streaming.
- *
- * The file is parsed using the main configuration parser.
+ * Check to see if there is a recovery trigger file (recovery.trigger),
+ * and if so validate recovery parameters and determine recovery target
+ * timeline.
  */
 static void
-readRecoveryCommandFile(void)
+CheckRecoveryReadyFile(void)
 {
-	FILE	   *fd;
-	TimeLineID	rtli = 0;
-	bool		rtliGiven = false;
-	ConfigVariable *item,
-			   *head = NULL,
-			   *tail = NULL;
-	bool		recoveryTargetActionSet = false;
-
-
-	fd = AllocateFile(RECOVERY_COMMAND_FILE, "r");
-	if (fd == NULL)
-	{
-		if (errno == ENOENT)
-			return;				/* not there, so no archive recovery */
-		ereport(FATAL,
-				(errcode_for_file_access(),
-				 errmsg("could not open recovery command file \"%s\": %m",
-						RECOVERY_COMMAND_FILE)));
-	}
-
-	/*
-	 * Since we're asking ParseConfigFp() to report errors as FATAL, there's
-	 * no need to check the return value.
-	 */
-	(void) ParseConfigFp(fd, RECOVERY_COMMAND_FILE, 0, FATAL, &head, &tail);
-
-	FreeFile(fd);
-
-	for (item = head; item; item = item->next)
-	{
-		if (strcmp(item->name, "restore_command") == 0)
-		{
-			recoveryRestoreCommand = pstrdup(item->value);
-			ereport(DEBUG2,
-					(errmsg_internal("restore_command = '%s'",
-									 recoveryRestoreCommand)));
-		}
-		else if (strcmp(item->name, "recovery_end_command") == 0)
-		{
-			recoveryEndCommand = pstrdup(item->value);
-			ereport(DEBUG2,
-					(errmsg_internal("recovery_end_command = '%s'",
-									 recoveryEndCommand)));
-		}
-		else if (strcmp(item->name, "archive_cleanup_command") == 0)
-		{
-			archiveCleanupCommand = pstrdup(item->value);
-			ereport(DEBUG2,
-					(errmsg_internal("archive_cleanup_command = '%s'",
-									 archiveCleanupCommand)));
-		}
-		else if (strcmp(item->name, "recovery_target_action") == 0)
-		{
-			if (strcmp(item->value, "pause") == 0)
-				recoveryTargetAction = RECOVERY_TARGET_ACTION_PAUSE;
-			else if (strcmp(item->value, "promote") == 0)
-				recoveryTargetAction = RECOVERY_TARGET_ACTION_PROMOTE;
-			else if (strcmp(item->value, "shutdown") == 0)
-				recoveryTargetAction = RECOVERY_TARGET_ACTION_SHUTDOWN;
-			else
-				ereport(ERROR,
-						(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
-				errmsg("invalid value for recovery parameter \"%s\": \"%s\"",
-					   "recovery_target_action",
-					   item->value),
-						 errhint("Valid values are \"pause\", \"promote\", and \"shutdown\".")));
-
-			ereport(DEBUG2,
-					(errmsg_internal("recovery_target_action = '%s'",
-									 item->value)));
-
-			recoveryTargetActionSet = true;
-		}
-		else if (strcmp(item->name, "recovery_target_timeline") == 0)
-		{
-			rtliGiven = true;
-			if (strcmp(item->value, "latest") == 0)
-				rtli = 0;
-			else
-			{
-				errno = 0;
-				rtli = (TimeLineID) strtoul(item->value, NULL, 0);
-				if (errno == EINVAL || errno == ERANGE)
-					ereport(FATAL,
-							(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
-							 errmsg("recovery_target_timeline is not a valid number: \"%s\"",
-									item->value)));
-			}
-			if (rtli)
-				ereport(DEBUG2,
-				   (errmsg_internal("recovery_target_timeline = %u", rtli)));
-			else
-				ereport(DEBUG2,
-					 (errmsg_internal("recovery_target_timeline = latest")));
-		}
-		else if (strcmp(item->name, "recovery_target_xid") == 0)
-		{
-			errno = 0;
-			recoveryTargetXid = (TransactionId) strtoul(item->value, NULL, 0);
-			if (errno == EINVAL || errno == ERANGE)
-				ereport(FATAL,
-						(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
-				  errmsg("recovery_target_xid is not a valid number: \"%s\"",
-						 item->value)));
-			ereport(DEBUG2,
-					(errmsg_internal("recovery_target_xid = %u",
-									 recoveryTargetXid)));
-			recoveryTarget = RECOVERY_TARGET_XID;
-		}
-		else if (strcmp(item->name, "recovery_target_time") == 0)
-		{
-			recoveryTarget = RECOVERY_TARGET_TIME;
-
-			/*
-			 * Convert the time string given by the user to TimestampTz form.
-			 */
-			recoveryTargetTime =
-				DatumGetTimestampTz(DirectFunctionCall3(timestamptz_in,
-												CStringGetDatum(item->value),
-												ObjectIdGetDatum(InvalidOid),
-														Int32GetDatum(-1)));
-			ereport(DEBUG2,
-					(errmsg_internal("recovery_target_time = '%s'",
-								   timestamptz_to_str(recoveryTargetTime))));
-		}
-		else if (strcmp(item->name, "recovery_target_name") == 0)
-		{
-			recoveryTarget = RECOVERY_TARGET_NAME;
-
-			recoveryTargetName = pstrdup(item->value);
-			if (strlen(recoveryTargetName) >= MAXFNAMELEN)
-				ereport(FATAL,
-						(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
-						 errmsg("recovery_target_name is too long (maximum %d characters)",
-								MAXFNAMELEN - 1)));
+	struct stat stat_buf;
 
-			ereport(DEBUG2,
-					(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 value for recovery parameter \"%s\": \"%s\"",
-					   "recovery_target",
-					   item->value),
-					   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)
-		{
-			/*
-			 * does nothing if a recovery_target is not also set
-			 */
-			if (!parse_bool(item->value, &recoveryTargetInclusive))
-				ereport(ERROR,
-						(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
-						 errmsg("parameter \"%s\" requires a Boolean value",
-								"recovery_target_inclusive")));
-			ereport(DEBUG2,
-					(errmsg_internal("recovery_target_inclusive = %s",
-									 item->value)));
-		}
-		else if (strcmp(item->name, "standby_mode") == 0)
-		{
-			if (!parse_bool(item->value, &StandbyModeRequested))
-				ereport(ERROR,
-						(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
-						 errmsg("parameter \"%s\" requires a Boolean value",
-								"standby_mode")));
-			ereport(DEBUG2,
-					(errmsg_internal("standby_mode = '%s'", item->value)));
-		}
-		else if (strcmp(item->name, "primary_conninfo") == 0)
-		{
-			PrimaryConnInfo = pstrdup(item->value);
-			ereport(DEBUG2,
-					(errmsg_internal("primary_conninfo = '%s'",
-									 PrimaryConnInfo)));
-		}
-		else if (strcmp(item->name, "primary_slot_name") == 0)
-		{
-			ReplicationSlotValidateName(item->value, ERROR);
-			PrimarySlotName = pstrdup(item->value);
-			ereport(DEBUG2,
-					(errmsg_internal("primary_slot_name = '%s'",
-									 PrimarySlotName)));
-		}
-		else if (strcmp(item->name, "trigger_file") == 0)
-		{
-			TriggerFile = pstrdup(item->value);
-			ereport(DEBUG2,
-					(errmsg_internal("trigger_file = '%s'",
-									 TriggerFile)));
-		}
-		else if (strcmp(item->name, "recovery_min_apply_delay") == 0)
-		{
-			const char *hintmsg;
+	if (stat(RECOVERY_COMMAND_READY, &stat_buf) != 0)
+		return;		/* not there, so no archive recovery */
 
-			if (!parse_int(item->value, &recovery_min_apply_delay, GUC_UNIT_MS,
-						   &hintmsg))
-				ereport(ERROR,
-						(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
-						 errmsg("parameter \"%s\" requires a temporal value",
-								"recovery_min_apply_delay"),
-						 hintmsg ? errhint("%s", _(hintmsg)) : 0));
-			ereport(DEBUG2,
-					(errmsg_internal("recovery_min_apply_delay = '%s'", item->value)));
-		}
-		else
-			ereport(FATAL,
-					(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
-					 errmsg("unrecognized recovery parameter \"%s\"",
-							item->name)));
-	}
+	/* Enable fetching from archive recovery area */
+	ArchiveRecoveryRequested = true;
 
 	/*
 	 * Check for compulsory parameters
 	 */
-	if (StandbyModeRequested)
-	{
-		if (PrimaryConnInfo == NULL && recoveryRestoreCommand == NULL)
-			ereport(WARNING,
-					(errmsg("recovery command file \"%s\" specified neither primary_conninfo nor restore_command",
-							RECOVERY_COMMAND_FILE),
-					 errhint("The database server will regularly poll the pg_xlog subdirectory to check for files placed there.")));
-	}
-	else
-	{
-		if (recoveryRestoreCommand == NULL)
-			ereport(FATAL,
-					(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
-					 errmsg("recovery command file \"%s\" must specify restore_command when standby mode is not enabled",
-							RECOVERY_COMMAND_FILE)));
-	}
+	if (standby_mode && !restore_command[0] && !primary_conninfo[0])
+		ereport(WARNING,
+				(errmsg("neither primary_conninfo nor restore_command is specified"),
+				 errhint("The database server will regularly poll the pg_xlog subdirectory to "
+						 "check for files placed there until either of them is set in postgresql.conf.")));
+
+	CheckRestoreCommandSet();
 
 	/*
 	 * Override any inconsistent requests. Not that this is a change of
 	 * behaviour in 9.5; prior to this we simply ignored a request to pause if
 	 * hot_standby = off, which was surprising behaviour.
 	 */
-	if (recoveryTargetAction == RECOVERY_TARGET_ACTION_PAUSE &&
-		recoveryTargetActionSet &&
+	if (recovery_target_action == RECOVERY_TARGET_ACTION_PAUSE &&
 		!EnableHotStandby)
-		recoveryTargetAction = RECOVERY_TARGET_ACTION_SHUTDOWN;
+		recovery_target_action = RECOVERY_TARGET_ACTION_SHUTDOWN;
 
 	/*
 	 * We don't support standby_mode in standalone backends; that requires
 	 * other processes such as the WAL receiver to be alive.
 	 */
-	if (StandbyModeRequested && !IsUnderPostmaster)
+	if (standby_mode && !IsUnderPostmaster)
 		ereport(FATAL,
 				(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
 			errmsg("standby mode is not supported by single-user servers")));
 
-	/* Enable fetching from archive recovery area */
-	ArchiveRecoveryRequested = true;
-
 	/*
 	 * If user specified recovery_target_timeline, validate it or compute the
 	 * "latest" value.  We can't do this until after we've gotten the restore
 	 * command and set InArchiveRecovery, because we need to fetch timeline
 	 * history files from the archive.
 	 */
-	if (rtliGiven)
+	if (strcmp(recovery_target_timeline_string, "") != 0)
 	{
-		if (rtli)
+		if (recovery_target_timeline)
 		{
 			/* Timeline 1 does not have a history file, all else should */
-			if (rtli != 1 && !existsTimeLineHistory(rtli))
+			if (recovery_target_timeline != 1 && !existsTimeLineHistory(recovery_target_timeline))
 				ereport(FATAL,
 						(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
 						 errmsg("recovery target timeline %u does not exist",
-								rtli)));
-			recoveryTargetTLI = rtli;
+								recovery_target_timeline)));
+			recoveryTargetTLI = recovery_target_timeline;
 			recoveryTargetIsLatest = false;
 		}
 		else
@@ -5228,8 +4996,6 @@ readRecoveryCommandFile(void)
 			recoveryTargetIsLatest = true;
 		}
 	}
-
-	FreeConfigVariables(head);
 }
 
 /*
@@ -5334,7 +5100,7 @@ exitArchiveRecovery(TimeLineID endTLI, XLogRecPtr endOfLog)
 	 * re-enter archive recovery mode in a subsequent crash.
 	 */
 	unlink(RECOVERY_COMMAND_DONE);
-	durable_rename(RECOVERY_COMMAND_FILE, RECOVERY_COMMAND_DONE, FATAL);
+	durable_rename(RECOVERY_COMMAND_READY, RECOVERY_COMMAND_DONE, FATAL);
 
 	ereport(LOG,
 			(errmsg("archive recovery complete")));
@@ -5393,7 +5159,7 @@ recoveryStopsBefore(XLogReaderState *record)
 	TransactionId recordXid;
 
 	/* Check if we should stop as soon as reaching consistency */
-	if (recoveryTarget == RECOVERY_TARGET_IMMEDIATE && reachedConsistency)
+	if (recovery_target == RECOVERY_TARGET_IMMEDIATE && reachedConsistency)
 	{
 		ereport(LOG,
 				(errmsg("recovery stopping after reaching consistency")));
@@ -5446,7 +5212,7 @@ recoveryStopsBefore(XLogReaderState *record)
 	else
 		return false;
 
-	if (recoveryTarget == RECOVERY_TARGET_XID && !recoveryTargetInclusive)
+	if (recovery_target == RECOVERY_TARGET_XID && !recovery_target_inclusive)
 	{
 		/*
 		 * There can be only one transaction end record with this exact
@@ -5457,10 +5223,10 @@ recoveryStopsBefore(XLogReaderState *record)
 		 * they complete. A higher numbered xid will complete before you about
 		 * 50% of the time...
 		 */
-		stopsHere = (recordXid == recoveryTargetXid);
+		stopsHere = (recordXid == recovery_target_xid);
 	}
 
-	if (recoveryTarget == RECOVERY_TARGET_TIME &&
+	if (recovery_target == RECOVERY_TARGET_TIME &&
 		getRecordTimestamp(record, &recordXtime))
 	{
 		/*
@@ -5468,14 +5234,15 @@ recoveryStopsBefore(XLogReaderState *record)
 		 * we stop after the last one, if we are inclusive, or stop at the
 		 * first one if we are exclusive
 		 */
-		if (recoveryTargetInclusive)
-			stopsHere = (recordXtime > recoveryTargetTime);
+		if (recovery_target_inclusive)
+			stopsHere = (recordXtime > recovery_target_time);
 		else
-			stopsHere = (recordXtime >= recoveryTargetTime);
+			stopsHere = (recordXtime >= recovery_target_time);
 	}
 
 	if (stopsHere)
 	{
+		recoveryStopTarget = recovery_target;
 		recoveryStopAfter = false;
 		recoveryStopXid = recordXid;
 		recoveryStopTime = recordXtime;
@@ -5521,14 +5288,14 @@ recoveryStopsAfter(XLogReaderState *record)
 	 * There can be many restore points that share the same name; we stop at
 	 * the first one.
 	 */
-	if (recoveryTarget == RECOVERY_TARGET_NAME &&
+	if (recovery_target == RECOVERY_TARGET_NAME &&
 		rmid == RM_XLOG_ID && info == XLOG_RESTORE_POINT)
 	{
 		xl_restore_point *recordRestorePointData;
 
 		recordRestorePointData = (xl_restore_point *) XLogRecGetData(record);
 
-		if (strcmp(recordRestorePointData->rp_name, recoveryTargetName) == 0)
+		if (strcmp(recordRestorePointData->rp_name, recovery_target_name) == 0)
 		{
 			recoveryStopAfter = true;
 			recoveryStopXid = InvalidTransactionId;
@@ -5592,8 +5359,8 @@ recoveryStopsAfter(XLogReaderState *record)
 		 * they complete. A higher numbered xid will complete before you about
 		 * 50% of the time...
 		 */
-		if (recoveryTarget == RECOVERY_TARGET_XID && recoveryTargetInclusive &&
-			recordXid == recoveryTargetXid)
+		if (recovery_target == RECOVERY_TARGET_XID && recovery_target_inclusive &&
+			recordXid == recovery_target_xid)
 		{
 			recoveryStopAfter = true;
 			recoveryStopXid = recordXid;
@@ -5621,7 +5388,7 @@ recoveryStopsAfter(XLogReaderState *record)
 	}
 
 	/* Check if we should stop as soon as reaching consistency */
-	if (recoveryTarget == RECOVERY_TARGET_IMMEDIATE && reachedConsistency)
+	if (recovery_target == RECOVERY_TARGET_IMMEDIATE && reachedConsistency)
 	{
 		ereport(LOG,
 				(errmsg("recovery stopping after reaching consistency")));
@@ -5917,6 +5684,19 @@ CheckRequiredParameterValues(void)
 }
 
 /*
+ * Check to see if restore_command must be set for archive recovery
+ * when standby mode is not enabled
+ */
+void
+CheckRestoreCommandSet(void)
+{
+	if (InArchiveRecovery && !standby_mode && !restore_command[0])
+		ereport(FATAL,
+				(errmsg("restore_command must be specified for archive recovery "
+						"when standby mode is not enabled")));
+}
+
+/*
  * This must be called ONCE during postmaster or standalone-backend startup
  */
 void
@@ -6025,39 +5805,16 @@ StartupXLOG(void)
 		recoveryTargetTLI = ControlFile->checkPointCopy.ThisTimeLineID;
 
 	/*
-	 * Check for recovery control file, and if so set up state for offline
+	 * Check for recovery trigger file, and if so set up state for offline
 	 * recovery
 	 */
-	readRecoveryCommandFile();
-
-	/*
-	 * Save archive_cleanup_command in shared memory so that other processes
-	 * can see it.
-	 */
-	strlcpy(XLogCtl->archiveCleanupCommand,
-			archiveCleanupCommand ? archiveCleanupCommand : "",
-			sizeof(XLogCtl->archiveCleanupCommand));
+	CheckRecoveryReadyFile();
 
 	if (ArchiveRecoveryRequested)
 	{
-		if (StandbyModeRequested)
+		if (standby_mode)
 			ereport(LOG,
 					(errmsg("entering standby mode")));
-		else if (recoveryTarget == RECOVERY_TARGET_XID)
-			ereport(LOG,
-					(errmsg("starting point-in-time recovery to XID %u",
-							recoveryTargetXid)));
-		else if (recoveryTarget == RECOVERY_TARGET_TIME)
-			ereport(LOG,
-					(errmsg("starting point-in-time recovery to %s",
-							timestamptz_to_str(recoveryTargetTime))));
-		else if (recoveryTarget == RECOVERY_TARGET_NAME)
-			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")));
@@ -6067,7 +5824,7 @@ StartupXLOG(void)
 	 * Take ownership of the wakeup latch if we're going to sleep during
 	 * recovery.
 	 */
-	if (StandbyModeRequested)
+	if (standby_mode)
 		OwnLatch(&XLogCtl->recoveryWakeupLatch);
 
 	/* Set up XLOG reader facility */
@@ -6091,8 +5848,6 @@ StartupXLOG(void)
 		 * archive recovery directly.
 		 */
 		InArchiveRecovery = true;
-		if (StandbyModeRequested)
-			StandbyMode = true;
 
 		/*
 		 * When a backup_label file is present, we want to roll forward from
@@ -6201,7 +5956,7 @@ StartupXLOG(void)
 		 * This can happen for example if a base backup is taken from a
 		 * running server using an atomic filesystem snapshot, without calling
 		 * pg_start/stop_backup. Or if you just kill a running master server
-		 * and put it into archive recovery by creating a recovery.conf file.
+		 * and put it into archive recovery by creating a recovery.trigger file.
 		 *
 		 * Our strategy in that case is to perform crash recovery first,
 		 * replaying all the WAL present in pg_xlog, and only enter archive
@@ -6218,8 +5973,6 @@ StartupXLOG(void)
 			 ControlFile->state == DB_SHUTDOWNED))
 		{
 			InArchiveRecovery = true;
-			if (StandbyModeRequested)
-				StandbyMode = true;
 		}
 
 		/*
@@ -6235,7 +5988,7 @@ StartupXLOG(void)
 					(errmsg("checkpoint record is at %X/%X",
 				   (uint32) (checkPointLoc >> 32), (uint32) checkPointLoc)));
 		}
-		else if (StandbyMode)
+		else if (standby_mode)
 		{
 			/*
 			 * The last valid checkpoint record required for a streaming
@@ -6431,7 +6184,7 @@ StartupXLOG(void)
 
 	/*
 	 * Check whether we need to force recovery from WAL.  If it appears to
-	 * have been a clean shutdown and we did not have a recovery.conf file,
+	 * have been a clean shutdown and we did not have a recovery.trigger file,
 	 * then assume no recovery needed.
 	 */
 	if (checkPoint.redo < RecPtr)
@@ -6445,7 +6198,7 @@ StartupXLOG(void)
 		InRecovery = true;
 	else if (ArchiveRecoveryRequested)
 	{
-		/* force recovery due to presence of recovery.conf */
+		/* force recovery due to presence of recovery.trigger */
 		InRecovery = true;
 	}
 
@@ -6954,7 +6707,7 @@ StartupXLOG(void)
 				 * this, Resource Managers may choose to do permanent
 				 * corrective actions at end of recovery.
 				 */
-				switch (recoveryTargetAction)
+				switch (recovery_target_action)
 				{
 					case RECOVERY_TARGET_ACTION_SHUTDOWN:
 
@@ -7023,7 +6776,7 @@ StartupXLOG(void)
 	 * We don't need the latch anymore. It's not strictly necessary to disown
 	 * it, but let's do it for the sake of tidiness.
 	 */
-	if (StandbyModeRequested)
+	if (standby_mode)
 		DisownLatch(&XLogCtl->recoveryWakeupLatch);
 
 	/*
@@ -7031,7 +6784,7 @@ StartupXLOG(void)
 	 * recovery to force fetching the files (which would be required at end of
 	 * recovery, e.g., timeline history file) from archive or pg_xlog.
 	 */
-	StandbyMode = false;
+	SetConfigOption("standby_mode", "false", PGC_POSTMASTER, PGC_S_OVERRIDE);
 
 	/*
 	 * Re-fetch the last valid or last applied record, so we can identify the
@@ -7114,21 +6867,21 @@ StartupXLOG(void)
 		 * Create a comment for the history file to explain why and where
 		 * timeline changed.
 		 */
-		if (recoveryTarget == RECOVERY_TARGET_XID)
+		if (recoveryStopTarget == RECOVERY_TARGET_XID)
 			snprintf(reason, sizeof(reason),
 					 "%s transaction %u",
 					 recoveryStopAfter ? "after" : "before",
 					 recoveryStopXid);
-		else if (recoveryTarget == RECOVERY_TARGET_TIME)
+		else if (recoveryStopTarget == RECOVERY_TARGET_TIME)
 			snprintf(reason, sizeof(reason),
 					 "%s %s\n",
 					 recoveryStopAfter ? "after" : "before",
 					 timestamptz_to_str(recoveryStopTime));
-		else if (recoveryTarget == RECOVERY_TARGET_NAME)
+		else if (recoveryStopTarget == RECOVERY_TARGET_NAME)
 			snprintf(reason, sizeof(reason),
 					 "at restore point \"%s\"",
 					 recoveryStopName);
-		else if (recoveryTarget == RECOVERY_TARGET_IMMEDIATE)
+		else if (recoveryStopTarget == RECOVERY_TARGET_IMMEDIATE)
 			snprintf(reason, sizeof(reason), "reached consistency");
 		else
 			snprintf(reason, sizeof(reason), "no recovery target specified");
@@ -7273,8 +7026,8 @@ StartupXLOG(void)
 		/*
 		 * And finally, execute the recovery_end_command, if any.
 		 */
-		if (recoveryEndCommand)
-			ExecuteRecoveryCommand(recoveryEndCommand,
+		if (recovery_end_command)
+			ExecuteRecoveryCommand(recovery_end_command,
 								   "recovery_end_command",
 								   true);
 	}
@@ -8921,8 +8674,8 @@ CreateRestartPoint(int flags)
 	/*
 	 * Finally, execute archive_cleanup_command, if any.
 	 */
-	if (XLogCtl->archiveCleanupCommand[0])
-		ExecuteRecoveryCommand(XLogCtl->archiveCleanupCommand,
+	if (archive_cleanup_command[0])
+		ExecuteRecoveryCommand(archive_cleanup_command,
 							   "archive_cleanup_command",
 							   false);
 
@@ -11103,7 +10856,7 @@ next_record_is_invalid:
 	readSource = 0;
 
 	/* In standby-mode, keep trying */
-	if (StandbyMode)
+	if (standby_mode)
 		goto retry;
 	else
 		return -1;
@@ -11189,7 +10942,7 @@ WaitForWALToBecomeAvailable(XLogRecPtr RecPtr, bool randAccess,
 					 * trigger file, we still finish replaying as much as we
 					 * can from archive and pg_xlog before failover.
 					 */
-					if (StandbyMode && CheckForStandbyTrigger())
+					if (standby_mode && CheckForStandbyTrigger())
 					{
 						ShutdownWalRcv();
 						return false;
@@ -11199,7 +10952,7 @@ WaitForWALToBecomeAvailable(XLogRecPtr RecPtr, bool randAccess,
 					 * Not in standby mode, and we've now tried the archive
 					 * and pg_xlog.
 					 */
-					if (!StandbyMode)
+					if (!standby_mode)
 						return false;
 
 					/*
@@ -11212,7 +10965,7 @@ WaitForWALToBecomeAvailable(XLogRecPtr RecPtr, bool randAccess,
 					 * that when we later jump backwards to start redo at
 					 * RedoStartLSN, we will have the logs streamed already.
 					 */
-					if (PrimaryConnInfo)
+					if (primary_conninfo)
 					{
 						XLogRecPtr	ptr;
 						TimeLineID	tli;
@@ -11233,8 +10986,8 @@ WaitForWALToBecomeAvailable(XLogRecPtr RecPtr, bool randAccess,
 									 tli, curFileTLI);
 						}
 						curFileTLI = tli;
-						RequestXLogStreaming(tli, ptr, PrimaryConnInfo,
-											 PrimarySlotName);
+						RequestXLogStreaming(tli, ptr, primary_conninfo,
+											 primary_slot_name);
 						receivedUpto = 0;
 					}
 
@@ -11562,14 +11315,14 @@ CheckForStandbyTrigger(void)
 		return true;
 	}
 
-	if (TriggerFile == NULL)
+	if (trigger_file == NULL)
 		return false;
 
-	if (stat(TriggerFile, &stat_buf) == 0)
+	if (stat(trigger_file, &stat_buf) == 0)
 	{
 		ereport(LOG,
-				(errmsg("trigger file found: %s", TriggerFile)));
-		unlink(TriggerFile);
+				(errmsg("trigger file found: %s", trigger_file)));
+		unlink(trigger_file);
 		triggered = true;
 		fast_promote = true;
 		return true;
@@ -11578,7 +11331,7 @@ CheckForStandbyTrigger(void)
 		ereport(ERROR,
 				(errcode_for_file_access(),
 				 errmsg("could not stat trigger file \"%s\": %m",
-						TriggerFile)));
+						trigger_file)));
 
 	return false;
 }
diff --git a/src/backend/access/transam/xlogarchive.c b/src/backend/access/transam/xlogarchive.c
index d153a44..5c4533d 100644
--- a/src/backend/access/transam/xlogarchive.c
+++ b/src/backend/access/transam/xlogarchive.c
@@ -67,7 +67,7 @@ RestoreArchivedFile(char *path, const char *xlogfname,
 	TimeLineID	restartTli;
 
 	/* In standby mode, restore_command might not be supplied */
-	if (recoveryRestoreCommand == NULL)
+	if (restore_command == NULL)
 		goto not_available;
 
 	/*
@@ -150,7 +150,7 @@ RestoreArchivedFile(char *path, const char *xlogfname,
 	endp = xlogRestoreCmd + MAXPGPATH - 1;
 	*endp = '\0';
 
-	for (sp = recoveryRestoreCommand; *sp; sp++)
+	for (sp = restore_command; *sp; sp++)
 	{
 		if (*sp == '%')
 		{
@@ -236,7 +236,7 @@ RestoreArchivedFile(char *path, const char *xlogfname,
 				 * incorrectly conclude we've reached the end of WAL and we're
 				 * done recovering ...
 				 */
-				if (StandbyMode && stat_buf.st_size < expectedSize)
+				if (standby_mode && stat_buf.st_size < expectedSize)
 					elevel = DEBUG1;
 				else
 					elevel = FATAL;
@@ -409,7 +409,7 @@ ExecuteRecoveryCommand(char *command, char *commandName, bool failOnSignal)
 
 		ereport((signaled && failOnSignal) ? FATAL : WARNING,
 		/*------
-		   translator: First %s represents a recovery.conf parameter name like
+		   translator: First %s represents a postgresql.conf parameter name like
 		  "recovery_end_command", the 2nd is the value of that parameter, the
 		  third an already translated error message. */
 				(errmsg("%s \"%s\": %s", commandName,
diff --git a/src/backend/commands/extension.c b/src/backend/commands/extension.c
index 518fefc..1c8e725 100644
--- a/src/backend/commands/extension.c
+++ b/src/backend/commands/extension.c
@@ -9,7 +9,7 @@
  * dependent objects can be associated with it.  An extension is created by
  * populating the pg_extension catalog from a "control" file.
  * The extension control file is parsed with the same parser we use for
- * postgresql.conf and recovery.conf.  An extension also has an installation
+ * postgresql.conf.  An extension also has an installation
  * script file, containing SQL commands to create the extension's objects.
  *
  * Portions Copyright (c) 1996-2016, PostgreSQL Global Development Group
diff --git a/src/backend/postmaster/startup.c b/src/backend/postmaster/startup.c
index a7ae7e3..1a6adac 100644
--- a/src/backend/postmaster/startup.c
+++ b/src/backend/postmaster/startup.c
@@ -153,6 +153,8 @@ HandleStartupProcInterrupts(void)
 	{
 		got_SIGHUP = false;
 		ProcessConfigFile(PGC_SIGHUP);
+
+		CheckRestoreCommandSet();
 	}
 
 	/*
diff --git a/src/backend/replication/walreceiver.c b/src/backend/replication/walreceiver.c
index 413ee3a..ec2f252 100644
--- a/src/backend/replication/walreceiver.c
+++ b/src/backend/replication/walreceiver.c
@@ -415,9 +415,33 @@ WalReceiverMain(void)
 
 				if (got_SIGHUP)
 				{
+					char	*conninfo = pstrdup(primary_conninfo);
+					char	*slotname = pstrdup(primary_slot_name);
+
 					got_SIGHUP = false;
 					ProcessConfigFile(PGC_SIGHUP);
 					XLogWalRcvSendHSFeedback(true);
+
+					/*
+					 * If primary_conninfo has been changed while walreceiver is running,
+					 * shut down walreceiver so that a new walreceiver is started and
+					 * initiates replication with the new primary_conninfo.
+					 */
+					if (strcmp(conninfo, primary_conninfo) != 0)
+						ereport(FATAL,
+								(errcode(ERRCODE_ADMIN_SHUTDOWN),
+								 errmsg("terminating walreceiver process because primary_conninfo was changed")));
+
+					/*
+					 * And the same for primary_slot_name.
+					 */
+					if (strcmp(slotname, primary_slot_name) != 0)
+						ereport(FATAL,
+								(errcode(ERRCODE_ADMIN_SHUTDOWN),
+								 errmsg("terminating walreceiver process because primary_slot_name was changed")));
+
+					pfree(conninfo);
+					pfree(slotname);
 				}
 
 				/* See if we can read data immediately */
diff --git a/src/backend/utils/misc/guc-file.l b/src/backend/utils/misc/guc-file.l
index dae5015..37c7316 100644
--- a/src/backend/utils/misc/guc-file.l
+++ b/src/backend/utils/misc/guc-file.l
@@ -206,6 +206,21 @@ ProcessConfigFileInternal(GucContext context, bool applySettings, int elevel)
 			ConfFileWithError = PG_AUTOCONF_FILENAME;
 			goto bail_out;
 		}
+
+                /*
+                 * For backwards compatibility, we also read recovery.conf if
+                 * it exists.
+                 */
+                
+		if (!ParseConfigFile("recovery.conf", false,
+							 NULL, 0, 0, elevel,
+							 &head, &tail))
+		{
+			/* Syntax error(s) detected in the file, so bail out */
+			error = true;
+			ConfFileWithError = "recovery.conf";
+			goto bail_out;
+		}
 	}
 	else
 	{
diff --git a/src/backend/utils/misc/guc.c b/src/backend/utils/misc/guc.c
index c5178f7..729b56a 100644
--- a/src/backend/utils/misc/guc.c
+++ b/src/backend/utils/misc/guc.c
@@ -31,6 +31,7 @@
 #include "access/transam.h"
 #include "access/twophase.h"
 #include "access/xact.h"
+#include "access/xlog_internal.h"
 #include "catalog/namespace.h"
 #include "commands/async.h"
 #include "commands/prepare.h"
@@ -181,6 +182,16 @@ static void assign_application_name(const char *newval, void *extra);
 static bool check_cluster_name(char **newval, void **extra, GucSource source);
 static const char *show_unix_socket_permissions(void);
 static const char *show_log_file_mode(void);
+static bool check_recovery_target_xid(char **newval, void **extra, GucSource source);
+static void assign_recovery_target_xid(const char *newval, void *extra);
+static bool check_recovery_target_name(char **newval, void **extra, GucSource source);
+static void assign_recovery_target_name(const char *newval, void *extra);
+static bool check_recovery_target_time(char **newval, void **extra, GucSource source);
+static void assign_recovery_target_time(const char *newval, void *extra);
+static bool check_recovery_target_timeline(char **newval, void **extra, GucSource source);
+static void assign_recovery_target_timeline(const char *newval, void *extra);
+static bool check_recovery_target_action(char **newval, void **extra, GucSource source);
+static void assign_recovery_target_action(const char *newval, void *extra);
 
 /* Private functions in guc-file.l that need to be called from guc.c */
 static ConfigVariable *ProcessConfigFileInternal(GucContext context,
@@ -495,6 +506,9 @@ static bool data_checksums;
 static int	wal_segment_size;
 static bool integer_datetimes;
 static bool assert_enabled;
+static char *recovery_target_xid_string;
+static char *recovery_target_time_string;
+static char *recovery_target_action_string;
 
 /* should be static, but commands/variable.c needs to get at this */
 char	   *role_string;
@@ -576,6 +590,10 @@ const char *const config_group_names[] =
 	gettext_noop("Write-Ahead Log / Checkpoints"),
 	/* WAL_ARCHIVING */
 	gettext_noop("Write-Ahead Log / Archiving"),
+	/* WAL_ARCHIVE_RECOVERY */
+	gettext_noop("Write-Ahead Log / Archive Recovery"),
+	/* WAL_RECOVERY_TARGET */
+	gettext_noop("Write-Ahead Log / Recovery Target"),
 	/* REPLICATION */
 	gettext_noop("Replication"),
 	/* REPLICATION_SENDING */
@@ -1553,6 +1571,36 @@ static struct config_bool ConfigureNamesBool[] =
 	},
 
 	{
+		{"recovery_target_inclusive", PGC_SIGHUP, WAL_RECOVERY_TARGET,
+			gettext_noop("Sets whether to include or exclude transaction with recovery target."),
+			NULL
+		},
+		&recovery_target_inclusive,
+		true,
+		NULL, NULL, NULL
+	},
+
+	{
+		{"pause_at_recovery_target", PGC_SIGHUP, WAL_RECOVERY_TARGET,
+			gettext_noop("Sets whether recovery should pause when the recovery target is reached."),
+			NULL
+		},
+		&pause_at_recovery_target,
+		true,
+		NULL, NULL, NULL
+	},
+
+	{
+		{"standby_mode", PGC_POSTMASTER, REPLICATION_STANDBY,
+			gettext_noop("Sets whether to start the server as a standby."),
+			NULL
+		},
+		&standby_mode,
+		false,
+		NULL, NULL, NULL
+	},
+
+	{
 		{"hot_standby", PGC_POSTMASTER, REPLICATION_STANDBY,
 			gettext_noop("Allows connections and queries during recovery."),
 			NULL
@@ -1793,6 +1841,17 @@ static struct config_int ConfigureNamesInt[] =
 	},
 
 	{
+		{"recovery_min_apply_delay", PGC_SIGHUP, WAL_RECOVERY_TARGET,
+			gettext_noop("Sets the minimum delay to apply changes during recovery."),
+			NULL,
+			GUC_UNIT_MS
+		},
+		&recovery_min_apply_delay,
+		0, 0, INT_MAX,
+		NULL, NULL, NULL
+	},
+
+	{
 		{"wal_receiver_status_interval", PGC_SIGHUP, REPLICATION_STANDBY,
 			gettext_noop("Sets the maximum interval between WAL receiver status reports to the primary."),
 			NULL,
@@ -2992,6 +3051,119 @@ static struct config_string ConfigureNamesString[] =
 	},
 
 	{
+		{"restore_command", PGC_SIGHUP, WAL_ARCHIVE_RECOVERY,
+			gettext_noop("Sets the shell command that will retrieve an archived WAL file."),
+			NULL
+		},
+		&restore_command,
+		"",
+		NULL, NULL, NULL
+	},
+
+	{
+		{"archive_cleanup_command", PGC_SIGHUP, WAL_ARCHIVE_RECOVERY,
+			gettext_noop("Sets the shell command that will be executed at every restartpoint."),
+			NULL
+		},
+		&archive_cleanup_command,
+		"",
+		NULL, NULL, NULL
+	},
+
+	{
+		{"recovery_end_command", PGC_SIGHUP, WAL_ARCHIVE_RECOVERY,
+			gettext_noop("Sets the shell command that will be executed once only at the end of recovery."),
+			NULL
+		},
+		&recovery_end_command,
+		"",
+		NULL, NULL, NULL
+	},
+
+	{
+		{"recovery_target_xid", PGC_SIGHUP, WAL_RECOVERY_TARGET,
+			gettext_noop("Sets the transaction ID up to which recovery will proceed."),
+			NULL
+		},
+		&recovery_target_xid_string,
+		"",
+		check_recovery_target_xid, assign_recovery_target_xid, NULL
+	},
+
+	{
+		{"recovery_target_name", PGC_SIGHUP, WAL_RECOVERY_TARGET,
+			gettext_noop("Sets the named restore point."),
+			NULL
+		},
+		&recovery_target_name,
+		"",
+		check_recovery_target_name, assign_recovery_target_name, NULL
+	},
+
+	{
+		{"recovery_target_time", PGC_SIGHUP, WAL_RECOVERY_TARGET,
+			gettext_noop("Sets the time stamp up to which recovery will proceed."),
+			NULL
+		},
+		&recovery_target_time_string,
+		"",
+		check_recovery_target_time, assign_recovery_target_time, NULL
+	},
+
+	{
+		{"recovery_target_timeline", PGC_POSTMASTER, WAL_RECOVERY_TARGET,
+			gettext_noop("Sets recoverying into a particular timeline."),
+			NULL
+		},
+		&recovery_target_timeline_string,
+		"",
+		check_recovery_target_timeline, assign_recovery_target_timeline, NULL
+	},
+
+	{
+		{"recovery_target_action", PGC_POSTMASTER, WAL_RECOVERY_TARGET,
+			gettext_noop("Sets the action to perform upon reaching the recovery target."),
+			NULL
+		},
+		&recovery_target_action_string,
+		"",
+		check_recovery_target_action, assign_recovery_target_action, NULL
+	},
+
+	{
+		{"primary_conninfo", PGC_SIGHUP, REPLICATION_STANDBY,
+			gettext_noop("Sets the connection string to be used to connect with the primary."),
+			NULL,
+			GUC_SUPERUSER_ONLY
+		},
+		&primary_conninfo,
+		"",
+		NULL, NULL, NULL
+	},
+
+	{
+		{"primary_slot_name", PGC_SIGHUP, REPLICATION_STANDBY,
+			gettext_noop("Sets the name of the replication slot to use on the primary."),
+			NULL,
+			GUC_SUPERUSER_ONLY
+		},
+		&primary_slot_name,
+		"",
+		NULL, NULL, NULL
+		/* XXX: Should be validated with ReplicationSlotValidateName() */
+	},
+
+	{
+		{"trigger_file", PGC_SIGHUP, REPLICATION_STANDBY,
+			gettext_noop("Sets the trigger file whose presence ends recovery in the standby."),
+			NULL
+		},
+		&trigger_file,
+		"",
+		NULL, NULL, NULL
+	},
+
+	{
 		{"client_encoding", PGC_USERSET, CLIENT_CONN_LOCALE,
 			gettext_noop("Sets the client's character set encoding."),
 			NULL,
@@ -10301,4 +10473,189 @@ show_log_file_mode(void)
 	return buf;
 }
 
+static bool
+check_recovery_target_xid(char **newval, void **extra, GucSource source)
+{
+	TransactionId   xid;
+	TransactionId   *myextra;
+
+	if (strcmp(*newval, "") == 0)
+		xid = InvalidTransactionId;
+	else
+	{
+		errno = 0;
+		xid = (TransactionId) strtoul(*newval, NULL, 0);
+		if (errno == EINVAL || errno == ERANGE)
+		{
+			GUC_check_errdetail("recovery_target_xid is not a valid number: \"%s\"",
+								*newval);
+			return false;
+		}
+	}
+
+	myextra = (TransactionId *) guc_malloc(ERROR, sizeof(TransactionId));
+	*myextra = xid;
+	*extra = (void *) myextra;
+
+	return true;
+}
+
+static void
+assign_recovery_target_xid(const char *newval, void *extra)
+{
+	recovery_target_xid = *((TransactionId *) extra);
+
+	if (recovery_target_xid != InvalidTransactionId)
+		recovery_target = RECOVERY_TARGET_XID;
+	else if (recovery_target_name[0])
+		recovery_target = RECOVERY_TARGET_NAME;
+	else if (recovery_target_time != 0)
+		recovery_target = RECOVERY_TARGET_TIME;
+	else
+		recovery_target = RECOVERY_TARGET_UNSET;
+}
+
+static bool
+check_recovery_target_name(char **newval, void **extra, GucSource source)
+{
+	if (strlen(*newval) >= MAXFNAMELEN)
+	{
+		GUC_check_errdetail("\"recovery_target_name\" is too long (maximum %d characters)",
+							MAXFNAMELEN - 1);
+		return false;
+	}
+	return true;
+}
+
+static void
+assign_recovery_target_name(const char *newval, void *extra)
+{
+	if (recovery_target_xid != InvalidTransactionId)
+		recovery_target = RECOVERY_TARGET_XID;
+	else if (newval[0])
+		recovery_target = RECOVERY_TARGET_NAME;
+	else if (recovery_target_time != 0)
+		recovery_target = RECOVERY_TARGET_TIME;
+	else
+		recovery_target = RECOVERY_TARGET_UNSET;
+}
+
+static bool
+check_recovery_target_time(char **newval, void **extra, GucSource source)
+{
+	TimestampTz     time;
+	TimestampTz     *myextra;
+	MemoryContext oldcontext = CurrentMemoryContext;
+
+	PG_TRY();
+	{
+		time = (strcmp(*newval, "") == 0) ?
+			0 :
+			DatumGetTimestampTz(DirectFunctionCall3(timestamptz_in,
+													CStringGetDatum(*newval),
+													ObjectIdGetDatum(InvalidOid),
+													Int32GetDatum(-1)));
+	}
+	PG_CATCH();
+	{
+		ErrorData  *edata;
+
+		/* Save error info */
+		MemoryContextSwitchTo(oldcontext);
+		edata = CopyErrorData();
+		FlushErrorState();
+
+		/* Pass the error message */
+		GUC_check_errdetail("%s", edata->message);
+		FreeErrorData(edata);
+		return false;
+	}
+	PG_END_TRY();
+
+	myextra = (TimestampTz *) guc_malloc(ERROR, sizeof(TimestampTz));
+	*myextra = time;
+	*extra = (void *) myextra;
+
+	return true;
+}
+
+static void
+assign_recovery_target_time(const char *newval, void *extra)
+{
+	recovery_target_time = *((TimestampTz *) extra);
+
+	if (recovery_target_xid != InvalidTransactionId)
+		recovery_target = RECOVERY_TARGET_XID;
+	else if (recovery_target_name[0])
+		recovery_target = RECOVERY_TARGET_NAME;
+	else if (recovery_target_time != 0)
+		recovery_target = RECOVERY_TARGET_TIME;
+	else
+		recovery_target = RECOVERY_TARGET_UNSET;
+}
+
+static bool
+check_recovery_target_timeline(char **newval, void **extra, GucSource source)
+{
+	TimeLineID	tli = 0;
+	TimeLineID	*myextra;
+
+	if (strcmp(*newval, "") == 0 || strcmp(*newval, "latest") == 0)
+		tli = 0;
+	else
+	{
+		errno = 0;
+		tli = (TimeLineID) strtoul(*newval, NULL, 0);
+		if (errno == EINVAL || errno == ERANGE)
+		{
+			GUC_check_errdetail("recovery_target_timeline is not a valid number: \"%s\"",
+								*newval);
+			return false;
+		}
+	}
+
+	myextra = (TimeLineID *) guc_malloc(ERROR, sizeof(TimeLineID));
+	*myextra = tli;
+	*extra = (void *) myextra;
+
+	return true;
+}
+
+static void
+assign_recovery_target_timeline(const char *newval, void *extra)
+{
+	recovery_target_timeline = *((TimeLineID *) extra);
+}
+
+static bool
+check_recovery_target_action(char **newval, void **extra, GucSource source)
+{
+	RecoveryTargetAction rta = RECOVERY_TARGET_ACTION_PAUSE;
+	RecoveryTargetAction *myextra;
+
+	if (strcmp(*newval, "pause") == 0)
+		rta = RECOVERY_TARGET_ACTION_PAUSE;
+	else if (strcmp(*newval, "promote") == 0)
+		rta = RECOVERY_TARGET_ACTION_PROMOTE;
+	else if (strcmp(*newval, "shutdown") == 0)
+		rta = RECOVERY_TARGET_ACTION_SHUTDOWN;
+	else
+	{
+		GUC_check_errdetail("recovery_target_action is not valid: \"%s\"", *newval);
+		return false;
+	}
+
+	myextra = (RecoveryTargetAction *) guc_malloc(ERROR, sizeof(RecoveryTargetAction));
+	*myextra = rta;
+	*extra = (void *) myextra;
+
+	return true;
+}
+
+static void
+assign_recovery_target_action(const char *newval, void *extra)
+{
+	recovery_target_action = *((RecoveryTargetAction *) extra);
+}
+
 #include "guc-file.c"
diff --git a/src/backend/utils/misc/postgresql.conf.sample b/src/backend/utils/misc/postgresql.conf.sample
index 6d0666c..2bbc83c 100644
--- a/src/backend/utils/misc/postgresql.conf.sample
+++ b/src/backend/utils/misc/postgresql.conf.sample
@@ -222,6 +222,23 @@
 #archive_timeout = 0		# force a logfile segment switch after this
 				# number of seconds; 0 disables
 
+# - Archive Recovery -
+#restore_command = ''		# command to use to restore an archived logfile segment
+				# placeholders: %p = path of file to restore
+				#               %f = file name only
+				# e.g. 'cp /mnt/server/archivedir/%f %p'
+#archive_cleanup_command = ''	# command to execute at every restartpoint
+#recovery_end_command = ''	# command to execute at completion of recovery
+
+# - Recovery Target -
+#recovery_target_xid = ''
+#recovery_target_name = ''
+#recovery_target_time = ''
+#recovery_target_inclusive = on
+#pause_at_recovery_target = on
+#recovery_target_timeline = ''		# timeline ID or 'latest'
+					# (change requires restart)
+
 
 #------------------------------------------------------------------------------
 # REPLICATION
@@ -254,6 +271,10 @@
 
 # These settings are ignored on a master server.
 
+#standby_mode = off			# "on" starts the server as a standby
+					# (change requires restart)
+#primary_conninfo = ''			# connection string to connect to the master
+#trigger_file = ''			# trigger file to promote the standby
 #hot_standby = off			# "on" allows queries during recovery
 					# (change requires restart)
 #max_standby_archive_delay = 30s	# max delay before canceling queries
diff --git a/src/bin/pg_archivecleanup/pg_archivecleanup.c b/src/bin/pg_archivecleanup/pg_archivecleanup.c
index 319038f..abbe367 100644
--- a/src/bin/pg_archivecleanup/pg_archivecleanup.c
+++ b/src/bin/pg_archivecleanup/pg_archivecleanup.c
@@ -270,7 +270,7 @@ usage(void)
 	printf("  -x EXT         clean up files if they have this extension\n");
 	printf("  -?, --help     show this help, then exit\n");
 	printf("\n"
-		   "For use as archive_cleanup_command in recovery.conf when standby_mode = on:\n"
+		   "For use as archive_cleanup_command in postgresql.conf when standby_mode = on:\n"
 		   "  archive_cleanup_command = 'pg_archivecleanup [OPTION]... ARCHIVELOCATION %%r'\n"
 		   "e.g.\n"
 		   "  archive_cleanup_command = 'pg_archivecleanup /mnt/server/archiverdir %%r'\n");
diff --git a/src/include/access/xlog.h b/src/include/access/xlog.h
index 14b7f7f..1879090 100644
--- a/src/include/access/xlog.h
+++ b/src/include/access/xlog.h
@@ -105,6 +105,22 @@ extern bool fullPageWrites;
 extern bool wal_log_hints;
 extern bool wal_compression;
 extern bool log_checkpoints;
+extern char *restore_command;
+extern char *archive_cleanup_command;
+extern char *recovery_end_command;
+extern bool standby_mode;
+extern char *primary_conninfo;
+extern char *primary_slot_name;
+extern char *trigger_file;
+extern RecoveryTargetType recovery_target;
+extern TransactionId recovery_target_xid;
+extern TimestampTz recovery_target_time;
+extern char *recovery_target_name;
+extern bool recovery_target_inclusive;
+extern bool pause_at_recovery_target;
+extern char *recovery_target_timeline_string;
+extern TimeLineID recovery_target_timeline;
+extern int recovery_min_apply_delay;
 
 extern int	CheckPointSegments;
 
@@ -267,6 +283,7 @@ extern void RemovePromoteSignalFiles(void);
 extern bool CheckPromoteSignal(void);
 extern void WakeupRecovery(void);
 extern void SetWalWriterSleeping(bool sleeping);
+extern void CheckRestoreCommandSet(void);
 
 extern void XLogRequestWalReceiverReply(void);
 
diff --git a/src/include/access/xlog_internal.h b/src/include/access/xlog_internal.h
index 0a595cc..e872bbe 100644
--- a/src/include/access/xlog_internal.h
+++ b/src/include/access/xlog_internal.h
@@ -294,8 +294,9 @@ extern void GetOldestRestartPoint(XLogRecPtr *oldrecptr, TimeLineID *oldtli);
  */
 extern bool ArchiveRecoveryRequested;
 extern bool InArchiveRecovery;
-extern bool StandbyMode;
-extern char *recoveryRestoreCommand;
+extern bool standby_mode;
+extern char *restore_command;
+extern RecoveryTargetAction recovery_target_action;
 
 /*
  * Prototypes for functions in xlogarchive.c
diff --git a/src/include/utils/guc_tables.h b/src/include/utils/guc_tables.h
index b695f61..7f4389c 100644
--- a/src/include/utils/guc_tables.h
+++ b/src/include/utils/guc_tables.h
@@ -68,6 +68,8 @@ enum config_group
 	WAL_SETTINGS,
 	WAL_CHECKPOINTS,
 	WAL_ARCHIVING,
+	WAL_ARCHIVE_RECOVERY,
+	WAL_RECOVERY_TARGET,
 	REPLICATION,
 	REPLICATION_SENDING,
 	REPLICATION_MASTER,
#3Michael Paquier
michael.paquier@gmail.com
In reply to: Simon Riggs (#1)
Re: Proposal for changes to recovery.conf API

On Thu, Sep 1, 2016 at 1:15 AM, Simon Riggs <simon@2ndquadrant.com> wrote:

This is a summary of proposed changes to the recovery.conf API for
v10. These are based in part on earlier discussions, and represent a
minimal modification to current usage.

Proposed changes (with reference to patches from Abhijit Menon-Sen and myself)

* pg_ctl start -M (normal|recover|continue)
pg_ctl can now start the server directly as a standby, similar to the
way pg_ctl promote works. The internal implementation is also similar,
with pg_ctl writing a "recovery.trigger" file that initiates recovery
in the same way recovery.conf used to do. It is still possible to
manually add a file called "recovery.trigger" and have that work if
users desire that mechanism.
Different startup methods can be selected with the <option>-M</option>
option. <quote>Normal</quote> mode starts the server for read/write,
overriding any previous use in Recover mode.
<quote>Recover</quote> mode starts the server as a standby server which
expects to receive changes from a primary/master server using physical
streaming replication or is used for
performing a recovery from backup. <quote>Continue</quote> mode is
the default and will startup the server in whatever mode it was in at
last proper shutdown, or as modified by any trigger files present.
(Patch: recovery_startup_r10_api.v1b.patch)

So you basically just set ArchiveRecoveryRequested based on the
presence of recovery.trigger instead of recovery.conf. And the
interface of pg_ctl:
- recover mode => creates recovery.trigger
- continue mode => does nothing
- normal mode => removes recovery.trigger
That looks like a sound design.

* Recovery parameters would now be part of the main postgresql.conf
infrastructure
Any parameters set in $DATADIR/recovery.conf will be read after the
main parameter file, similar to the way that postgresql.conf.auto is
read.
(Abhijit)
* pg_basebackup -R will continue to generate a parameter file called
recovery.conf as it does now, but will also create a file named
recovery.trigger.
(This part is WIP; patch doesn't include implementation for tar format yet)

Or we could just throw away this dependency with recovery.conf,
simply. I see no need to keep it if recovery is triggered depending on
recovery.trigger, nor recommend its use. We could instead add
include_if_exists 'recovery.conf' at the bottom of postgresql.conf and
let the infrastructure do the rest to simplify the patch.

* Parameters
All of the parameters formerly set in recovery.conf can be set in
postgresql.conf using RELOAD
These parameters will have no defaults in postgresql.conf.sample
Setting them has no effect during normal running, or once recovery ends.
https://www.postgresql.org/docs/devel/static/archive-recovery-settings.html
https://www.postgresql.org/docs/devel/static/recovery-target-settings.html
https://www.postgresql.org/docs/devel/static/standby-settings.html
(Abhijit)

Hm. I think that what would make sense here is a new GUC category,
meaning that once recovery is launched the new parameters are not
taken into account once again. Even updating primary_conninfo would
need a restart of the WAL receiver, so we could just make them
GUC_POSTMASTER and be done with it.

Related cleanup
* Promotion signal file is now called "promote.trigger" rather than
just "promote"

If that's not strictly necessary this renaming is not mandatory.

* Remove user configurable "trigger_file" mechanism - use
"promote.trigger" for all cases

Ugh. I am -1 on that. There are likely backup tools and users that
rely on this option, particularly to be able to trigger promotion
using a file that is on a different partition than PGDATA.

* Remove Fallback promote mechanism, so all promotion is now "fast" in xlog.c

No problem with that. Now others have surely other opinions. That
could be addressed as a separate patch.
--
Michael

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#4Michael Banck
mbanck@gmx.net
In reply to: Simon Riggs (#1)
Re: Proposal for changes to recovery.conf API

On Wed, Aug 31, 2016 at 05:15:59PM +0100, Simon Riggs wrote:

<quote>Recover</quote> mode starts the server as a standby server which
expects to receive changes from a primary/master server using physical
streaming replication or is used for performing a recovery from
backup.

I understand where this is coming from, but wouldn't "standby",
"replication" or something be more generally understood than "recover"?
Streaming replication got bolted on to recovery, but that seems like an
implementation detail by now.

Michael

--
Michael Banck
Projektleiter / Senior Berater
Tel.: +49 2166 9901-171
Fax: +49 2166 9901-100
Email: michael.banck@credativ.de

credativ GmbH, HRB M�nchengladbach 12080
USt-ID-Nummer: DE204566209
Trompeterallee 108, 41189 M�nchengladbach
Gesch�ftsf�hrung: Dr. Michael Meskes, J�rg Folz, Sascha Heuer

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#5Michael Paquier
michael.paquier@gmail.com
In reply to: Abhijit Menon-Sen (#2)
Re: Proposal for changes to recovery.conf API

On Thu, Sep 1, 2016 at 4:01 AM, Abhijit Menon-Sen <ams@2ndquadrant.com> wrote:

At 2016-08-31 17:15:59 +0100, simon@2ndquadrant.com wrote:

* Recovery parameters would now be part of the main postgresql.conf
infrastructure
Any parameters set in $DATADIR/recovery.conf will be read after the
main parameter file, similar to the way that postgresql.conf.auto is
read.
(Abhijit)

* Parameters
All of the parameters formerly set in recovery.conf can be set in
postgresql.conf using RELOAD
These parameters will have no defaults in postgresql.conf.sample
Setting them has no effect during normal running, or once recovery ends.
https://www.postgresql.org/docs/devel/static/archive-recovery-settings.html
https://www.postgresql.org/docs/devel/static/recovery-target-settings.html
https://www.postgresql.org/docs/devel/static/standby-settings.html
(Abhijit)

I've attached a WIP patch for the above (recovery_guc_v20160831.patch).
This was based on the unite_recoveryconf_postgresqlconf_v3.patch posted
by Fujii Masao.

Unfortunately, some parts conflict with the patch that Simon just posted
(e.g., his patch removes trigger_file altogether, whereas mine converts
it into a GUC along the lines of the original patch). Rather than trying
to untangle that right now, I'm posting what I have as-is, and I'll post
an updated version tomorrow.

- else if (recoveryTarget == RECOVERY_TARGET_XID)
- ereport(LOG,
- (errmsg("starting point-in-time recovery to XID %u",
- recoveryTargetXid)));
User loses information if those logs are removed.

+       {"recovery_min_apply_delay", PGC_SIGHUP, WAL_RECOVERY_TARGET,
+           gettext_noop("Sets the minimum delay to apply changes
during recovery."),
+           NULL,
+           GUC_UNIT_MS
+       },
What's the point of having them all as SIGHUP? The startup process
does not reload GUC parameters with ProcessConfigFile(), it works on
recoveryWakeupLatch though. I'd rather let them as PGC_POSTMASTER to
begin with as that's the safest approach, and then get a second patch
that processes ProcessConfigFile in the startup process and switch
some of them to PGC_SIGHUP. Recovery targets should not be SIGHUP, but
recovery_min_apply_delay applies definitely applies to that, as well
as archive_cleanup_command or restore_command.
+static void
+assign_recovery_target_name(const char *newval, void *extra)
+{
+   if (recovery_target_xid != InvalidTransactionId)
+       recovery_target = RECOVERY_TARGET_XID;
+   else if (newval[0])
+       recovery_target = RECOVERY_TARGET_NAME;
+   else if (recovery_target_time != 0)
+       recovery_target = RECOVERY_TARGET_TIME;
+   else
+       recovery_target = RECOVERY_TARGET_UNSET;
+}
That's brittle to put that in the GUC machinery... The recovery target
type should be determined only once, if archive recovery is wanted at
the beginning of the startup process once and for all, and just be set
up within the startup process. If multiple recovery_target_*
parameters are set, we should just define the target type in order of
priority, instead of the-last-one-wins that is currently present.
-- 
Michael

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#6Abhijit Menon-Sen
ams@2ndQuadrant.com
In reply to: Michael Paquier (#5)
Re: Proposal for changes to recovery.conf API

At 2016-09-01 15:51:11 +0900, michael.paquier@gmail.com wrote:

- (errmsg("starting point-in-time recovery to XID %u",
- recoveryTargetXid)));
User loses information if those logs are removed.

Agreed. I'm revising the patch right now, and I decided to leave them.
I'll consider and comment on the remainder of your points after I've
posted an update. Thanks for reading.

-- Abhijit

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#7Simon Riggs
simon@2ndquadrant.com
In reply to: Michael Paquier (#3)
Re: Proposal for changes to recovery.conf API

On 1 September 2016 at 06:34, Michael Paquier <michael.paquier@gmail.com> wrote:

On Thu, Sep 1, 2016 at 1:15 AM, Simon Riggs <simon@2ndquadrant.com> wrote:

This is a summary of proposed changes to the recovery.conf API for
v10. These are based in part on earlier discussions, and represent a
minimal modification to current usage.

Proposed changes (with reference to patches from Abhijit Menon-Sen and myself)

* pg_ctl start -M (normal|recover|continue)

...

That looks like a sound design.

Thanks

* Recovery parameters would now be part of the main postgresql.conf
infrastructure
Any parameters set in $DATADIR/recovery.conf will be read after the
main parameter file, similar to the way that postgresql.conf.auto is
read.
(Abhijit)
* pg_basebackup -R will continue to generate a parameter file called
recovery.conf as it does now, but will also create a file named
recovery.trigger.
(This part is WIP; patch doesn't include implementation for tar format yet)

Or we could just throw away this dependency with recovery.conf,
simply. I see no need to keep it if recovery is triggered depending on
recovery.trigger, nor recommend its use. We could instead add
include_if_exists 'recovery.conf' at the bottom of postgresql.conf and
let the infrastructure do the rest to simplify the patch.

It works exactly the same as ALTER SYSTEM and adds only one small hunk of code.

* Parameters
All of the parameters formerly set in recovery.conf can be set in
postgresql.conf using RELOAD
These parameters will have no defaults in postgresql.conf.sample
Setting them has no effect during normal running, or once recovery ends.
https://www.postgresql.org/docs/devel/static/archive-recovery-settings.html
https://www.postgresql.org/docs/devel/static/recovery-target-settings.html
https://www.postgresql.org/docs/devel/static/standby-settings.html
(Abhijit)

Hm. I think that what would make sense here is a new GUC category,
meaning that once recovery is launched the new parameters are not
taken into account once again. Even updating primary_conninfo would
need a restart of the WAL receiver, so we could just make them
GUC_POSTMASTER and be done with it.

We definitely want most of them set at RELOAD, especially recovery targets.

Almost all of them will have no effect when normal mode starts, so I
don't see any other special handling needed.

Related cleanup
* Promotion signal file is now called "promote.trigger" rather than
just "promote"

If that's not strictly necessary this renaming is not mandatory.

I think it makes sense to keep both files with same suffix, for clarity.

* Remove user configurable "trigger_file" mechanism - use
"promote.trigger" for all cases

Ugh. I am -1 on that. There are likely backup tools and users that
rely on this option, particularly to be able to trigger promotion
using a file that is on a different partition than PGDATA.

OK, I wasn't thinking of that. Perhaps we should have a trigger
directory parameter?

* Remove Fallback promote mechanism, so all promotion is now "fast" in xlog.c

No problem with that. Now others have surely other opinions. That
could be addressed as a separate patch.

I'll post separate patch.

--
Simon Riggs http://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#8Simon Riggs
simon@2ndquadrant.com
In reply to: Abhijit Menon-Sen (#2)
Re: Proposal for changes to recovery.conf API

On 31 August 2016 at 20:01, Abhijit Menon-Sen <ams@2ndquadrant.com> wrote:

Unfortunately, some parts conflict with the patch that Simon just posted
(e.g., his patch removes trigger_file altogether, whereas mine converts
it into a GUC along the lines of the original patch). Rather than trying
to untangle that right now, I'm posting what I have as-is, and I'll post
an updated version tomorrow.

Thanks.

archive_cleanup_command no longer needs to be in shmem. Checkpointer
will have its own copy of the value.

--
Simon Riggs http://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#9Abhijit Menon-Sen
ams@2ndQuadrant.com
In reply to: Abhijit Menon-Sen (#6)
1 attachment(s)
Re: Proposal for changes to recovery.conf API

Hi.

Here's an updated version of my patch, which now applies on top of the
patch that Simon posted earlier (recovery_startup_r10_api.v1b.patch).

A few notes:

1. I merged in recovery_target_lsn as a new GUC setting.
2. I fixed various minor nits in the earlier patch, not worth mentioning
individually.
3. I haven't added back trigger_file, because Simon's patch removes it.
I can add it back in separately after discussion (otherwise Simon's
and my patches will conflict).
4. I've tested this to the extent that setting things in postgresql.conf
works, and recovery.conf is still read if it exists, and so on.

One open issue is the various assign_recovery_target_xxx functions,
which Michael noted in his earlier review:

+static void
+assign_recovery_target_xid(const char *newval, void *extra)
+{
+	recovery_target_xid = *((TransactionId *) extra);
+
+	if (recovery_target_xid != InvalidTransactionId)
+		recovery_target = RECOVERY_TARGET_XID;
+	else if (recovery_target_name && *recovery_target_name)
+		recovery_target = RECOVERY_TARGET_NAME;
+	else if (recovery_target_time != 0)
+		recovery_target = RECOVERY_TARGET_TIME;
+	else if (recovery_target_lsn != 0)
+		recovery_target = RECOVERY_TARGET_LSN;
+	else
+		recovery_target = RECOVERY_TARGET_UNSET;
+}

(Note how recovery_target_lsn caused this—and three other functions
besides—to grow an extra branch.)

I don't like this code, but I'm not yet sure what to replace it with. I
think we should address the underlying problem—that the UI doesn't map
cleanly to what the code wants. There's been some discussion about this
earlier, but not any consensus that I could see.

Do we want something like this (easy to implement and document, perhaps
not especially convenient to use):

recovery_target = 'xid' # or 'time'/'name'/'lsn'/'immediate'
recovery_target_xid = xxx? # the only setting we care about now
recovery_target_otherthings = parsed_but_ignored

Or something like this (a bit harder to implement):

recovery_target = 'xid:xxx' # or 'time:xxx' etc.

Alternatively, the do-nothing option is to move the tests from guc.c to
StartupXLOG and do it only once in some defined order (which would also
break the current last-mention-wins behaviour).

Thoughts? (I've added Fujii to the Cc: list, in case he has any
comments, since this is based on his earlier patch.)

-- Abhijit

Attachments:

recovery_guc_v20160906.patchtext/x-diff; charset=us-asciiDownload
commit c20d735648f5ea867fda5afc499a63d3877536a3
Author: Abhijit Menon-Sen <ams@2ndQuadrant.com>
Date:   Thu Sep 1 09:01:04 2016 +0530

    Convert recovery.conf settings to GUCs

    Based on unite_recoveryconf_postgresqlconf_v3.patch by Fujii Masao.

diff --git a/contrib/pg_standby/pg_standby.c b/contrib/pg_standby/pg_standby.c
index e4136f9..8fcb85c 100644
--- a/contrib/pg_standby/pg_standby.c
+++ b/contrib/pg_standby/pg_standby.c
@@ -520,7 +520,7 @@ usage(void)
 	printf("  -w MAXWAITTIME     max seconds to wait for a file (0=no limit) (default=0)\n");
 	printf("  -?, --help         show this help, then exit\n");
 	printf("\n"
-		   "Main intended use as restore_command in recovery.conf:\n"
+		   "Main intended use as restore_command in postgresql.conf:\n"
 		   "  restore_command = 'pg_standby [OPTION]... ARCHIVELOCATION %%f %%p %%r'\n"
 		   "e.g.\n"
 	"  restore_command = 'pg_standby /mnt/server/archiverdir %%f %%p %%r'\n");
diff --git a/doc/src/sgml/backup.sgml b/doc/src/sgml/backup.sgml
index 0f09d82..f43e41e 100644
--- a/doc/src/sgml/backup.sgml
+++ b/doc/src/sgml/backup.sgml
@@ -1174,8 +1174,15 @@ SELECT pg_stop_backup();
    </listitem>
    <listitem>
     <para>
-     Create a recovery command file <filename>recovery.conf</> in the cluster
-     data directory (see <xref linkend="recovery-config">). You might
+     Set up recovery parameters in <filename>postgresql.conf</> (see
+     <xref linkend="runtime-config-wal-archive-recovery"> and
+     <xref linkend="runtime-config-wal-recovery-target">).
+    </para>
+   </listitem>
+   <listitem>
+    <para>
+     Create a recovery trigger file <filename>recovery.trigger</>
+     in the cluster data directory. You might
      also want to temporarily modify <filename>pg_hba.conf</> to prevent
      ordinary users from connecting until you are sure the recovery was successful.
     </para>
@@ -1187,7 +1194,7 @@ SELECT pg_stop_backup();
      recovery be terminated because of an external error, the server can
      simply be restarted and it will continue recovery.  Upon completion
      of the recovery process, the server will rename
-     <filename>recovery.conf</> to <filename>recovery.done</> (to prevent
+     <filename>recovery.trigger</> to <filename>recovery.done</> (to prevent
      accidentally re-entering recovery mode later) and then
      commence normal database operations.
     </para>
@@ -1203,12 +1210,11 @@ SELECT pg_stop_backup();
    </para>
 
    <para>
-    The key part of all this is to set up a recovery configuration file that
-    describes how you want to recover and how far the recovery should
-    run.  You can use <filename>recovery.conf.sample</> (normally
-    located in the installation's <filename>share/</> directory) as a
-    prototype.  The one thing that you absolutely must specify in
-    <filename>recovery.conf</> is the <varname>restore_command</>,
+    The key part of all this is to set up recovery parameters that
+    specify how you want to recover and how far the recovery should
+    run. The one thing that you absolutely must specify in
+    <filename>postgresql.conf</> to recover from the backup is
+    the <varname>restore_command</>,
     which tells <productname>PostgreSQL</> how to retrieve archived
     WAL file segments.  Like the <varname>archive_command</>, this is
     a shell command string.  It can contain <literal>%f</>, which is
@@ -1270,7 +1276,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 <link linkend="recovery-target-settings">stopping point</link> in <filename>recovery.conf</>.  You can specify
+    required <link linkend="recovery-target-settings">stopping point</link> in <filename>postgresql.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
@@ -1367,8 +1373,9 @@ restore_command = 'cp /mnt/server/archivedir/%f %p'
     The default behavior of recovery is to recover along the same timeline
     that was current when the base backup was taken.  If you wish to recover
     into some child timeline (that is, you want to return to some state that
-    was itself generated after a recovery attempt), you need to specify the
-    target timeline ID in <filename>recovery.conf</>.  You cannot recover into
+    was itself generated after a recovery attempt), you need to set
+    <xref linkend="guc-recovery-target-timeline"> to the
+    target timeline ID in <filename>postgresql.conf</>.  You cannot recover into
     timelines that branched off earlier than the base backup.
    </para>
   </sect2>
diff --git a/doc/src/sgml/config.sgml b/doc/src/sgml/config.sgml
index 7c483c6..c277ad8 100644
--- a/doc/src/sgml/config.sgml
+++ b/doc/src/sgml/config.sgml
@@ -2840,6 +2840,348 @@ include_dir 'conf.d'
      </variablelist>
     </sect2>
 
+    <sect2 id="runtime-config-wal-archive-recovery">
+     <title>Archive Recovery</title>
+
+     <variablelist>
+      <varlistentry id="guc-restore-command" xreflabel="restore_command">
+       <term><varname>restore_command</varname> (<type>string</type>)</term>
+       <indexterm>
+        <primary><varname>restore_command</> configuration parameter</primary>
+       </indexterm>
+       <listitem>
+        <para>
+         The shell command to execute to retrieve an archived segment of
+         the WAL file series. This parameter is required for archive recovery,
+         but optional for streaming replication.
+         Any <literal>%f</> in the string is
+         replaced by the name of the file to retrieve from the archive,
+         and any <literal>%p</> is replaced by the copy destination path name
+         on the server.
+         (The path name is relative to the current working directory,
+         i.e., the cluster's data directory.)
+         Any <literal>%r</> is replaced by the name of the file containing the
+         last valid restart point. That is the earliest file that must be kept
+         to allow a restore to be restartable, so this information can be used
+         to truncate the archive to just the minimum required to support
+         restarting from the current restore. <literal>%r</> is typically only
+         used by warm-standby configurations
+         (see <xref linkend="warm-standby">).
+         Write <literal>%%</> to embed an actual <literal>%</> character.
+        </para>
+        <para>
+         It is important for the command to return a zero exit status
+         only if it succeeds.  The command <emphasis>will</> be asked for file
+         names that are not present in the archive; it must return nonzero
+         when so asked.  Examples:
+<programlisting>
+restore_command = 'cp /mnt/server/archivedir/%f "%p"'
+restore_command = 'copy "C:\\server\\archivedir\\%f" "%p"'  # Windows
+</programlisting>
+        </para>
+        <para>
+         This parameter can only be set in the <filename>postgresql.conf</>
+         file or on the server command line. It only has effect during archive
+         recovery or in standby mode.
+        </para>
+       </listitem>
+      </varlistentry>
+
+      <varlistentry id="guc-archive-cleanup-command" xreflabel="archive_cleanup_command">
+       <term><varname>archive_cleanup_command</varname> (<type>string</type>)</term>
+       <indexterm>
+         <primary><varname>archive_cleanup_command</> configuration parameter</primary>
+       </indexterm>
+       <listitem>
+        <para>
+         The shell command that will be executed at every restartpoint.
+         The purpose of <varname>archive_cleanup_command</> is to
+         provide a mechanism for cleaning up old archived WAL files that
+         are no longer needed by the standby server.
+         Any <literal>%r</> is replaced by the name of the file containing the
+         last valid restart point.
+         That is the earliest file that must be <emphasis>kept</> to allow a
+         restore to be restartable, and so all files earlier than <literal>%r</>
+         may be safely removed.
+         This information can be used to truncate the archive to just the
+         minimum required to support restart from the current restore.
+         The <xref linkend="pgarchivecleanup"> module
+         is often used in <varname>archive_cleanup_command</> for
+         single-standby configurations, for example:
+<programlisting>archive_cleanup_command = 'pg_archivecleanup /mnt/server/archivedir %r'</programlisting>
+         Note however that if multiple standby servers are restoring from the
+         same archive directory, you will need to ensure that you do not delete
+         WAL files until they are no longer needed by any of the servers.
+         <varname>archive_cleanup_command</> would typically be used in a
+         warm-standby configuration (see <xref linkend="warm-standby">).
+         Write <literal>%%</> to embed an actual <literal>%</> character in the
+         command.
+        </para>
+        <para>
+         If the command returns a non-zero exit status then a WARNING log
+         message will be written.
+        </para>
+        <para>
+         This parameter can only be set in the <filename>postgresql.conf</>
+         file or on the server command line. It only has effect during archive
+         recovery or in standby mode.
+        </para>
+       </listitem>
+      </varlistentry>
+
+      <varlistentry id="guc-recovery-end-command" xreflabel="recovery_end_command">
+       <term><varname>recovery_end_command</varname> (<type>string</type>)</term>
+       <indexterm>
+         <primary><varname>recovery_end_command</> configuration parameter</primary>
+       </indexterm>
+       <listitem>
+        <para>
+         The shell command that will be executed once only
+         at the end of recovery. This parameter is optional. The purpose of the
+         <varname>recovery_end_command</> is to provide a mechanism for cleanup
+         following replication or recovery.
+         Any <literal>%r</> is replaced by the name of the file containing the
+         last valid restart point, like in <varname>archive_cleanup_command</>.
+        </para>
+        <para>
+         If the command returns a non-zero exit status then a WARNING log
+         message will be written and the database will proceed to start up
+         anyway.  An exception is that if the command was terminated by a
+         signal, the database will not proceed with startup.
+        </para>
+        <para>
+         This parameter can only be set in the <filename>postgresql.conf</>
+         file or on the server command line. It only has effect during archive
+         recovery or in standby mode.
+        </para>
+       </listitem>
+      </varlistentry>
+     </variablelist>
+    </sect2>
+
+    <sect2 id="runtime-config-wal-recovery-target">
+     <title>Recovery Target</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_lsn</>, <varname>recovery_target_name</>,
+      <varname>recovery_target_time</>, or <varname>recovery_target_xid</>
+      can be used; if more than one of these is specified in the configuration
+      file, the last entry will be used.
+     </para>
+
+     <variablelist>
+     <varlistentry id="recovery-target" xreflabel="recovery_target">
+      <term><varname>recovery_target</varname><literal> = 'immediate'</literal>
+      <indexterm>
+        <primary><varname>recovery_target</> recovery parameter</primary>
+      </indexterm>
+      </term>
+      <listitem>
+       <para>
+        This parameter specifies that recovery should end as soon as a
+        consistent state is reached, i.e. 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>
+
+     <variablelist>
+      <varlistentry id="guc-recovery-target-name" xreflabel="recovery_target_name">
+       <term><varname>recovery_target_name</varname> (<type>string</type>)</term>
+       <indexterm>
+        <primary><varname>recovery_target_name</> configuration parameter</primary>
+       </indexterm>
+       <listitem>
+        <para>
+         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</>,
+         <varname>recovery_target_time</> or
+         <varname>recovery_target_xid</> can be specified.  The default
+         value is an empty string, which will recover to the end of the WAL log.
+        </para>
+        <para>
+         This parameter can only be set in the <filename>postgresql.conf</>
+         file or on the server command line. It only has effect during archive
+         recovery or in standby mode.
+        </para>
+       </listitem>
+      </varlistentry>
+
+      <varlistentry id="guc-recovery-target-time" xreflabel="recovery_target_time">
+       <term><varname>recovery_target_time</varname> (<type>string</type>)</term>
+       <indexterm>
+        <primary><varname>recovery_target_time</> configuration parameter</primary>
+       </indexterm>
+       <listitem>
+        <para>
+         Specifies the time stamp up to which recovery will proceed.
+         This parameter must be specified in the date/time format
+         (see <xref linkend="datatype-datetime-input"> for details).
+         At most one of <varname>recovery_target_time</>,
+         <varname>recovery_target_name</> or
+         <varname>recovery_target_xid</> can be specified.
+         The default value is an empty string, which will recover to
+         the end of the WAL log. The precise stopping point is also
+         influenced by <varname>recovery_target_inclusive</>.
+        </para>
+        <para>
+         This parameter can only be set in the <filename>postgresql.conf</>
+         file or on the server command line. It only has effect during archive
+         recovery or in standby mode.
+        </para>
+       </listitem>
+      </varlistentry>
+
+      <varlistentry id="guc-recovery-target-xid" xreflabel="recovery_target_xid">
+       <term><varname>recovery_target_xid</varname> (<type>string</type>)</term>
+       <indexterm>
+        <primary><varname>recovery_target_xid</> configuration parameter</primary>
+       </indexterm>
+       <listitem>
+        <para>
+         Specifies the transaction ID up to which recovery will proceed.
+         Keep in mind that while transaction IDs are assigned sequentially
+         at transaction 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</>,
+         <varname>recovery_target_name</> or
+         <varname>recovery_target_time</> can be specified.
+         The default value is an empty string, which will recover to the end of
+         the WAL log. The precise stopping point is also influenced by
+         <varname>recovery_target_inclusive</>.
+        </para>
+        <para>
+         This parameter can only be set in the <filename>postgresql.conf</>
+         file or on the server command line. It only has effect during archive
+         recovery or in standby mode.
+        </para>
+       </listitem>
+      </varlistentry>
+
+      <varlistentry id="guc-recovery-target-inclusive" xreflabel="recovery_target_inclusive">
+       <term><varname>recovery_target_inclusive</varname> (<type>boolean</type>)</term>
+       <indexterm>
+        <primary><varname>recovery_target_inclusive</> configuration parameter</primary>
+       </indexterm>
+       <listitem>
+        <para>
+         Specifies whether we stop just after the specified recovery target
+         (<literal>on</>), or just before the recovery target (<literal>off</>).
+         Applies to both <varname>recovery_target_time</>
+         and <varname>recovery_target_xid</>, whichever one is
+         specified for this recovery.  This indicates whether transactions
+         having exactly the target commit time or ID, respectively, will
+         be included in the recovery.  Default is <literal>on</>.
+        </para>
+        <para>
+         This parameter can only be set in the <filename>postgresql.conf</>
+         file or on the server command line. It only has effect during archive
+         recovery or in standby mode.
+        </para>
+       </listitem>
+      </varlistentry>
+
+      <varlistentry id="guc-recovery-target-timeline" xreflabel="recovery_target_timeline">
+       <term><varname>recovery_target_timeline</varname> (<type>string</type>)</term>
+       <indexterm>
+        <primary><varname>recovery_target_timeline</> configuration parameter</primary>
+       </indexterm>
+       <listitem>
+        <para>
+         Specifies recovering into a particular timeline.  The default value is
+         an empty string, which will recover along the same timeline that was
+         current when the base backup was taken. Setting this to
+         <literal>latest</> recovers to the latest timeline found in the archive,
+         which is useful in a standby server. Other than that you only need to
+         set this parameter in complex re-recovery situations, where you need
+         to return to a state that itself was reached after a point-in-time
+         recovery. See <xref linkend="backup-timelines"> for discussion.
+        </para>
+        <para>
+         This parameter can only be set at server start. It only has effect
+         during archive recovery or in standby mode.
+        </para>
+       </listitem>
+      </varlistentry>
+
+      <varlistentry id="recovery-target-lsn" xreflabel="recovery_target_lsn">
+       <term><varname>recovery_target_lsn</varname> (<type>pg_lsn</type>)
+       <indexterm>
+         <primary><varname>recovery_target_lsn</> recovery parameter</primary>
+       </indexterm>
+       </term>
+       <listitem>
+        <para>
+         This parameter specifies the LSN of the transaction log location up
+         to which recovery will proceed. The precise stopping point is also
+         influenced by <xref linkend="recovery-target-inclusive">. This
+         parameter is parsed using the system data type
+         <link linkend="datatype-pg-lsn"><type>pg_lsn</></link>.
+        </para>
+       </listitem>
+      </varlistentry>
+
+      <varlistentry id="guc-recovery-target-action" xreflabel="recovery_target_action">
+       <term><varname>recovery_target_action</varname> (<type>enum</type>)
+       <indexterm>
+         <primary><varname>recovery_target_action</> recovery parameter</primary>
+       </indexterm>
+       </term>
+       <listitem>
+        <para>
+         Specifies what action the server should take once the recovery target is
+         reached. The default is <literal>pause</>, which means recovery will
+         be paused. <literal>promote</> means the recovery process will finish
+         and the server will start to accept connections.
+         Finally <literal>shutdown</> will stop the server after reaching the
+         recovery target.
+        </para>
+        <para>
+         The intended use of the <literal>pause</> setting is to allow queries
+         to be executed against the database to check if this recovery target
+         is the most desirable point for recovery.
+         The paused state can be resumed by
+         using <function>pg_xlog_replay_resume()</> (see
+         <xref linkend="functions-recovery-control-table">), which then
+         causes recovery to end. If this recovery target is not the
+         desired stopping point, then shut down the server, change the
+         recovery target settings to a later target and restart to
+         continue recovery.
+        </para>
+        <para>
+         The <literal>shutdown</> setting is useful to have the instance ready
+         at the exact replay point desired.  The instance will still be able to
+         replay more WAL records (and in fact will have to replay WAL records
+         since the last checkpoint next time it is started).
+        </para>
+        <para>
+         Note that because <filename>recovery.trigger</> will not be renamed when
+         <varname>recovery_target_action</> is set to <literal>shutdown</>,
+         any subsequent start will end with immediate shutdown unless the
+         configuration is changed or the <filename>recovery.trigger</> file is
+         removed manually.
+        </para>
+        <para>
+         This setting has no effect if no recovery target is set.
+         If <xref linkend="guc-hot-standby"> is not enabled, a setting of
+         <literal>pause</> will act the same as <literal>shutdown</>.
+        </para>
+       </listitem>
+      </varlistentry>
+
+     </variablelist>
+     </sect2>
+
    </sect1>
 
    <sect1 id="runtime-config-replication">
@@ -3143,6 +3485,93 @@ include_dir 'conf.d'
 
     <variablelist>
 
+     <varlistentry id="guc-standby-mode" xreflabel="standby_mode">
+      <term><varname>standby_mode</varname> (<type>boolean</type>)</term>
+      <indexterm>
+       <primary><varname>standby_mode</> configuration parameter</primary>
+      </indexterm>
+      <listitem>
+       <para>
+        Specifies whether to start the <productname>PostgreSQL</> server as
+        a standby when recovery trigger file <filename>recovery.trigger</> exists.
+        The default value is <literal>off</>.
+        If this parameter is <literal>on</>, the server will not
+        stop recovery when the end of archived WAL is reached,
+        but will keep trying to continue recovery by fetching new WAL segments
+        using <varname>restore_command</> and/or by connecting to
+        the primary server as specified by the <varname>primary_conninfo</>
+        setting.
+       </para>
+       <para>
+        This parameter can only be set at server start. It only has effect
+        if recovery trigger file <filename>recovery.trigger</> exists.
+       </para>
+      </listitem>
+     </varlistentry>
+
+     <varlistentry id="guc-primary-conninfo" xreflabel="primary_conninfo">
+      <term><varname>primary_conninfo</varname> (<type>string</type>)</term>
+      <indexterm>
+        <primary><varname>primary_conninfo</> configuration parameter</primary>
+      </indexterm>
+      <listitem>
+       <para>
+        Specifies a connection string to be used for the standby server
+        to connect with the primary. This string is in the format
+        accepted by the libpq <function>PQconnectdb</function> function,
+        described in <xref linkend="libpq-connect">. If any option is
+        unspecified in this string, then the corresponding environment
+        variable (see <xref linkend="libpq-envars">) is checked. If the
+        environment variable is not set either, then defaults are used.
+        If this parameter is an empty string (the default), no attempt is
+        made to connect to the master.
+       </para>
+       <para>
+        The connection string should specify the host name (or address)
+        of the primary server, as well as the port number if it is not
+        the same as the standby server's default.
+        Also specify a user name corresponding to a role that has the
+        <literal>REPLICATION</> and <literal>LOGIN</> privileges on the
+        primary (see
+        <xref linkend="streaming-replication-authentication">).
+        A password needs to be provided too, if the primary demands password
+        authentication.  It can be provided in the
+        <varname>primary_conninfo</varname> string, or in a separate
+        <filename>~/.pgpass</> file on the standby server (use
+        <literal>replication</> as the database name).
+        Do not specify a database name in the
+        <varname>primary_conninfo</varname> string.
+       </para>
+       <para>
+        This parameter can only be set in the <filename>postgresql.conf</>
+        file or on the server command line. It only has effect in standby mode.
+       </para>
+       <para>
+        If this parameter is changed while replication is in progress,
+        the standby terminates replication, and then tries to restart
+        replication with new setting.
+       </para>
+      </listitem>
+     </varlistentry>
+
+     <varlistentry id="primary-slot-name" xreflabel="primary_slot_name">
+      <term><varname>primary_slot_name</varname> (<type>string</type>)
+      <indexterm>
+        <primary><varname>primary_slot_name</> recovery parameter</primary>
+      </indexterm>
+      </term>
+      <listitem>
+       <para>
+        Optionally specifies an existing replication slot to be used when
+        connecting to the primary via streaming replication to control
+        resource removal on the upstream node
+        (see <xref linkend="streaming-replication-slots">).
+        This setting has no effect if <varname>primary_conninfo</> is not
+        set.
+       </para>
+      </listitem>
+     </varlistentry>
+
      <varlistentry id="guc-hot-standby" xreflabel="hot_standby">
       <term><varname>hot_standby</varname> (<type>boolean</type>)
       <indexterm>
@@ -3223,6 +3652,57 @@ include_dir 'conf.d'
       </listitem>
      </varlistentry>
 
+     <varlistentry id="recovery-min-apply-delay" xreflabel="recovery_min_apply_delay">
+      <term><varname>recovery_min_apply_delay</varname> (<type>integer</type>)
+      <indexterm>
+        <primary><varname>recovery_min_apply_delay</> recovery parameter</primary>
+      </indexterm>
+      </term>
+      <listitem>
+       <para>
+        By default, a standby server restores WAL records from the
+        primary as soon as possible. It may be useful to have a time-delayed
+        copy of the data, offering opportunities to correct data loss errors.
+        This parameter allows you to delay recovery by a fixed period of time,
+        measured in milliseconds if no unit is specified.  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>
+        It is possible that the replication delay between servers exceeds the
+        value of this parameter, in which case no delay is added.
+        Note that the delay is calculated between the WAL time stamp as written
+        on master and the current time on the standby. Delays in transfer
+        because of network lag or cascading replication configurations
+        may reduce the actual wait time significantly. If the system
+        clocks on master and standby are not synchronized, this may lead to
+        recovery applying records earlier than expected; but that is not a
+        major issue because useful settings of this parameter are much larger
+        than typical time deviations between servers.
+       </para>
+       <para>
+        The delay occurs only on WAL records for transaction commits.
+        Other records are replayed as quickly as possible, which
+        is not a problem because MVCC visibility rules ensure their effects
+        are not visible until the corresponding commit record is applied.
+       </para>
+       <para>
+        The delay occurs once the database in recovery has reached a consistent
+        state, until the standby is promoted or triggered. After that the standby
+        will end recovery without further waiting.
+       </para>
+       <para>
+        This parameter is intended for use with streaming replication deployments;
+        however, if the parameter is specified it will be honored in all cases.
+        Synchronous replication is not affected by this setting because there is
+        not yet any setting to request synchronous apply of transaction commits.
+        <varname>hot_standby_feedback</> will be delayed by use of this feature
+        which could lead to bloat on the master; use both together with care.
+       </para>
+      </listitem>
+     </varlistentry>
+
      <varlistentry id="guc-wal-receiver-status-interval" xreflabel="wal_receiver_status_interval">
       <term><varname>wal_receiver_status_interval</varname> (<type>integer</type>)
       <indexterm>
diff --git a/doc/src/sgml/filelist.sgml b/doc/src/sgml/filelist.sgml
index 4383711..e9a6236 100644
--- a/doc/src/sgml/filelist.sgml
+++ b/doc/src/sgml/filelist.sgml
@@ -44,7 +44,6 @@
 <!ENTITY manage-ag     SYSTEM "manage-ag.sgml">
 <!ENTITY monitoring    SYSTEM "monitoring.sgml">
 <!ENTITY regress       SYSTEM "regress.sgml">
-<!ENTITY recovery-config SYSTEM "recovery-config.sgml">
 <!ENTITY runtime       SYSTEM "runtime.sgml">
 <!ENTITY config        SYSTEM "config.sgml">
 <!ENTITY user-manag    SYSTEM "user-manag.sgml">
diff --git a/doc/src/sgml/func.sgml b/doc/src/sgml/func.sgml
index 5148095..1a01a59 100644
--- a/doc/src/sgml/func.sgml
+++ b/doc/src/sgml/func.sgml
@@ -18092,7 +18092,7 @@ postgres=# select pg_start_backup('label_goes_here');
     <function>pg_create_restore_point</> creates a named transaction log
     record that can be used as recovery target, and returns the corresponding
     transaction log location.  The given name can then be used with
-    <xref linkend="recovery-target-name"> to specify the point up to which
+    <xref linkend="guc-recovery-target-name"> to specify the point up to which
     recovery will proceed.  Avoid creating multiple restore points with the
     same name, since recovery will stop at the first one whose name matches
     the recovery target.
diff --git a/doc/src/sgml/high-availability.sgml b/doc/src/sgml/high-availability.sgml
index 06f49db..d2fe0c3 100644
--- a/doc/src/sgml/high-availability.sgml
+++ b/doc/src/sgml/high-availability.sgml
@@ -591,7 +591,7 @@ protocol to make nodes agree on a serializable transactional order.
    <para>
     In standby mode, the server continuously applies WAL received from the
     master server. The standby server can read WAL from a WAL archive
-    (see <xref linkend="restore-command">) or directly from the master
+    (see <xref linkend="guc-restore-command">) or directly from the master
     over a TCP connection (streaming replication). The standby server will
     also attempt to restore any WAL found in the standby cluster's
     <filename>pg_xlog</> directory. That typically happens after a server
@@ -660,8 +660,8 @@ protocol to make nodes agree on a serializable transactional order.
    <para>
     To set up the standby server, restore the base backup taken from primary
     server (see <xref linkend="backup-pitr-recovery">). Create a recovery
-    command file <filename>recovery.conf</> in the standby's cluster data
-    directory, and turn on <varname>standby_mode</>. Set
+    trigger file <filename>recovery.trigger</> in the standby's cluster data
+    directory. Turn on <varname>standby_mode</> and set
     <varname>restore_command</> to a simple command to copy files from
     the WAL archive. If you plan to have multiple standby servers for high
     availability purposes, set <varname>recovery_target_timeline</> to
@@ -697,7 +697,7 @@ protocol to make nodes agree on a serializable transactional order.
 
    <para>
     If you're using a WAL archive, its size can be minimized using the <xref
-    linkend="archive-cleanup-command"> parameter to remove files that are no
+    linkend="guc-archive-cleanup-command"> parameter to remove files that are no
     longer required by the standby server.
     The <application>pg_archivecleanup</> utility is designed specifically to
     be used with <varname>archive_cleanup_command</> in typical single-standby
@@ -708,7 +708,7 @@ protocol to make nodes agree on a serializable transactional order.
    </para>
 
    <para>
-    A simple example of a <filename>recovery.conf</> is:
+    A simple example of standby settings is:
 <programlisting>
 standby_mode = 'on'
 primary_conninfo = 'host=192.168.1.50 port=5432 user=foo password=foopass'
@@ -766,8 +766,8 @@ archive_cleanup_command = 'pg_archivecleanup /path/to/archive %r'
     To use streaming replication, set up a file-based log-shipping standby
     server as described in <xref linkend="warm-standby">. The step that
     turns a file-based log-shipping standby into streaming replication
-    standby is setting <varname>primary_conninfo</> setting in the
-    <filename>recovery.conf</> file to point to the primary server. Set
+    standby is setting <varname>primary_conninfo</> to
+    point to the primary server. Set
     <xref linkend="guc-listen-addresses"> and authentication options
     (see <filename>pg_hba.conf</>) on the primary so that the standby server
     can connect to the <literal>replication</> pseudo-database on the primary
@@ -827,15 +827,14 @@ host    replication     foo             192.168.1.100/32        md5
     </para>
     <para>
      The host name and port number of the primary, connection user name,
-     and password are specified in the <filename>recovery.conf</> file.
+     and password are specified in <varname>primary_conninfo</>.
      The password can also be set in the <filename>~/.pgpass</> file on the
      standby (specify <literal>replication</> in the <replaceable>database</>
      field).
      For example, if the primary is running on host IP <literal>192.168.1.50</>,
      port <literal>5432</literal>, the account name for replication is
      <literal>foo</>, and the password is <literal>foopass</>, the administrator
-     can add the following line to the <filename>recovery.conf</> file on the
-     standby:
+     can set <varname>primary_conninfo</> on the standby like this:
 
 <programlisting>
 # The standby connects to the primary that is running on host 192.168.1.50
@@ -940,7 +939,7 @@ postgres=# SELECT * FROM pg_replication_slots;
 (1 row)
 </programlisting>
      To configure the standby to use this slot, <varname>primary_slot_name</>
-     should be configured in the standby's <filename>recovery.conf</>.
+     should be configured in the standby's <filename>postgresql.conf</>.
      Here is a simple example:
 <programlisting>
 standby_mode = 'on'
@@ -1387,8 +1386,8 @@ synchronous_standby_names = '2 (s1, s2, s3)'
    <para>
     To trigger failover of a log-shipping standby server,
     run <command>pg_ctl promote</> or create a trigger
-    file with the file name and path specified by the <varname>trigger_file</>
-    setting in <filename>recovery.conf</>. If you're planning to use
+    file with the file name and path specified by the <varname>trigger_file</>.
+    If you're planning to use
     <command>pg_ctl promote</> to fail over, <varname>trigger_file</> is
     not required. If you're setting up the reporting servers that are
     only used to offload read-only queries from the primary, not for high
@@ -1433,8 +1432,7 @@ synchronous_standby_names = '2 (s1, s2, s3)'
     The magic that makes the two loosely coupled servers work together is
     simply a <varname>restore_command</> used on the standby that,
     when asked for the next WAL file, waits for it to become available from
-    the primary. The <varname>restore_command</> is specified in the
-    <filename>recovery.conf</> file on the standby server. Normal recovery
+    the primary. Normal recovery
     processing would request a file from the WAL archive, reporting failure
     if the file was unavailable.  For standby processing it is normal for
     the next WAL file to be unavailable, so the standby must wait for
@@ -1521,8 +1519,14 @@ if (!triggered)
      </listitem>
      <listitem>
       <para>
+       Create a recovery trigger file <filename>recovery.trigger</>
+       in the standby's cluster data directory.
+      </para>
+     </listitem>
+     <listitem>
+      <para>
        Begin recovery on the standby server from the local WAL
-       archive, using a <filename>recovery.conf</> that specifies a
+       archive, specifying a
        <varname>restore_command</> that waits as described
        previously (see <xref linkend="backup-pitr-recovery">).
       </para>
@@ -2018,9 +2022,9 @@ if (!triggered)
    <title>Administrator's Overview</title>
 
    <para>
-    If <varname>hot_standby</> is turned <literal>on</> in
-    <filename>postgresql.conf</> and there is a <filename>recovery.conf</>
-    file present, the server will run in Hot Standby mode.
+    If <varname>hot_standby</> is turned <literal>on</>
+    and there is a recovery trigger file
+    <filename>recovery.trigger</> present, the server will run in Hot Standby mode.
     However, it may take some time for Hot Standby connections to be allowed,
     because the server will not accept connections until it has completed
     sufficient recovery to provide a consistent state against which queries
diff --git a/doc/src/sgml/pgstandby.sgml b/doc/src/sgml/pgstandby.sgml
index 80c6f60..c9e6185 100644
--- a/doc/src/sgml/pgstandby.sgml
+++ b/doc/src/sgml/pgstandby.sgml
@@ -46,8 +46,8 @@
 
   <para>
    To configure a standby
-   server to use <application>pg_standby</>, put this into its
-   <filename>recovery.conf</filename> configuration file:
+   server to use <application>pg_standby</>, specify
+   <xref linkend="guc-restore-command"> like this:
 <programlisting>
 restore_command = 'pg_standby <replaceable>archiveDir</> %f %p %r'
 </programlisting>
diff --git a/doc/src/sgml/postgres.sgml b/doc/src/sgml/postgres.sgml
index 0346d36..c47bc6b 100644
--- a/doc/src/sgml/postgres.sgml
+++ b/doc/src/sgml/postgres.sgml
@@ -155,7 +155,6 @@
   &maintenance;
   &backup;
   &high-availability;
-  &recovery-config;
   &monitoring;
   &diskusage;
   &wal;
diff --git a/doc/src/sgml/recovery-config.sgml b/doc/src/sgml/recovery-config.sgml
deleted file mode 100644
index de3fb10..0000000
--- a/doc/src/sgml/recovery-config.sgml
+++ /dev/null
@@ -1,501 +0,0 @@
-<!-- doc/src/sgml/recovery-config.sgml -->
-
-<chapter id="recovery-config">
-  <title>Recovery Configuration</title>
-
-  <indexterm>
-   <primary>configuration</primary>
-   <secondary>of recovery</secondary>
-   <tertiary>of a standby server</tertiary>
-  </indexterm>
-
-   <para>
-    This chapter describes the settings available in the
-    <filename>recovery.conf</><indexterm><primary>recovery.conf</></>
-    file. They apply only for the duration of the
-    recovery.  They must be reset for any subsequent recovery you wish to
-    perform.  They cannot be changed once recovery has begun.
-   </para>
-
-   <para>
-     Settings in <filename>recovery.conf</> are specified in the format
-     <literal>name = 'value'</>. One parameter is specified per line.
-     Hash marks (<literal>#</literal>) designate the rest of the
-     line as a comment.  To embed a single quote in a parameter
-     value, write two quotes (<literal>''</>).
-   </para>
-
-   <para>
-    A sample file, <filename>share/recovery.conf.sample</>,
-    is provided in the installation's <filename>share/</> directory.
-   </para>
-
-  <sect1 id="archive-recovery-settings">
-
-    <title>Archive Recovery Settings</title>
-     <variablelist>
-
-     <varlistentry id="restore-command" xreflabel="restore_command">
-      <term><varname>restore_command</varname> (<type>string</type>)
-      <indexterm>
-        <primary><varname>restore_command</> recovery parameter</primary>
-      </indexterm>
-      </term>
-      <listitem>
-       <para>
-        The local shell command to execute to retrieve an archived segment of
-        the WAL file series. This parameter is required for archive recovery,
-        but optional for streaming replication.
-        Any <literal>%f</> in the string is
-        replaced by the name of the file to retrieve from the archive,
-        and any <literal>%p</> is replaced by the copy destination path name
-        on the server.
-        (The path name is relative to the current working directory,
-        i.e., the cluster's data directory.)
-        Any <literal>%r</> is replaced by the name of the file containing the
-        last valid restart point. That is the earliest file that must be kept
-        to allow a restore to be restartable, so this information can be used
-        to truncate the archive to just the minimum required to support
-        restarting from the current restore. <literal>%r</> is typically only
-        used by warm-standby configurations
-        (see <xref linkend="warm-standby">).
-        Write <literal>%%</> to embed an actual <literal>%</> character.
-       </para>
-
-       <para>
-        It is important for the command to return a zero exit status
-        only if it succeeds.  The command <emphasis>will</> be asked for file
-        names that are not present in the archive; it must return nonzero
-        when so asked.  Examples:
-<programlisting>
-restore_command = 'cp /mnt/server/archivedir/%f "%p"'
-restore_command = 'copy "C:\\server\\archivedir\\%f" "%p"'  # Windows
-</programlisting>
-        An exception is that if the command was terminated by a signal (other
-        than <systemitem>SIGTERM</systemitem>, which is used as part of a
-        database server shutdown) or an error by the shell (such as command
-        not found), then recovery will abort and the server will not start up.
-       </para>
-      </listitem>
-     </varlistentry>
-
-     <varlistentry id="archive-cleanup-command" xreflabel="archive_cleanup_command">
-      <term><varname>archive_cleanup_command</varname> (<type>string</type>)
-      <indexterm>
-        <primary><varname>archive_cleanup_command</> recovery parameter</primary>
-      </indexterm>
-      </term>
-      <listitem>
-       <para>
-        This optional parameter specifies a shell command that will be executed
-        at every restartpoint.  The purpose of
-        <varname>archive_cleanup_command</> is to provide a mechanism for
-        cleaning up old archived WAL files that are no longer needed by the
-        standby server.
-        Any <literal>%r</> is replaced by the name of the file containing the
-        last valid restart point.
-        That is the earliest file that must be <emphasis>kept</> to allow a
-        restore to be restartable, and so all files earlier than <literal>%r</>
-        may be safely removed.
-        This information can be used to truncate the archive to just the
-        minimum required to support restart from the current restore.
-        The <xref linkend="pgarchivecleanup"> module
-        is often used in <varname>archive_cleanup_command</> for
-        single-standby configurations, for example:
-<programlisting>archive_cleanup_command = 'pg_archivecleanup /mnt/server/archivedir %r'</programlisting>
-        Note however that if multiple standby servers are restoring from the
-        same archive directory, you will need to ensure that you do not delete
-        WAL files until they are no longer needed by any of the servers.
-        <varname>archive_cleanup_command</> would typically be used in a
-        warm-standby configuration (see <xref linkend="warm-standby">).
-        Write <literal>%%</> to embed an actual <literal>%</> character in the
-        command.
-       </para>
-       <para>
-        If the command returns a nonzero exit status then a warning log
-        message will be written.  An exception is that if the command was
-        terminated by a signal or an error by the shell (such as command not
-        found), a fatal error will be raised.
-       </para>
-      </listitem>
-     </varlistentry>
-
-     <varlistentry id="recovery-end-command" xreflabel="recovery_end_command">
-      <term><varname>recovery_end_command</varname> (<type>string</type>)
-      <indexterm>
-        <primary><varname>recovery_end_command</> recovery parameter</primary>
-      </indexterm>
-      </term>
-      <listitem>
-       <para>
-        This parameter specifies a shell command that will be executed once only
-        at the end of recovery. This parameter is optional. The purpose of the
-        <varname>recovery_end_command</> is to provide a mechanism for cleanup
-        following replication or recovery.
-        Any <literal>%r</> is replaced by the name of the file containing the
-        last valid restart point, like in <xref linkend="archive-cleanup-command">.
-       </para>
-       <para>
-        If the command returns a nonzero exit status then a warning log
-        message will be written and the database will proceed to start up
-        anyway.  An exception is that if the command was terminated by a
-        signal or an error by the shell (such as command not found), the
-        database will not proceed with startup.
-       </para>
-      </listitem>
-     </varlistentry>
-
-    </variablelist>
-
-  </sect1>
-
-  <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_lsn</>, <varname>recovery_target_name</>,
-      <varname>recovery_target_time</>, or <varname>recovery_target_xid</>
-      can be used; if more than one of these is specified in the configuration
-      file, the last entry will be used.
-     </para>
-
-     <variablelist>
-     <varlistentry id="recovery-target" xreflabel="recovery_target">
-      <term><varname>recovery_target</varname><literal> = 'immediate'</literal>
-      <indexterm>
-        <primary><varname>recovery_target</> recovery parameter</primary>
-      </indexterm>
-      </term>
-      <listitem>
-       <para>
-        This parameter specifies that recovery should end as soon as a
-        consistent state is reached, i.e. 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>)
-      <indexterm>
-        <primary><varname>recovery_target_name</> recovery parameter</primary>
-      </indexterm>
-      </term>
-      <listitem>
-       <para>
-        This parameter specifies the named restore point (created with
-        <function>pg_create_restore_point()</>) to which recovery will proceed.
-       </para>
-      </listitem>
-     </varlistentry>
-
-     <varlistentry id="recovery-target-time" xreflabel="recovery_target_time">
-      <term><varname>recovery_target_time</varname> (<type>timestamp</type>)
-      <indexterm>
-        <primary><varname>recovery_target_time</> recovery parameter</primary>
-      </indexterm>
-      </term>
-      <listitem>
-       <para>
-        This parameter specifies the time stamp up to which recovery
-        will proceed.
-        The precise stopping point is also influenced by
-        <xref linkend="recovery-target-inclusive">.
-       </para>
-      </listitem>
-     </varlistentry>
-
-     <varlistentry id="recovery-target-xid" xreflabel="recovery_target_xid">
-      <term><varname>recovery_target_xid</varname> (<type>string</type>)
-      <indexterm>
-        <primary><varname>recovery_target_xid</> recovery parameter</primary>
-      </indexterm>
-      </term>
-      <listitem>
-       <para>
-        This parameter specifies the transaction ID up to which recovery
-        will proceed. Keep in mind
-        that while transaction IDs are assigned sequentially at transaction
-        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.
-        The precise stopping point is also influenced by
-        <xref linkend="recovery-target-inclusive">.
-       </para>
-      </listitem>
-     </varlistentry>
-
-     <varlistentry id="recovery-target-lsn" xreflabel="recovery_target_lsn">
-      <term><varname>recovery_target_lsn</varname> (<type>pg_lsn</type>)
-      <indexterm>
-        <primary><varname>recovery_target_lsn</> recovery parameter</primary>
-      </indexterm>
-      </term>
-      <listitem>
-       <para>
-        This parameter specifies the LSN of the transaction log location up
-        to which recovery will proceed. The precise stopping point is also
-        influenced by <xref linkend="recovery-target-inclusive">. This
-        parameter is parsed using the system data type
-        <link linkend="datatype-pg-lsn"><type>pg_lsn</></link>.
-       </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">
-      <term><varname>recovery_target_inclusive</varname> (<type>boolean</type>)
-      <indexterm>
-        <primary><varname>recovery_target_inclusive</> recovery parameter</primary>
-      </indexterm>
-      </term>
-      <listitem>
-       <para>
-        Specifies whether to stop just after the specified recovery target
-        (<literal>true</literal>), or just before the recovery target
-        (<literal>false</literal>).
-        Applies when either <xref linkend="recovery-target-time">
-        or <xref linkend="recovery-target-xid"> is specified.
-        This setting controls whether transactions
-        having exactly the target commit time or ID, respectively, will
-        be included in the recovery.  Default is <literal>true</>.
-       </para>
-      </listitem>
-     </varlistentry>
-
-     <varlistentry id="recovery-target-timeline"
-                   xreflabel="recovery_target_timeline">
-      <term><varname>recovery_target_timeline</varname> (<type>string</type>)
-      <indexterm>
-        <primary><varname>recovery_target_timeline</> recovery parameter</primary>
-      </indexterm>
-      </term>
-      <listitem>
-       <para>
-        Specifies recovering into a particular timeline.  The default is
-        to recover along the same timeline that was current when the
-        base backup was taken. Setting this to <literal>latest</> recovers
-        to the latest timeline found in the archive, which is useful in
-        a standby server. Other than that you only need to set this parameter
-        in complex re-recovery situations, where you need to return to
-        a state that itself was reached after a point-in-time recovery.
-        See <xref linkend="backup-timelines"> for discussion.
-       </para>
-      </listitem>
-     </varlistentry>
-
-     <varlistentry id="recovery-target-action"
-                   xreflabel="recovery_target_action">
-      <term><varname>recovery_target_action</varname> (<type>enum</type>)
-      <indexterm>
-        <primary><varname>recovery_target_action</> recovery parameter</primary>
-      </indexterm>
-      </term>
-      <listitem>
-       <para>
-        Specifies what action the server should take once the recovery target is
-        reached. The default is <literal>pause</>, which means recovery will
-        be paused. <literal>promote</> means the recovery process will finish
-        and the server will start to accept connections.
-        Finally <literal>shutdown</> will stop the server after reaching the
-        recovery target.
-       </para>
-       <para>
-        The intended use of the <literal>pause</> setting is to allow queries
-        to be executed against the database to check if this recovery target
-        is the most desirable point for recovery.
-        The paused state can be resumed by
-        using <function>pg_xlog_replay_resume()</> (see
-        <xref linkend="functions-recovery-control-table">), which then
-        causes recovery to end. If this recovery target is not the
-        desired stopping point, then shut down the server, change the
-        recovery target settings to a later target and restart to
-        continue recovery.
-       </para>
-       <para>
-        The <literal>shutdown</> setting is useful to have the instance ready
-        at the exact replay point desired.  The instance will still be able to
-        replay more WAL records (and in fact will have to replay WAL records
-        since the last checkpoint next time it is started).
-       </para>
-       <para>
-        Note that because <filename>recovery.conf</> will not be renamed when
-        <varname>recovery_target_action</> is set to <literal>shutdown</>,
-        any subsequent start will end with immediate shutdown unless the
-        configuration is changed or the <filename>recovery.conf</> file is
-        removed manually.
-       </para>
-       <para>
-        This setting has no effect if no recovery target is set.
-        If <xref linkend="guc-hot-standby"> is not enabled, a setting of
-        <literal>pause</> will act the same as <literal>shutdown</>.
-       </para>
-      </listitem>
-     </varlistentry>
-
-    </variablelist>
-   </sect1>
-
-  <sect1 id="standby-settings">
-
-    <title>Standby Server Settings</title>
-     <variablelist>
-
-       <varlistentry id="standby-mode" xreflabel="standby_mode">
-        <term><varname>standby_mode</varname> (<type>boolean</type>)
-        <indexterm>
-          <primary><varname>standby_mode</> recovery parameter</primary>
-        </indexterm>
-        </term>
-        <listitem>
-         <para>
-          Specifies whether to start the <productname>PostgreSQL</> server as
-          a standby. If this parameter is <literal>on</>, the server will
-          not stop recovery when the end of archived WAL is reached, but
-          will keep trying to continue recovery by fetching new WAL segments
-          using <varname>restore_command</>
-          and/or by connecting to the primary server as specified by the
-          <varname>primary_conninfo</> setting.
-         </para>
-        </listitem>
-       </varlistentry>
-       <varlistentry id="primary-conninfo" xreflabel="primary_conninfo">
-        <term><varname>primary_conninfo</varname> (<type>string</type>)
-        <indexterm>
-          <primary><varname>primary_conninfo</> recovery parameter</primary>
-        </indexterm>
-        </term>
-        <listitem>
-         <para>
-          Specifies a connection string to be used for the standby server
-          to connect with the primary. This string is in the format
-          described in <xref linkend="libpq-connstring">. If any option is
-          unspecified in this string, then the corresponding environment
-          variable (see <xref linkend="libpq-envars">) is checked. If the
-          environment variable is not set either, then
-          defaults are used.
-         </para>
-         <para>
-          The connection string should specify the host name (or address)
-          of the primary server, as well as the port number if it is not
-          the same as the standby server's default.
-          Also specify a user name corresponding to a suitably-privileged role
-          on the primary (see
-          <xref linkend="streaming-replication-authentication">).
-          A password needs to be provided too, if the primary demands password
-          authentication.  It can be provided in the
-          <varname>primary_conninfo</varname> string, or in a separate
-          <filename>~/.pgpass</> file on the standby server (use
-          <literal>replication</> as the database name).
-          Do not specify a database name in the
-          <varname>primary_conninfo</varname> string.
-         </para>
-         <para>
-          This setting has no effect if <varname>standby_mode</> is <literal>off</>.
-         </para>
-        </listitem>
-       </varlistentry>
-       <varlistentry id="primary-slot-name" xreflabel="primary_slot_name">
-        <term><varname>primary_slot_name</varname> (<type>string</type>)
-        <indexterm>
-          <primary><varname>primary_slot_name</> recovery parameter</primary>
-        </indexterm>
-        </term>
-        <listitem>
-         <para>
-          Optionally specifies an existing replication slot to be used when
-          connecting to the primary via streaming replication to control
-          resource removal on the upstream node
-          (see <xref linkend="streaming-replication-slots">).
-          This setting has no effect if <varname>primary_conninfo</> is not
-          set.
-         </para>
-        </listitem>
-       </varlistentry>
-       <varlistentry id="trigger-file" xreflabel="trigger_file">
-        <term><varname>trigger_file</varname> (<type>string</type>)
-        <indexterm>
-          <primary><varname>trigger_file</> recovery parameter</primary>
-        </indexterm>
-        </term>
-        <listitem>
-         <para>
-          Specifies a trigger file whose presence ends recovery in the
-          standby.  Even if this value is not set, you can still promote
-          the standby using <command>pg_ctl promote</>.
-          This setting has no effect if <varname>standby_mode</> is <literal>off</>.
-         </para>
-        </listitem>
-       </varlistentry>
-
-     <varlistentry id="recovery-min-apply-delay" xreflabel="recovery_min_apply_delay">
-      <term><varname>recovery_min_apply_delay</varname> (<type>integer</type>)
-      <indexterm>
-        <primary><varname>recovery_min_apply_delay</> recovery parameter</primary>
-      </indexterm>
-      </term>
-      <listitem>
-       <para>
-        By default, a standby server restores WAL records from the
-        primary as soon as possible. It may be useful to have a time-delayed
-        copy of the data, offering opportunities to correct data loss errors.
-        This parameter allows you to delay recovery by a fixed period of time,
-        measured in milliseconds if no unit is specified.  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>
-        It is possible that the replication delay between servers exceeds the
-        value of this parameter, in which case no delay is added.
-        Note that the delay is calculated between the WAL time stamp as written
-        on master and the current time on the standby. Delays in transfer
-        because of network lag or cascading replication configurations
-        may reduce the actual wait time significantly. If the system
-        clocks on master and standby are not synchronized, this may lead to
-        recovery applying records earlier than expected; but that is not a
-        major issue because useful settings of this parameter are much larger
-        than typical time deviations between servers.
-       </para>
-       <para>
-        The delay occurs only on WAL records for transaction commits.
-        Other records are replayed as quickly as possible, which
-        is not a problem because MVCC visibility rules ensure their effects
-        are not visible until the corresponding commit record is applied.
-       </para>
-       <para>
-        The delay occurs once the database in recovery has reached a consistent
-        state, until the standby is promoted or triggered. After that the standby
-        will end recovery without further waiting.
-       </para>
-       <para>
-        This parameter is intended for use with streaming replication deployments;
-        however, if the parameter is specified it will be honored in all cases.
-        Synchronous replication is not affected by this setting because there is
-        not yet any setting to request synchronous apply of transaction commits.
-        <varname>hot_standby_feedback</> will be delayed by use of this feature
-        which could lead to bloat on the master; use both together with care.
-       </para>
-      </listitem>
-     </varlistentry>
-
-     </variablelist>
-   </sect1>
-
-</chapter>
diff --git a/doc/src/sgml/ref/pgarchivecleanup.sgml b/doc/src/sgml/ref/pgarchivecleanup.sgml
index abe01be..dc29854 100644
--- a/doc/src/sgml/ref/pgarchivecleanup.sgml
+++ b/doc/src/sgml/ref/pgarchivecleanup.sgml
@@ -38,8 +38,8 @@
 
   <para>
    To configure a standby
-   server to use <application>pg_archivecleanup</>, put this into its
-   <filename>recovery.conf</filename> configuration file:
+   server to use <application>pg_archivecleanup</>, specify
+   <xref linkend="guc-archive-cleanup-command"> like this:
 <programlisting>
 archive_cleanup_command = 'pg_archivecleanup <replaceable>archivelocation</> %r'
 </programlisting>
@@ -47,7 +47,7 @@ archive_cleanup_command = 'pg_archivecleanup <replaceable>archivelocation</> %r'
    files should be removed.
   </para>
   <para>
-   When used within <xref linkend="archive-cleanup-command">, all WAL files
+   When used within <varname>archive_cleanup_command</>, all WAL files
    logically preceding the value of the <literal>%r</> argument will be removed
    from <replaceable>archivelocation</>. This minimizes the number of files
    that need to be retained, while preserving crash-restart capability.  Use of
diff --git a/doc/src/sgml/release-9.1.sgml b/doc/src/sgml/release-9.1.sgml
index a66ca0d..afac09c 100644
--- a/doc/src/sgml/release-9.1.sgml
+++ b/doc/src/sgml/release-9.1.sgml
@@ -9601,7 +9601,7 @@ Branch: REL9_0_STABLE [9d6af7367] 2015-08-15 11:02:34 -0400
        <para>
         These named restore points can be specified as recovery
         targets using the new <filename>recovery.conf</> setting
-        <link linkend="recovery-target-name"><varname>recovery_target_name</></link>.
+        <link linkend="guc-recovery-target-name"><varname>recovery_target_name</></link>.
        </para>
       </listitem>
 
@@ -9633,8 +9633,7 @@ Branch: REL9_0_STABLE [9d6af7367] 2015-08-15 11:02:34 -0400
 
       <listitem>
        <para>
-        Allow <link
-        linkend="recovery-config"><filename>recovery.conf</></link>
+        Allow <filename>recovery.conf</>
         to use the same quoting behavior as <filename>postgresql.conf</>
         (Dimitri Fontaine)
        </para>
diff --git a/doc/src/sgml/release.sgml b/doc/src/sgml/release.sgml
index 472c1f6..4237a66 100644
--- a/doc/src/sgml/release.sgml
+++ b/doc/src/sgml/release.sgml
@@ -6,7 +6,7 @@ Typical markup:
 &<>                             use & escapes
 PostgreSQL                      <productname>
 postgresql.conf, pg_hba.conf,
-        recovery.conf           <filename>
+        recovery.trigger        <filename>
 [A-Z][A-Z_ ]+[A-Z_]             <command>, <literal>, <envar>, <acronym>
 [A-Za-z_][A-Za-z0-9_]+()        <function>
 -[-A-Za-z_]+                    <option>
diff --git a/src/backend/access/transam/recovery.conf.sample b/src/backend/access/transam/recovery.conf.sample
index 7a16751..15ce784 100644
--- a/src/backend/access/transam/recovery.conf.sample
+++ b/src/backend/access/transam/recovery.conf.sample
@@ -2,23 +2,14 @@
 # PostgreSQL recovery config file
 # -------------------------------
 #
-# Edit this file to provide the parameters that PostgreSQL needs to
-# perform an archive recovery of a database, or to act as a replication
-# standby.
+# PostgreSQL 10.0 or later, recovery.conf is no longer used. Instead,
+# specify all recovery parameters in postgresql.conf and create
+# recovery.trigger to enter archive recovery or standby mode.
 #
-# If "recovery.conf" is present in the PostgreSQL data directory, it is
-# read on postmaster startup.  After successful recovery, it is renamed
-# to "recovery.done" to ensure that we do not accidentally re-enter
-# archive recovery or standby mode.
+# If you must use recovery.conf, specify "include directives" in
+# postgresql.conf like this:
 #
-# This file consists of lines of the form:
-#
-#   name = value
-#
-# Comments are introduced with '#'.
-#
-# The complete list of option names and allowed values can be found
-# in the PostgreSQL documentation.
+#   include 'recovery.conf'
 #
 #---------------------------------------------------------------------------
 # ARCHIVE RECOVERY PARAMETERS
@@ -131,14 +122,6 @@
 #
 #primary_slot_name = ''
 #
-# By default, a standby server keeps restoring XLOG records from the
-# primary indefinitely. If you want to stop the standby mode, finish recovery
-# and open the system in read/write mode, specify a path to a trigger file.
-# The server will poll the trigger file path periodically and start as a
-# primary server when it's found.
-#
-#trigger_file = ''
-#
 # By default, a standby server restores XLOG records from the primary as
 # soon as possible. If you want to explicitly delay the replay of committed
 # transactions from the master, specify a minimum apply delay. For example,
diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c
index 2808423..183a7a6 100644
--- a/src/backend/access/transam/xlog.c
+++ b/src/backend/access/transam/xlog.c
@@ -77,7 +77,6 @@
 extern uint32 bootstrap_data_checksum_version;
 
 /* File path names (all relative to $PGDATA) */
-#define RECOVERY_COMMAND_FILE	"recovery.conf"
 #define RECOVERY_SIGNAL_FILE	"recovery.trigger"
 #define PROMOTE_SIGNAL_FILE		"promote.trigger"
 
@@ -100,6 +99,25 @@ int			wal_level = WAL_LEVEL_MINIMAL;
 int			CommitDelay = 0;	/* precommit delay in microseconds */
 int			CommitSiblings = 5; /* # concurrent xacts needed to sleep */
 int			wal_retrieve_retry_interval = 5000;
+char	   *restore_command = NULL;
+char	   *archive_cleanup_command = NULL;
+char	   *recovery_end_command = NULL;
+bool		standby_mode = false;
+char	   *primary_conninfo = NULL;
+char	   *primary_slot_name = NULL;
+int			recovery_min_apply_delay = 0;
+RecoveryTargetType	recovery_target = RECOVERY_TARGET_UNSET;
+TransactionId	recovery_target_xid = InvalidTransactionId;
+TimestampTz	recovery_target_time = 0;
+char	   *recovery_target_name = NULL;
+XLogRecPtr recovery_target_lsn;
+bool		recovery_target_inclusive = true;
+char	   *recovery_target_timeline_string = NULL;
+char	   *recovery_target_action_string = NULL;
+char	   *recovery_target_lsn_string = NULL;
+char	   *recovery_target_string = NULL;
+TimeLineID	recovery_target_timeline = 0;
+RecoveryTargetAction recovery_target_action = RECOVERY_TARGET_ACTION_PAUSE;
 
 #ifdef WAL_DEBUG
 bool		XLOG_DEBUG = false;
@@ -244,24 +262,10 @@ bool		InArchiveRecovery = false;
 /* Was the last xlog file restored from archive, or local? */
 static bool restoredFromArchive = false;
 
-/* options taken from recovery.conf for archive recovery */
-char	   *recoveryRestoreCommand = NULL;
-static char *recoveryEndCommand = NULL;
-static char *archiveCleanupCommand = NULL;
-static RecoveryTargetType recoveryTarget = RECOVERY_TARGET_UNSET;
-static bool recoveryTargetInclusive = true;
-static RecoveryTargetAction recoveryTargetAction = RECOVERY_TARGET_ACTION_PAUSE;
-static TransactionId recoveryTargetXid;
-static TimestampTz recoveryTargetTime;
-static char *recoveryTargetName;
-static XLogRecPtr recoveryTargetLSN;
-static int	recovery_min_apply_delay = 0;
 static TimestampTz recoveryDelayUntilTime;
 
 /* options taken from recovery.conf for XLOG streaming */
 static bool StandbyModeRequested = false;
-static char *PrimaryConnInfo = NULL;
-static char *PrimarySlotName = NULL;
 
 /* are we currently in standby mode? */
 bool		StandbyMode = false;
@@ -270,12 +274,15 @@ bool		StandbyMode = false;
  * if recoveryStopsBefore/After returns true, it saves information of the stop
  * point here
  */
+static RecoveryTargetType recoveryStopTarget;
 static TransactionId recoveryStopXid;
 static TimestampTz recoveryStopTime;
 static XLogRecPtr recoveryStopLSN;
 static char recoveryStopName[MAXFNAMELEN];
 static bool recoveryStopAfter;
 
+static TimestampTz recoveryDelayUntilTime;
+
 /*
  * During normal operation, the only timeline we care about is ThisTimeLineID.
  * During recovery, however, things are more complicated.  To simplify life
@@ -577,10 +584,10 @@ typedef struct XLogCtlData
 	TimeLineID	PrevTimeLineID;
 
 	/*
-	 * archiveCleanupCommand is read from recovery.conf but needs to be in
-	 * shared memory so that the checkpointer process can access it.
+	 * archive_cleanup_command is read from recovery.conf but needs to
+	 * be in shared memory so that the checkpointer process can access it.
 	 */
-	char		archiveCleanupCommand[MAXPGPATH];
+	char		archive_cleanup_command[MAXPGPATH];
 
 	/*
 	 * SharedRecoveryInProgress indicates if we're still in crash or archive
@@ -783,7 +790,7 @@ static bool holdingAllLocks = false;
 static MemoryContext walDebugCxt = NULL;
 #endif
 
-static void readRecoveryCommandFile(void);
+static void CheckRecoveryParameters(void);
 static void exitArchiveRecovery(TimeLineID endTLI, XLogRecPtr endOfLog);
 static bool recoveryStopsBefore(XLogReaderState *record);
 static bool recoveryStopsAfter(XLogReaderState *record);
@@ -3979,7 +3986,7 @@ ReadRecord(XLogReaderState *xlogreader, XLogRecPtr RecPtr, int emode,
 			/*
 			 * We only end up here without a message when XLogPageRead()
 			 * failed - in that case we already logged something. In
-			 * StandbyMode that only happens if we have been triggered, so we
+			 * standby_mode that only happens if we have been triggered, so we
 			 * shouldn't loop anymore in that case.
 			 */
 			if (errormsg)
@@ -4037,8 +4044,6 @@ ReadRecord(XLogReaderState *xlogreader, XLogRecPtr RecPtr, int emode,
 				ereport(DEBUG1,
 						(errmsg_internal("reached end of WAL in pg_xlog, entering archive recovery")));
 				InArchiveRecovery = true;
-				if (StandbyModeRequested)
-					StandbyMode = true;
 
 				/* initialize minRecoveryPoint to this record */
 				LWLockAcquire(ControlFileLock, LW_EXCLUSIVE);
@@ -4068,7 +4073,7 @@ ReadRecord(XLogReaderState *xlogreader, XLogRecPtr RecPtr, int emode,
 			}
 
 			/* In standby mode, loop back to retry. Otherwise, give up. */
-			if (StandbyMode && !CheckForPromoteTrigger())
+			if (standby_mode && !CheckForPromoteTrigger())
 				continue;
 			else
 				return NULL;
@@ -4929,305 +4934,58 @@ str_time(pg_time_t tnow)
 }
 
 /*
- * See if there is a recovery command file (recovery.trigger), and if so
- * read in parameters for archive recovery and XLOG streaming.
- *
- * The file is parsed using the main configuration parser.
+ * Check recovery parameters and determine recovery target timeline.
  */
 static void
-readRecoveryCommandFile(void)
+CheckRecoveryParameters(void)
 {
-	FILE	   *fd;
-	TimeLineID	rtli = 0;
-	bool		rtliGiven = false;
-	ConfigVariable *item,
-			   *head = NULL,
-			   *tail = NULL;
-	bool		recoveryTargetActionSet = false;
-
-
-	fd = AllocateFile(RECOVERY_COMMAND_FILE, "r");
-	if (fd == NULL)
-	{
-		if (errno == ENOENT)
-			return;				/* not there, so no archive recovery */
-		ereport(FATAL,
-				(errcode_for_file_access(),
-				 errmsg("could not open recovery command file \"%s\": %m",
-						RECOVERY_COMMAND_FILE)));
-	}
-
-	/*
-	 * Since we're asking ParseConfigFp() to report errors as FATAL, there's
-	 * no need to check the return value.
-	 */
-	(void) ParseConfigFp(fd, RECOVERY_COMMAND_FILE, 0, FATAL, &head, &tail);
-
-	FreeFile(fd);
-
-	for (item = head; item; item = item->next)
-	{
-		if (strcmp(item->name, "restore_command") == 0)
-		{
-			recoveryRestoreCommand = pstrdup(item->value);
-			ereport(DEBUG2,
-					(errmsg_internal("restore_command = '%s'",
-									 recoveryRestoreCommand)));
-		}
-		else if (strcmp(item->name, "recovery_end_command") == 0)
-		{
-			recoveryEndCommand = pstrdup(item->value);
-			ereport(DEBUG2,
-					(errmsg_internal("recovery_end_command = '%s'",
-									 recoveryEndCommand)));
-		}
-		else if (strcmp(item->name, "archive_cleanup_command") == 0)
-		{
-			archiveCleanupCommand = pstrdup(item->value);
-			ereport(DEBUG2,
-					(errmsg_internal("archive_cleanup_command = '%s'",
-									 archiveCleanupCommand)));
-		}
-		else if (strcmp(item->name, "recovery_target_action") == 0)
-		{
-			if (strcmp(item->value, "pause") == 0)
-				recoveryTargetAction = RECOVERY_TARGET_ACTION_PAUSE;
-			else if (strcmp(item->value, "promote") == 0)
-				recoveryTargetAction = RECOVERY_TARGET_ACTION_PROMOTE;
-			else if (strcmp(item->value, "shutdown") == 0)
-				recoveryTargetAction = RECOVERY_TARGET_ACTION_SHUTDOWN;
-			else
-				ereport(ERROR,
-						(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
-				errmsg("invalid value for recovery parameter \"%s\": \"%s\"",
-					   "recovery_target_action",
-					   item->value),
-						 errhint("Valid values are \"pause\", \"promote\", and \"shutdown\".")));
-
-			ereport(DEBUG2,
-					(errmsg_internal("recovery_target_action = '%s'",
-									 item->value)));
-
-			recoveryTargetActionSet = true;
-		}
-		else if (strcmp(item->name, "recovery_target_timeline") == 0)
-		{
-			rtliGiven = true;
-			if (strcmp(item->value, "latest") == 0)
-				rtli = 0;
-			else
-			{
-				errno = 0;
-				rtli = (TimeLineID) strtoul(item->value, NULL, 0);
-				if (errno == EINVAL || errno == ERANGE)
-					ereport(FATAL,
-							(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
-							 errmsg("recovery_target_timeline is not a valid number: \"%s\"",
-									item->value)));
-			}
-			if (rtli)
-				ereport(DEBUG2,
-				   (errmsg_internal("recovery_target_timeline = %u", rtli)));
-			else
-				ereport(DEBUG2,
-					 (errmsg_internal("recovery_target_timeline = latest")));
-		}
-		else if (strcmp(item->name, "recovery_target_xid") == 0)
-		{
-			errno = 0;
-			recoveryTargetXid = (TransactionId) strtoul(item->value, NULL, 0);
-			if (errno == EINVAL || errno == ERANGE)
-				ereport(FATAL,
-						(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
-				  errmsg("recovery_target_xid is not a valid number: \"%s\"",
-						 item->value)));
-			ereport(DEBUG2,
-					(errmsg_internal("recovery_target_xid = %u",
-									 recoveryTargetXid)));
-			recoveryTarget = RECOVERY_TARGET_XID;
-		}
-		else if (strcmp(item->name, "recovery_target_time") == 0)
-		{
-			recoveryTarget = RECOVERY_TARGET_TIME;
-
-			/*
-			 * Convert the time string given by the user to TimestampTz form.
-			 */
-			recoveryTargetTime =
-				DatumGetTimestampTz(DirectFunctionCall3(timestamptz_in,
-												CStringGetDatum(item->value),
-												ObjectIdGetDatum(InvalidOid),
-														Int32GetDatum(-1)));
-			ereport(DEBUG2,
-					(errmsg_internal("recovery_target_time = '%s'",
-								   timestamptz_to_str(recoveryTargetTime))));
-		}
-		else if (strcmp(item->name, "recovery_target_name") == 0)
-		{
-			recoveryTarget = RECOVERY_TARGET_NAME;
-
-			recoveryTargetName = pstrdup(item->value);
-			if (strlen(recoveryTargetName) >= MAXFNAMELEN)
-				ereport(FATAL,
-						(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
-						 errmsg("recovery_target_name is too long (maximum %d characters)",
-								MAXFNAMELEN - 1)));
-
-			ereport(DEBUG2,
-					(errmsg_internal("recovery_target_name = '%s'",
-									 recoveryTargetName)));
-		}
-		else if (strcmp(item->name, "recovery_target_lsn") == 0)
-		{
-			recoveryTarget = RECOVERY_TARGET_LSN;
-
-			/*
-			 * Convert the LSN string given by the user to XLogRecPtr form.
-			 */
-			recoveryTargetLSN =
-				DatumGetLSN(DirectFunctionCall3(pg_lsn_in,
-												CStringGetDatum(item->value),
-												ObjectIdGetDatum(InvalidOid),
-														Int32GetDatum(-1)));
-			ereport(DEBUG2,
-					(errmsg_internal("recovery_target_lsn = '%X/%X'",
-									 (uint32) (recoveryTargetLSN >> 32),
-									 (uint32) recoveryTargetLSN)));
-		}
-		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 value for recovery parameter \"%s\": \"%s\"",
-					   "recovery_target",
-					   item->value),
-					   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)
-		{
-			/*
-			 * does nothing if a recovery_target is not also set
-			 */
-			if (!parse_bool(item->value, &recoveryTargetInclusive))
-				ereport(ERROR,
-						(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
-						 errmsg("parameter \"%s\" requires a Boolean value",
-								"recovery_target_inclusive")));
-			ereport(DEBUG2,
-					(errmsg_internal("recovery_target_inclusive = %s",
-									 item->value)));
-		}
-		else if (strcmp(item->name, "standby_mode") == 0)
-		{
-			if (!parse_bool(item->value, &StandbyModeRequested))
-				ereport(ERROR,
-						(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
-						 errmsg("parameter \"%s\" requires a Boolean value",
-								"standby_mode")));
-			ereport(DEBUG2,
-					(errmsg_internal("standby_mode = '%s'", item->value)));
-		}
-		else if (strcmp(item->name, "primary_conninfo") == 0)
-		{
-			PrimaryConnInfo = pstrdup(item->value);
-			ereport(DEBUG2,
-					(errmsg_internal("primary_conninfo = '%s'",
-									 PrimaryConnInfo)));
-		}
-		else if (strcmp(item->name, "primary_slot_name") == 0)
-		{
-			ReplicationSlotValidateName(item->value, ERROR);
-			PrimarySlotName = pstrdup(item->value);
-			ereport(DEBUG2,
-					(errmsg_internal("primary_slot_name = '%s'",
-									 PrimarySlotName)));
-		}
-		else if (strcmp(item->name, "recovery_min_apply_delay") == 0)
-		{
-			const char *hintmsg;
-
-			if (!parse_int(item->value, &recovery_min_apply_delay, GUC_UNIT_MS,
-						   &hintmsg))
-				ereport(ERROR,
-						(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
-						 errmsg("parameter \"%s\" requires a temporal value",
-								"recovery_min_apply_delay"),
-						 hintmsg ? errhint("%s", _(hintmsg)) : 0));
-			ereport(DEBUG2,
-					(errmsg_internal("recovery_min_apply_delay = '%s'", item->value)));
-		}
-		else
-			ereport(FATAL,
-					(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
-					 errmsg("unrecognized recovery parameter \"%s\"",
-							item->name)));
-	}
-
-	/*
-	 * Check for compulsory parameters
-	 */
-	if (StandbyModeRequested)
-	{
-		if (PrimaryConnInfo == NULL && recoveryRestoreCommand == NULL)
-			ereport(WARNING,
-					(errmsg("recovery command file \"%s\" specified neither primary_conninfo nor restore_command",
-							RECOVERY_COMMAND_FILE),
-					 errhint("The database server will regularly poll the pg_xlog subdirectory to check for files placed there.")));
-	}
-	else
-	{
-		if (recoveryRestoreCommand == NULL)
-			ereport(FATAL,
-					(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
-					 errmsg("recovery command file \"%s\" must specify restore_command when standby mode is not enabled",
-							RECOVERY_COMMAND_FILE)));
-	}
+ 	/*
+ 	 * Check for compulsory parameters
+ 	 */
+	if (standby_mode && !restore_command[0] && !primary_conninfo[0])
+		ereport(WARNING,
+				(errmsg("neither primary_conninfo nor restore_command is specified"),
+				 errhint("The database server will regularly poll the pg_xlog subdirectory to "
+						 "check for files placed there until either of them is set in postgresql.conf.")));
 
+	CheckRestoreCommandSet();
+ 
 	/*
-	 * Override any inconsistent requests. Not that this is a change of
+	 * Override any inconsistent requests. Note that this is a change of
 	 * behaviour in 9.5; prior to this we simply ignored a request to pause if
 	 * hot_standby = off, which was surprising behaviour.
 	 */
-	if (recoveryTargetAction == RECOVERY_TARGET_ACTION_PAUSE &&
-		recoveryTargetActionSet &&
+	if (recovery_target_action == RECOVERY_TARGET_ACTION_PAUSE &&
+		strcmp(recovery_target_action_string, "") == 0 &&
 		!EnableHotStandby)
-		recoveryTargetAction = RECOVERY_TARGET_ACTION_SHUTDOWN;
+		recovery_target_action = RECOVERY_TARGET_ACTION_SHUTDOWN;
 
 	/*
 	 * We don't support standby_mode in standalone backends; that requires
 	 * other processes such as the WAL receiver to be alive.
 	 */
-	if (StandbyModeRequested && !IsUnderPostmaster)
+	if (standby_mode && !IsUnderPostmaster)
 		ereport(FATAL,
 				(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
 			errmsg("standby mode is not supported by single-user servers")));
 
-	/* Enable fetching from archive recovery area */
-	ArchiveRecoveryRequested = true;
-
 	/*
 	 * If user specified recovery_target_timeline, validate it or compute the
 	 * "latest" value.  We can't do this until after we've gotten the restore
 	 * command and set InArchiveRecovery, because we need to fetch timeline
 	 * history files from the archive.
 	 */
-	if (rtliGiven)
+	if (strcmp(recovery_target_timeline_string, "") != 0)
 	{
-		if (rtli)
+		if (recovery_target_timeline)
 		{
 			/* Timeline 1 does not have a history file, all else should */
-			if (rtli != 1 && !existsTimeLineHistory(rtli))
+			if (recovery_target_timeline != 1 && !existsTimeLineHistory(recovery_target_timeline))
 				ereport(FATAL,
 						(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
 						 errmsg("recovery target timeline %u does not exist",
-								rtli)));
-			recoveryTargetTLI = rtli;
+								recovery_target_timeline)));
+			recoveryTargetTLI = recovery_target_timeline;
 			recoveryTargetIsLatest = false;
 		}
 		else
@@ -5237,8 +4995,6 @@ readRecoveryCommandFile(void)
 			recoveryTargetIsLatest = true;
 		}
 	}
-
-	FreeConfigVariables(head);
 }
 
 /*
@@ -5401,7 +5157,7 @@ recoveryStopsBefore(XLogReaderState *record)
 	TransactionId recordXid;
 
 	/* Check if we should stop as soon as reaching consistency */
-	if (recoveryTarget == RECOVERY_TARGET_IMMEDIATE && reachedConsistency)
+	if (recovery_target == RECOVERY_TARGET_IMMEDIATE && reachedConsistency)
 	{
 		ereport(LOG,
 				(errmsg("recovery stopping after reaching consistency")));
@@ -5415,9 +5171,9 @@ recoveryStopsBefore(XLogReaderState *record)
 	}
 
 	/* Check if target LSN has been reached */
-	if (recoveryTarget == RECOVERY_TARGET_LSN &&
-		!recoveryTargetInclusive &&
-		record->ReadRecPtr >= recoveryTargetLSN)
+	if (recovery_target == RECOVERY_TARGET_LSN &&
+		!recovery_target_inclusive &&
+		record->ReadRecPtr >= recovery_target_lsn)
 	{
 		recoveryStopAfter = false;
 		recoveryStopXid = InvalidTransactionId;
@@ -5472,7 +5228,7 @@ recoveryStopsBefore(XLogReaderState *record)
 	else
 		return false;
 
-	if (recoveryTarget == RECOVERY_TARGET_XID && !recoveryTargetInclusive)
+	if (recovery_target == RECOVERY_TARGET_XID && !recovery_target_inclusive)
 	{
 		/*
 		 * There can be only one transaction end record with this exact
@@ -5483,10 +5239,10 @@ recoveryStopsBefore(XLogReaderState *record)
 		 * they complete. A higher numbered xid will complete before you about
 		 * 50% of the time...
 		 */
-		stopsHere = (recordXid == recoveryTargetXid);
+		stopsHere = (recordXid == recovery_target_xid);
 	}
 
-	if (recoveryTarget == RECOVERY_TARGET_TIME &&
+	if (recovery_target == RECOVERY_TARGET_TIME &&
 		getRecordTimestamp(record, &recordXtime))
 	{
 		/*
@@ -5494,14 +5250,15 @@ recoveryStopsBefore(XLogReaderState *record)
 		 * we stop after the last one, if we are inclusive, or stop at the
 		 * first one if we are exclusive
 		 */
-		if (recoveryTargetInclusive)
-			stopsHere = (recordXtime > recoveryTargetTime);
+		if (recovery_target_inclusive)
+			stopsHere = (recordXtime > recovery_target_time);
 		else
-			stopsHere = (recordXtime >= recoveryTargetTime);
+			stopsHere = (recordXtime >= recovery_target_time);
 	}
 
 	if (stopsHere)
 	{
+		recoveryStopTarget = recovery_target;
 		recoveryStopAfter = false;
 		recoveryStopXid = recordXid;
 		recoveryStopTime = recordXtime;
@@ -5548,14 +5305,14 @@ recoveryStopsAfter(XLogReaderState *record)
 	 * There can be many restore points that share the same name; we stop at
 	 * the first one.
 	 */
-	if (recoveryTarget == RECOVERY_TARGET_NAME &&
+	if (recovery_target == RECOVERY_TARGET_NAME &&
 		rmid == RM_XLOG_ID && info == XLOG_RESTORE_POINT)
 	{
 		xl_restore_point *recordRestorePointData;
 
 		recordRestorePointData = (xl_restore_point *) XLogRecGetData(record);
 
-		if (strcmp(recordRestorePointData->rp_name, recoveryTargetName) == 0)
+		if (strcmp(recordRestorePointData->rp_name, recovery_target_name) == 0)
 		{
 			recoveryStopAfter = true;
 			recoveryStopXid = InvalidTransactionId;
@@ -5572,9 +5329,9 @@ recoveryStopsAfter(XLogReaderState *record)
 	}
 
 	/* Check if the target LSN has been reached */
-	if (recoveryTarget == RECOVERY_TARGET_LSN &&
-		recoveryTargetInclusive &&
-		record->ReadRecPtr >= recoveryTargetLSN)
+	if (recovery_target == RECOVERY_TARGET_LSN &&
+		recovery_target_inclusive &&
+		record->ReadRecPtr >= recovery_target_lsn)
 	{
 		recoveryStopAfter = true;
 		recoveryStopXid = InvalidTransactionId;
@@ -5637,8 +5394,8 @@ recoveryStopsAfter(XLogReaderState *record)
 		 * they complete. A higher numbered xid will complete before you about
 		 * 50% of the time...
 		 */
-		if (recoveryTarget == RECOVERY_TARGET_XID && recoveryTargetInclusive &&
-			recordXid == recoveryTargetXid)
+		if (recovery_target == RECOVERY_TARGET_XID && recovery_target_inclusive &&
+			recordXid == recovery_target_xid)
 		{
 			recoveryStopAfter = true;
 			recoveryStopXid = recordXid;
@@ -5667,7 +5424,7 @@ recoveryStopsAfter(XLogReaderState *record)
 	}
 
 	/* Check if we should stop as soon as reaching consistency */
-	if (recoveryTarget == RECOVERY_TARGET_IMMEDIATE && reachedConsistency)
+	if (recovery_target == RECOVERY_TARGET_IMMEDIATE && reachedConsistency)
 	{
 		ereport(LOG,
 				(errmsg("recovery stopping after reaching consistency")));
@@ -5964,6 +5721,19 @@ CheckRequiredParameterValues(void)
 }
 
 /*
+ * Check to see if restore_command must be set for archive recovery
+ * when standby mode is not enabled
+ */
+void
+CheckRestoreCommandSet(void)
+{
+	if (InArchiveRecovery && !standby_mode && !restore_command[0])
+		ereport(FATAL,
+				(errmsg("restore_command must be specified for archive recovery "
+						"when standby mode is not enabled")));
+}
+
+/*
  * This must be called ONCE during postmaster or standalone-backend startup
  */
 void
@@ -6074,40 +5844,32 @@ StartupXLOG(void)
 	 * Check for recovery trigger file, and if so set up state for offline
 	 * recovery
 	 */
-	CheckForRecoveryTrigger();
-	readRecoveryCommandFile(); // remove this later
-
-	/*
-	 * Save archive_cleanup_command in shared memory so that other processes
-	 * can see it.
-	 */
-	strlcpy(XLogCtl->archiveCleanupCommand,
-			archiveCleanupCommand ? archiveCleanupCommand : "",
-			sizeof(XLogCtl->archiveCleanupCommand));
+	if (CheckForRecoveryTrigger())
+		CheckRecoveryParameters();
 
 	if (ArchiveRecoveryRequested)
 	{
 		if (StandbyModeRequested)
 			ereport(LOG,
 					(errmsg("entering standby mode")));
-		else if (recoveryTarget == RECOVERY_TARGET_XID)
+		else if (recovery_target == RECOVERY_TARGET_XID)
 			ereport(LOG,
 					(errmsg("starting point-in-time recovery to XID %u",
-							recoveryTargetXid)));
-		else if (recoveryTarget == RECOVERY_TARGET_TIME)
+							recovery_target_xid)));
+		else if (recovery_target == RECOVERY_TARGET_TIME)
 			ereport(LOG,
 					(errmsg("starting point-in-time recovery to %s",
-							timestamptz_to_str(recoveryTargetTime))));
-		else if (recoveryTarget == RECOVERY_TARGET_NAME)
+							timestamptz_to_str(recovery_target_time))));
+		else if (recovery_target == RECOVERY_TARGET_NAME)
 			ereport(LOG,
 					(errmsg("starting point-in-time recovery to \"%s\"",
-							recoveryTargetName)));
-		else if (recoveryTarget == RECOVERY_TARGET_LSN)
+							recovery_target_name)));
+		else if (recovery_target == RECOVERY_TARGET_LSN)
 			ereport(LOG,
 					(errmsg("starting point-in-time recovery to WAL position (LSN) \"%X/%X\"",
-							(uint32) (recoveryTargetLSN >> 32),
-							(uint32) recoveryTargetLSN)));
-		else if (recoveryTarget == RECOVERY_TARGET_IMMEDIATE)
+							(uint32) (recovery_target_lsn >> 32),
+							(uint32) recovery_target_lsn)));
+		else if (recovery_target == RECOVERY_TARGET_IMMEDIATE)
 			ereport(LOG,
 					(errmsg("starting point-in-time recovery to earliest consistent point")));
 		else
@@ -6119,7 +5881,7 @@ StartupXLOG(void)
 	 * Take ownership of the wakeup latch if we're going to sleep during
 	 * recovery.
 	 */
-	if (StandbyModeRequested)
+	if (standby_mode)
 		OwnLatch(&XLogCtl->recoveryWakeupLatch);
 
 	/* Set up XLOG reader facility */
@@ -6143,8 +5905,6 @@ StartupXLOG(void)
 		 * archive recovery directly.
 		 */
 		InArchiveRecovery = true;
-		if (StandbyModeRequested)
-			StandbyMode = true;
 
 		/*
 		 * When a backup_label file is present, we want to roll forward from
@@ -6270,8 +6030,6 @@ StartupXLOG(void)
 			 ControlFile->state == DB_SHUTDOWNED))
 		{
 			InArchiveRecovery = true;
-			if (StandbyModeRequested)
-				StandbyMode = true;
 		}
 
 		/*
@@ -6287,7 +6045,7 @@ StartupXLOG(void)
 					(errmsg("checkpoint record is at %X/%X",
 				   (uint32) (checkPointLoc >> 32), (uint32) checkPointLoc)));
 		}
-		else if (StandbyMode)
+		else if (standby_mode)
 		{
 			/*
 			 * The last valid checkpoint record required for a streaming
@@ -7006,7 +6764,7 @@ StartupXLOG(void)
 				 * this, Resource Managers may choose to do permanent
 				 * corrective actions at end of recovery.
 				 */
-				switch (recoveryTargetAction)
+				switch (recovery_target_action)
 				{
 					case RECOVERY_TARGET_ACTION_SHUTDOWN:
 
@@ -7075,7 +6833,7 @@ StartupXLOG(void)
 	 * We don't need the latch anymore. It's not strictly necessary to disown
 	 * it, but let's do it for the sake of tidiness.
 	 */
-	if (StandbyModeRequested)
+	if (standby_mode)
 		DisownLatch(&XLogCtl->recoveryWakeupLatch);
 
 	/*
@@ -7083,7 +6841,7 @@ StartupXLOG(void)
 	 * recovery to force fetching the files (which would be required at end of
 	 * recovery, e.g., timeline history file) from archive or pg_xlog.
 	 */
-	StandbyMode = false;
+	SetConfigOption("standby_mode", "false", PGC_POSTMASTER, PGC_S_OVERRIDE);
 
 	/*
 	 * Re-fetch the last valid or last applied record, so we can identify the
@@ -7166,27 +6924,27 @@ StartupXLOG(void)
 		 * Create a comment for the history file to explain why and where
 		 * timeline changed.
 		 */
-		if (recoveryTarget == RECOVERY_TARGET_XID)
+		if (recoveryStopTarget == RECOVERY_TARGET_XID)
 			snprintf(reason, sizeof(reason),
 					 "%s transaction %u",
 					 recoveryStopAfter ? "after" : "before",
 					 recoveryStopXid);
-		else if (recoveryTarget == RECOVERY_TARGET_TIME)
+		else if (recoveryStopTarget == RECOVERY_TARGET_TIME)
 			snprintf(reason, sizeof(reason),
 					 "%s %s\n",
 					 recoveryStopAfter ? "after" : "before",
 					 timestamptz_to_str(recoveryStopTime));
-		else if (recoveryTarget == RECOVERY_TARGET_LSN)
+		else if (recoveryStopTarget == RECOVERY_TARGET_LSN)
 			snprintf(reason, sizeof(reason),
 					 "%s LSN %X/%X\n",
 					 recoveryStopAfter ? "after" : "before",
 					 (uint32 ) (recoveryStopLSN >> 32),
 					 (uint32) recoveryStopLSN);
-		else if (recoveryTarget == RECOVERY_TARGET_NAME)
+		else if (recoveryStopTarget == RECOVERY_TARGET_NAME)
 			snprintf(reason, sizeof(reason),
 					 "at restore point \"%s\"",
 					 recoveryStopName);
-		else if (recoveryTarget == RECOVERY_TARGET_IMMEDIATE)
+		else if (recoveryStopTarget == RECOVERY_TARGET_IMMEDIATE)
 			snprintf(reason, sizeof(reason), "reached consistency");
 		else
 			snprintf(reason, sizeof(reason), "no recovery target specified");
@@ -7325,8 +7083,8 @@ StartupXLOG(void)
 		/*
 		 * And finally, execute the recovery_end_command, if any.
 		 */
-		if (recoveryEndCommand)
-			ExecuteRecoveryCommand(recoveryEndCommand,
+		if (recovery_end_command)
+			ExecuteRecoveryCommand(recovery_end_command,
 								   "recovery_end_command",
 								   true);
 	}
@@ -8972,8 +8730,8 @@ CreateRestartPoint(int flags)
 	/*
 	 * Finally, execute archive_cleanup_command, if any.
 	 */
-	if (XLogCtl->archiveCleanupCommand[0])
-		ExecuteRecoveryCommand(XLogCtl->archiveCleanupCommand,
+	if (archive_cleanup_command[0])
+		ExecuteRecoveryCommand(archive_cleanup_command,
 							   "archive_cleanup_command",
 							   false);
 
@@ -11154,7 +10912,7 @@ next_record_is_invalid:
 	readSource = 0;
 
 	/* In standby-mode, keep trying */
-	if (StandbyMode)
+	if (standby_mode)
 		goto retry;
 	else
 		return -1;
@@ -11240,7 +10998,7 @@ WaitForWALToBecomeAvailable(XLogRecPtr RecPtr, bool randAccess,
 					 * trigger file, we still finish replaying as much as we
 					 * can from archive and pg_xlog before failover.
 					 */
-					if (StandbyMode && CheckForPromoteTrigger())
+					if (standby_mode && CheckForPromoteTrigger())
 					{
 						ShutdownWalRcv();
 						return false;
@@ -11250,7 +11008,7 @@ WaitForWALToBecomeAvailable(XLogRecPtr RecPtr, bool randAccess,
 					 * Not in standby mode, and we've now tried the archive
 					 * and pg_xlog.
 					 */
-					if (!StandbyMode)
+					if (!standby_mode)
 						return false;
 
 					/*
@@ -11263,7 +11021,7 @@ WaitForWALToBecomeAvailable(XLogRecPtr RecPtr, bool randAccess,
 					 * that when we later jump backwards to start redo at
 					 * RedoStartLSN, we will have the logs streamed already.
 					 */
-					if (PrimaryConnInfo)
+					if (primary_conninfo)
 					{
 						XLogRecPtr	ptr;
 						TimeLineID	tli;
@@ -11284,8 +11042,8 @@ WaitForWALToBecomeAvailable(XLogRecPtr RecPtr, bool randAccess,
 									 tli, curFileTLI);
 						}
 						curFileTLI = tli;
-						RequestXLogStreaming(tli, ptr, PrimaryConnInfo,
-											 PrimarySlotName);
+						RequestXLogStreaming(tli, ptr, primary_conninfo,
+											 primary_slot_name);
 						receivedUpto = 0;
 					}
 
diff --git a/src/backend/access/transam/xlogarchive.c b/src/backend/access/transam/xlogarchive.c
index d153a44..585c3ec 100644
--- a/src/backend/access/transam/xlogarchive.c
+++ b/src/backend/access/transam/xlogarchive.c
@@ -67,7 +67,7 @@ RestoreArchivedFile(char *path, const char *xlogfname,
 	TimeLineID	restartTli;
 
 	/* In standby mode, restore_command might not be supplied */
-	if (recoveryRestoreCommand == NULL)
+	if (!restore_command[0])
 		goto not_available;
 
 	/*
@@ -150,7 +150,7 @@ RestoreArchivedFile(char *path, const char *xlogfname,
 	endp = xlogRestoreCmd + MAXPGPATH - 1;
 	*endp = '\0';
 
-	for (sp = recoveryRestoreCommand; *sp; sp++)
+	for (sp = restore_command; *sp; sp++)
 	{
 		if (*sp == '%')
 		{
@@ -236,7 +236,7 @@ RestoreArchivedFile(char *path, const char *xlogfname,
 				 * incorrectly conclude we've reached the end of WAL and we're
 				 * done recovering ...
 				 */
-				if (StandbyMode && stat_buf.st_size < expectedSize)
+				if (standby_mode && stat_buf.st_size < expectedSize)
 					elevel = DEBUG1;
 				else
 					elevel = FATAL;
@@ -409,7 +409,7 @@ ExecuteRecoveryCommand(char *command, char *commandName, bool failOnSignal)
 
 		ereport((signaled && failOnSignal) ? FATAL : WARNING,
 		/*------
-		   translator: First %s represents a recovery.conf parameter name like
+		   translator: First %s represents a postgresql.conf parameter name like
 		  "recovery_end_command", the 2nd is the value of that parameter, the
 		  third an already translated error message. */
 				(errmsg("%s \"%s\": %s", commandName,
diff --git a/src/backend/postmaster/startup.c b/src/backend/postmaster/startup.c
index a7ae7e3..1a6adac 100644
--- a/src/backend/postmaster/startup.c
+++ b/src/backend/postmaster/startup.c
@@ -153,6 +153,8 @@ HandleStartupProcInterrupts(void)
 	{
 		got_SIGHUP = false;
 		ProcessConfigFile(PGC_SIGHUP);
+
+		CheckRestoreCommandSet();
 	}
 
 	/*
diff --git a/src/backend/replication/walreceiver.c b/src/backend/replication/walreceiver.c
index 413ee3a..ec2f252 100644
--- a/src/backend/replication/walreceiver.c
+++ b/src/backend/replication/walreceiver.c
@@ -415,9 +415,33 @@ WalReceiverMain(void)
 
 				if (got_SIGHUP)
 				{
+					char	*conninfo = pstrdup(primary_conninfo);
+					char	*slotname = pstrdup(primary_slot_name);
+
 					got_SIGHUP = false;
 					ProcessConfigFile(PGC_SIGHUP);
 					XLogWalRcvSendHSFeedback(true);
+
+					/*
+					 * If primary_conninfo has been changed while walreceiver is running,
+					 * shut down walreceiver so that a new walreceiver is started and
+					 * initiates replication with the new primary_conninfo.
+					 */
+					if (strcmp(conninfo, primary_conninfo) != 0)
+						ereport(FATAL,
+								(errcode(ERRCODE_ADMIN_SHUTDOWN),
+								 errmsg("terminating walreceiver process because primary_conninfo was changed")));
+
+					/*
+					 * And the same for primary_slot_name.
+					 */
+					if (strcmp(slotname, primary_slot_name) != 0)
+						ereport(FATAL,
+								(errcode(ERRCODE_ADMIN_SHUTDOWN),
+								 errmsg("terminating walreceiver process because primary_slot_name was changed")));
+
+					pfree(conninfo);
+					pfree(slotname);
 				}
 
 				/* See if we can read data immediately */
diff --git a/src/backend/utils/misc/guc-file.l b/src/backend/utils/misc/guc-file.l
index dae5015..10c052c 100644
--- a/src/backend/utils/misc/guc-file.l
+++ b/src/backend/utils/misc/guc-file.l
@@ -197,6 +197,8 @@ ProcessConfigFileInternal(GucContext context, bool applySettings, int elevel)
 	 */
 	if (DataDir)
 	{
+		struct stat stat_buf;
+
 		if (!ParseConfigFile(PG_AUTOCONF_FILENAME, false,
 							 NULL, 0, 0, elevel,
 							 &head, &tail))
@@ -206,6 +208,22 @@ ProcessConfigFileInternal(GucContext context, bool applySettings, int elevel)
 			ConfFileWithError = PG_AUTOCONF_FILENAME;
 			goto bail_out;
 		}
+
+		/*
+		 * For backwards compatibility, we also read recovery.conf if
+		 * it exists.
+		 */
+                
+		if (stat("recovery.conf", &stat_buf) == 0 &&
+			!ParseConfigFile("recovery.conf", false,
+							 NULL, 0, 0, elevel,
+							 &head, &tail))
+		{
+			/* Syntax error(s) detected in the file, so bail out */
+			error = true;
+			ConfFileWithError = "recovery.conf";
+			goto bail_out;
+		}
 	}
 	else
 	{
diff --git a/src/backend/utils/misc/guc.c b/src/backend/utils/misc/guc.c
index c5178f7..f68e397 100644
--- a/src/backend/utils/misc/guc.c
+++ b/src/backend/utils/misc/guc.c
@@ -31,6 +31,7 @@
 #include "access/transam.h"
 #include "access/twophase.h"
 #include "access/xact.h"
+#include "access/xlog_internal.h"
 #include "catalog/namespace.h"
 #include "commands/async.h"
 #include "commands/prepare.h"
@@ -76,6 +77,7 @@
 #include "utils/guc_tables.h"
 #include "utils/memutils.h"
 #include "utils/pg_locale.h"
+#include "utils/pg_lsn.h"
 #include "utils/plancache.h"
 #include "utils/portal.h"
 #include "utils/ps_status.h"
@@ -181,6 +183,21 @@ static void assign_application_name(const char *newval, void *extra);
 static bool check_cluster_name(char **newval, void **extra, GucSource source);
 static const char *show_unix_socket_permissions(void);
 static const char *show_log_file_mode(void);
+static bool check_recovery_target(char **newval, void **extra, GucSource source);
+static void assign_recovery_target(const char *newval, void *extra);
+static bool check_recovery_target_xid(char **newval, void **extra, GucSource source);
+static void assign_recovery_target_xid(const char *newval, void *extra);
+static bool check_recovery_target_name(char **newval, void **extra, GucSource source);
+static void assign_recovery_target_name(const char *newval, void *extra);
+static bool check_recovery_target_time(char **newval, void **extra, GucSource source);
+static void assign_recovery_target_time(const char *newval, void *extra);
+static bool check_recovery_target_lsn(char **newval, void **extra, GucSource source);
+static void assign_recovery_target_lsn(const char *newval, void *extra);
+static bool check_recovery_target_timeline(char **newval, void **extra, GucSource source);
+static void assign_recovery_target_timeline(const char *newval, void *extra);
+static bool check_recovery_target_action(char **newval, void **extra, GucSource source);
+static void assign_recovery_target_action(const char *newval, void *extra);
+static bool check_primary_slot_name(char **newval, void **extra, GucSource source);
 
 /* Private functions in guc-file.l that need to be called from guc.c */
 static ConfigVariable *ProcessConfigFileInternal(GucContext context,
@@ -495,6 +512,8 @@ static bool data_checksums;
 static int	wal_segment_size;
 static bool integer_datetimes;
 static bool assert_enabled;
+static char *recovery_target_xid_string;
+static char *recovery_target_time_string;
 
 /* should be static, but commands/variable.c needs to get at this */
 char	   *role_string;
@@ -576,6 +595,10 @@ const char *const config_group_names[] =
 	gettext_noop("Write-Ahead Log / Checkpoints"),
 	/* WAL_ARCHIVING */
 	gettext_noop("Write-Ahead Log / Archiving"),
+	/* WAL_ARCHIVE_RECOVERY */
+	gettext_noop("Write-Ahead Log / Archive Recovery"),
+	/* WAL_RECOVERY_TARGET */
+	gettext_noop("Write-Ahead Log / Recovery Target"),
 	/* REPLICATION */
 	gettext_noop("Replication"),
 	/* REPLICATION_SENDING */
@@ -1553,6 +1576,26 @@ static struct config_bool ConfigureNamesBool[] =
 	},
 
 	{
+		{"recovery_target_inclusive", PGC_SIGHUP, WAL_RECOVERY_TARGET,
+			gettext_noop("Sets whether to include or exclude transaction with recovery target."),
+			NULL
+		},
+		&recovery_target_inclusive,
+		true,
+		NULL, NULL, NULL
+	},
+
+	{
+		{"standby_mode", PGC_POSTMASTER, REPLICATION_STANDBY,
+			gettext_noop("Sets whether to start the server as a standby."),
+			NULL
+		},
+		&standby_mode,
+		false,
+		NULL, NULL, NULL
+	},
+
+	{
 		{"hot_standby", PGC_POSTMASTER, REPLICATION_STANDBY,
 			gettext_noop("Allows connections and queries during recovery."),
 			NULL
@@ -1793,6 +1836,17 @@ static struct config_int ConfigureNamesInt[] =
 	},
 
 	{
+		{"recovery_min_apply_delay", PGC_SIGHUP, REPLICATION_STANDBY,
+			gettext_noop("Sets the minimum delay to apply changes during recovery."),
+			NULL,
+			GUC_UNIT_MS
+		},
+		&recovery_min_apply_delay,
+		0, 0, INT_MAX,
+		NULL, NULL, NULL
+	},
+
+	{
 		{"wal_receiver_status_interval", PGC_SIGHUP, REPLICATION_STANDBY,
 			gettext_noop("Sets the maximum interval between WAL receiver status reports to the primary."),
 			NULL,
@@ -2992,6 +3046,128 @@ static struct config_string ConfigureNamesString[] =
 	},
 
 	{
+		{"restore_command", PGC_SIGHUP, WAL_ARCHIVE_RECOVERY,
+			gettext_noop("Sets the shell command that will retrieve an archived WAL file."),
+			NULL
+		},
+		&restore_command,
+		"",
+		NULL, NULL, NULL
+	},
+
+	{
+		{"archive_cleanup_command", PGC_SIGHUP, WAL_ARCHIVE_RECOVERY,
+			gettext_noop("Sets the shell command that will be executed at every restartpoint."),
+			NULL
+		},
+		&archive_cleanup_command,
+		"",
+		NULL, NULL, NULL
+	},
+
+	{
+		{"recovery_end_command", PGC_SIGHUP, WAL_ARCHIVE_RECOVERY,
+			gettext_noop("Sets the shell command that will be executed once only at the end of recovery."),
+			NULL
+		},
+		&recovery_end_command,
+		"",
+		NULL, NULL, NULL
+	},
+
+	{
+		{"recovery_target", PGC_SIGHUP, WAL_RECOVERY_TARGET,
+			gettext_noop("Sets the desired recovery target."),
+			NULL
+		},
+		&recovery_target_string,
+		"",
+		check_recovery_target, assign_recovery_target, NULL
+	},
+
+	{
+		{"recovery_target_xid", PGC_SIGHUP, WAL_RECOVERY_TARGET,
+			gettext_noop("Sets the transaction ID up to which recovery will proceed."),
+			NULL
+		},
+		&recovery_target_xid_string,
+		"",
+		check_recovery_target_xid, assign_recovery_target_xid, NULL
+	},
+
+	{
+		{"recovery_target_name", PGC_SIGHUP, WAL_RECOVERY_TARGET,
+			gettext_noop("Sets the named restore point."),
+			NULL
+		},
+		&recovery_target_name,
+		"",
+		check_recovery_target_name, assign_recovery_target_name, NULL
+	},
+
+	{
+		{"recovery_target_time", PGC_SIGHUP, WAL_RECOVERY_TARGET,
+			gettext_noop("Sets the time stamp up to which recovery will proceed."),
+			NULL
+		},
+		&recovery_target_time_string,
+		"",
+		check_recovery_target_time, assign_recovery_target_time, NULL
+	},
+
+	{
+		{"recovery_target_lsn", PGC_SIGHUP, WAL_RECOVERY_TARGET,
+			gettext_noop("Sets the LSN up to which recovery will proceed."),
+			NULL
+		},
+		&recovery_target_lsn_string,
+		"",
+		check_recovery_target_lsn, assign_recovery_target_lsn, NULL
+	},
+
+	{
+		{"recovery_target_timeline", PGC_POSTMASTER, WAL_RECOVERY_TARGET,
+			gettext_noop("Sets recoverying into a particular timeline."),
+			NULL
+		},
+		&recovery_target_timeline_string,
+		"",
+		check_recovery_target_timeline, assign_recovery_target_timeline, NULL
+	},
+
+	{
+		{"recovery_target_action", PGC_POSTMASTER, WAL_RECOVERY_TARGET,
+			gettext_noop("Sets the action to perform upon reaching the recovery target."),
+			NULL
+		},
+		&recovery_target_action_string,
+		"",
+		check_recovery_target_action, assign_recovery_target_action, NULL
+	},
+
+	{
+		{"primary_conninfo", PGC_SIGHUP, REPLICATION_STANDBY,
+			gettext_noop("Sets the connection string to be used to connect with the primary."),
+			NULL,
+			GUC_SUPERUSER_ONLY
+		},
+		&primary_conninfo,
+		"",
+		NULL, NULL, NULL
+	},
+
+	{
+		{"primary_slot_name", PGC_SIGHUP, REPLICATION_STANDBY,
+			gettext_noop("Sets the name of the replication slot to use on the primary."),
+			NULL,
+			GUC_SUPERUSER_ONLY
+		},
+		&primary_slot_name,
+		"",
+		check_primary_slot_name, NULL, NULL
+	},
+
+	{
 		{"client_encoding", PGC_USERSET, CLIENT_CONN_LOCALE,
 			gettext_noop("Sets the client's character set encoding."),
 			NULL,
@@ -10301,4 +10477,291 @@ show_log_file_mode(void)
 	return buf;
 }
 
+static bool
+check_recovery_target(char **newval, void **extra, GucSource source)
+{
+	RecoveryTargetType rt = RECOVERY_TARGET_UNSET;
+	RecoveryTargetType *myextra;
+
+	if (strcmp(*newval, "immediate") == 0)
+		rt = RECOVERY_TARGET_IMMEDIATE;
+	else if (strcmp(*newval, "") != 0)
+	{
+		GUC_check_errdetail("recovery_target is not valid: \"%s\"", *newval);
+		return false;
+	}
+
+	myextra = (RecoveryTargetType *) guc_malloc(ERROR, sizeof(RecoveryTargetType));
+	*myextra = rt;
+	*extra = (void *) myextra;
+
+	return true;
+}
+
+static void
+assign_recovery_target(const char *newval, void *extra)
+{
+	recovery_target = *((RecoveryTargetType *) extra);
+}
+
+static bool
+check_recovery_target_xid(char **newval, void **extra, GucSource source)
+{
+	TransactionId   xid;
+	TransactionId   *myextra;
+
+	if (strcmp(*newval, "") == 0)
+		xid = InvalidTransactionId;
+	else
+	{
+		errno = 0;
+		xid = (TransactionId) strtoul(*newval, NULL, 0);
+		if (errno == EINVAL || errno == ERANGE)
+		{
+			GUC_check_errdetail("recovery_target_xid is not a valid number: \"%s\"",
+								*newval);
+			return false;
+		}
+	}
+
+	myextra = (TransactionId *) guc_malloc(ERROR, sizeof(TransactionId));
+	*myextra = xid;
+	*extra = (void *) myextra;
+
+	return true;
+}
+
+static void
+assign_recovery_target_xid(const char *newval, void *extra)
+{
+	recovery_target_xid = *((TransactionId *) extra);
+
+	if (recovery_target_xid != InvalidTransactionId)
+		recovery_target = RECOVERY_TARGET_XID;
+	else if (recovery_target_name && *recovery_target_name)
+		recovery_target = RECOVERY_TARGET_NAME;
+	else if (recovery_target_time != 0)
+		recovery_target = RECOVERY_TARGET_TIME;
+	else if (recovery_target_lsn != 0)
+		recovery_target = RECOVERY_TARGET_LSN;
+	else
+		recovery_target = RECOVERY_TARGET_UNSET;
+}
+
+static bool
+check_recovery_target_name(char **newval, void **extra, GucSource source)
+{
+	if (strlen(*newval) >= MAXFNAMELEN)
+	{
+		GUC_check_errdetail("\"recovery_target_name\" is too long (maximum %d characters)",
+							MAXFNAMELEN - 1);
+		return false;
+	}
+	return true;
+}
+
+static void
+assign_recovery_target_name(const char *newval, void *extra)
+{
+	if (recovery_target_xid != InvalidTransactionId)
+		recovery_target = RECOVERY_TARGET_XID;
+	else if (newval[0])
+		recovery_target = RECOVERY_TARGET_NAME;
+	else if (recovery_target_time != 0)
+		recovery_target = RECOVERY_TARGET_TIME;
+	else if (recovery_target_lsn != 0)
+		recovery_target = RECOVERY_TARGET_LSN;
+	else
+		recovery_target = RECOVERY_TARGET_UNSET;
+}
+
+static bool
+check_recovery_target_time(char **newval, void **extra, GucSource source)
+{
+	TimestampTz     time;
+	TimestampTz     *myextra;
+	MemoryContext oldcontext = CurrentMemoryContext;
+
+	PG_TRY();
+	{
+		time = (strcmp(*newval, "") == 0) ?
+			0 :
+			DatumGetTimestampTz(DirectFunctionCall3(timestamptz_in,
+													CStringGetDatum(*newval),
+													ObjectIdGetDatum(InvalidOid),
+													Int32GetDatum(-1)));
+	}
+	PG_CATCH();
+	{
+		ErrorData  *edata;
+
+		/* Save error info */
+		MemoryContextSwitchTo(oldcontext);
+		edata = CopyErrorData();
+		FlushErrorState();
+
+		/* Pass the error message */
+		GUC_check_errdetail("%s", edata->message);
+		FreeErrorData(edata);
+		return false;
+	}
+	PG_END_TRY();
+
+	myextra = (TimestampTz *) guc_malloc(ERROR, sizeof(TimestampTz));
+	*myextra = time;
+	*extra = (void *) myextra;
+
+	return true;
+}
+
+static void
+assign_recovery_target_time(const char *newval, void *extra)
+{
+	recovery_target_time = *((TimestampTz *) extra);
+
+	if (recovery_target_xid != InvalidTransactionId)
+		recovery_target = RECOVERY_TARGET_XID;
+	else if (recovery_target_name && *recovery_target_name)
+		recovery_target = RECOVERY_TARGET_NAME;
+	else if (recovery_target_time != 0)
+		recovery_target = RECOVERY_TARGET_TIME;
+	else if (recovery_target_lsn != 0)
+		recovery_target = RECOVERY_TARGET_LSN;
+	else
+		recovery_target = RECOVERY_TARGET_UNSET;
+}
+
+static bool
+check_recovery_target_lsn(char **newval, void **extra, GucSource source)
+{
+	XLogRecPtr		lsn;
+	XLogRecPtr	   *myextra;
+	MemoryContext oldcontext = CurrentMemoryContext;
+
+	PG_TRY();
+	{
+		lsn = (strcmp(*newval, "") == 0) ?
+			0 :
+			DatumGetLSN(DirectFunctionCall3(pg_lsn_in,
+											CStringGetDatum(*newval),
+											ObjectIdGetDatum(InvalidOid),
+											Int32GetDatum(-1)));
+	}
+	PG_CATCH();
+	{
+		ErrorData  *edata;
+
+		/* Save error info */
+		MemoryContextSwitchTo(oldcontext);
+		edata = CopyErrorData();
+		FlushErrorState();
+
+		/* Pass the error message */
+		GUC_check_errdetail("%s", edata->message);
+		FreeErrorData(edata);
+		return false;
+	}
+	PG_END_TRY();
+
+	myextra = (XLogRecPtr *) guc_malloc(ERROR, sizeof(XLogRecPtr));
+	*myextra = lsn;
+	*extra = (void *) myextra;
+
+	return true;
+}
+
+static void
+assign_recovery_target_lsn(const char *newval, void *extra)
+{
+	recovery_target_lsn = *((XLogRecPtr *) extra);
+
+	if (recovery_target_xid != InvalidTransactionId)
+		recovery_target = RECOVERY_TARGET_XID;
+	else if (recovery_target_name && *recovery_target_name)
+		recovery_target = RECOVERY_TARGET_NAME;
+	else if (recovery_target_time != 0)
+		recovery_target = RECOVERY_TARGET_TIME;
+	else if (recovery_target_lsn != 0)
+		recovery_target = RECOVERY_TARGET_LSN;
+	else
+		recovery_target = RECOVERY_TARGET_UNSET;
+}
+
+static bool
+check_recovery_target_timeline(char **newval, void **extra, GucSource source)
+{
+	TimeLineID	tli = 0;
+	TimeLineID	*myextra;
+
+	if (strcmp(*newval, "") == 0 || strcmp(*newval, "latest") == 0)
+		tli = 0;
+	else
+	{
+		errno = 0;
+		tli = (TimeLineID) strtoul(*newval, NULL, 0);
+		if (errno == EINVAL || errno == ERANGE)
+		{
+			GUC_check_errdetail("recovery_target_timeline is not a valid number: \"%s\"",
+								*newval);
+			return false;
+		}
+	}
+
+	myextra = (TimeLineID *) guc_malloc(ERROR, sizeof(TimeLineID));
+	*myextra = tli;
+	*extra = (void *) myextra;
+
+	return true;
+}
+
+static void
+assign_recovery_target_timeline(const char *newval, void *extra)
+{
+	recovery_target_timeline = *((TimeLineID *) extra);
+}
+
+static bool
+check_recovery_target_action(char **newval, void **extra, GucSource source)
+{
+	RecoveryTargetAction rta = RECOVERY_TARGET_ACTION_PAUSE;
+	RecoveryTargetAction *myextra;
+
+	if (strcmp(*newval, "pause") == 0)
+		rta = RECOVERY_TARGET_ACTION_PAUSE;
+	else if (strcmp(*newval, "promote") == 0)
+		rta = RECOVERY_TARGET_ACTION_PROMOTE;
+	else if (strcmp(*newval, "shutdown") == 0)
+		rta = RECOVERY_TARGET_ACTION_SHUTDOWN;
+	else if (strcmp(*newval, "") != 0)
+	{
+		GUC_check_errdetail("recovery_target_action is not valid: \"%s\"", *newval);
+		return false;
+	}
+
+	myextra = (RecoveryTargetAction *) guc_malloc(ERROR, sizeof(RecoveryTargetAction));
+	*myextra = rta;
+	*extra = (void *) myextra;
+
+	return true;
+}
+
+static void
+assign_recovery_target_action(const char *newval, void *extra)
+{
+	recovery_target_action = *((RecoveryTargetAction *) extra);
+}
+
+static bool
+check_primary_slot_name(char **newval, void **extra, GucSource source)
+{
+	if (strcmp(*newval,"") != 0 &&
+		!ReplicationSlotValidateName(*newval, WARNING))
+	{
+		GUC_check_errdetail("primary_slot_name is not valid: \"%s\"", *newval);
+		return false;
+	}
+
+	return true;
+}
+
 #include "guc-file.c"
diff --git a/src/backend/utils/misc/postgresql.conf.sample b/src/backend/utils/misc/postgresql.conf.sample
index 6d0666c..0edbf91 100644
--- a/src/backend/utils/misc/postgresql.conf.sample
+++ b/src/backend/utils/misc/postgresql.conf.sample
@@ -222,6 +222,25 @@
 #archive_timeout = 0		# force a logfile segment switch after this
 				# number of seconds; 0 disables
 
+# - Archive Recovery -
+#restore_command = ''		# command to use to restore an archived logfile segment
+				# placeholders: %p = path of file to restore
+				#               %f = file name only
+				# e.g. 'cp /mnt/server/archivedir/%f %p'
+#archive_cleanup_command = ''	# command to execute at every restartpoint
+#recovery_end_command = ''	# command to execute at completion of recovery
+
+# - Recovery Target -
+#recovery_target_action = 'pause' 	# 'pause', 'promote', 'shutdown'
+#recovery_target=immediate
+#recovery_target_xid = ''
+#recovery_target_name = ''
+#recovery_target_time = ''
+#recovery_target_lsn = ''
+#recovery_target_timeline = ''		# timeline ID or 'latest'
+					# (change requires restart)
+#recovery_target_inclusive = on
+
 
 #------------------------------------------------------------------------------
 # REPLICATION
@@ -254,6 +273,10 @@
 
 # These settings are ignored on a master server.
 
+#standby_mode = off			# "on" starts the server as a standby
+					# (change requires restart)
+#primary_conninfo = ''			# connection string to connect to the master
+#trigger_file = ''			# trigger file to promote the standby
 #hot_standby = off			# "on" allows queries during recovery
 					# (change requires restart)
 #max_standby_archive_delay = 30s	# max delay before canceling queries
diff --git a/src/bin/pg_archivecleanup/pg_archivecleanup.c b/src/bin/pg_archivecleanup/pg_archivecleanup.c
index 319038f..abbe367 100644
--- a/src/bin/pg_archivecleanup/pg_archivecleanup.c
+++ b/src/bin/pg_archivecleanup/pg_archivecleanup.c
@@ -270,7 +270,7 @@ usage(void)
 	printf("  -x EXT         clean up files if they have this extension\n");
 	printf("  -?, --help     show this help, then exit\n");
 	printf("\n"
-		   "For use as archive_cleanup_command in recovery.conf when standby_mode = on:\n"
+		   "For use as archive_cleanup_command in postgresql.conf when standby_mode = on:\n"
 		   "  archive_cleanup_command = 'pg_archivecleanup [OPTION]... ARCHIVELOCATION %%r'\n"
 		   "e.g.\n"
 		   "  archive_cleanup_command = 'pg_archivecleanup /mnt/server/archiverdir %%r'\n");
diff --git a/src/include/access/xlog.h b/src/include/access/xlog.h
index c9f332c..5a735bd 100644
--- a/src/include/access/xlog.h
+++ b/src/include/access/xlog.h
@@ -106,6 +106,26 @@ extern bool fullPageWrites;
 extern bool wal_log_hints;
 extern bool wal_compression;
 extern bool log_checkpoints;
+extern char *restore_command;
+extern char *archive_cleanup_command;
+extern char *recovery_end_command;
+extern bool standby_mode;
+extern char *primary_conninfo;
+extern char *primary_slot_name;
+extern char *trigger_file;
+extern RecoveryTargetType recovery_target;
+extern TransactionId recovery_target_xid;
+extern TimestampTz recovery_target_time;
+extern XLogRecPtr recovery_target_lsn;
+extern char *recovery_target_name;
+extern bool recovery_target_inclusive;
+extern bool pause_at_recovery_target;
+extern char *recovery_target_timeline_string;
+extern char *recovery_target_action_string;
+extern char *recovery_target_lsn_string;
+extern char *recovery_target_string;
+extern TimeLineID recovery_target_timeline;
+extern int recovery_min_apply_delay;
 
 extern int	CheckPointSegments;
 
@@ -268,6 +288,7 @@ extern void RemovePromoteSignalFiles(void);
 extern bool CheckPromoteSignal(void);
 extern void WakeupRecovery(void);
 extern void SetWalWriterSleeping(bool sleeping);
+extern void CheckRestoreCommandSet(void);
 
 extern void XLogRequestWalReceiverReply(void);
 
diff --git a/src/include/access/xlog_internal.h b/src/include/access/xlog_internal.h
index 0a595cc..2f01c58 100644
--- a/src/include/access/xlog_internal.h
+++ b/src/include/access/xlog_internal.h
@@ -255,6 +255,8 @@ typedef enum
 	RECOVERY_TARGET_ACTION_SHUTDOWN
 } RecoveryTargetAction;
 
+extern RecoveryTargetAction recovery_target_action;
+
 /*
  * Method table for resource managers.
  *
diff --git a/src/include/utils/guc_tables.h b/src/include/utils/guc_tables.h
index b695f61..7f4389c 100644
--- a/src/include/utils/guc_tables.h
+++ b/src/include/utils/guc_tables.h
@@ -68,6 +68,8 @@ enum config_group
 	WAL_SETTINGS,
 	WAL_CHECKPOINTS,
 	WAL_ARCHIVING,
+	WAL_ARCHIVE_RECOVERY,
+	WAL_RECOVERY_TARGET,
 	REPLICATION,
 	REPLICATION_SENDING,
 	REPLICATION_MASTER,
#10Michael Paquier
michael.paquier@gmail.com
In reply to: Abhijit Menon-Sen (#9)
Re: Proposal for changes to recovery.conf API

On Tue, Sep 6, 2016 at 2:18 PM, Abhijit Menon-Sen <ams@2ndquadrant.com> wrote:

One open issue is the various assign_recovery_target_xxx functions,
which Michael noted in his earlier review:
[...]
I don't like this code, but I'm not yet sure what to replace it with. I
think we should address the underlying problem—that the UI doesn't map
cleanly to what the code wants. There's been some discussion about this
earlier, but not any consensus that I could see.

By moving all the recovery parameters to GUC, we will need to define a
hierarchy among the target types. I see no way to avoid that except by
changing the parametrization but that would be more confusing for the
users. So that will not be anymore the last one wins, but the first
one listed wins in all the ones enabled that wins. I think that we
could leverage that a little bit though and reduce the complexity of
the patch: my best advice here is to make all those recovery_target_*
parameters PGC_POSTMASTER so as they are loaded only once when the
server starts, and then we define the recovery target type used in the
startup process instead of trying to do so at GUC level. This does not
change the fact that we'd still need a hierarchy among the target
types, but it slightly reduces the complexity of the patch. And this
does not prevent further enhancements by switching them later to be
PGC_SIGHUP, really. I'd really like to see this improvement, but I
don't think that this applies to this change, which is complicated
enough, and will likely introduce its lot of bugs even after several
reviews.

Do we want something like this (easy to implement and document, perhaps
not especially convenient to use):

recovery_target = 'xid' # or 'time'/'name'/'lsn'/'immediate'
recovery_target_xid = xxx? # the only setting we care about now
recovery_target_otherthings = parsed_but_ignored

Or something like this (a bit harder to implement):

recovery_target = 'xid:xxx' # or 'time:xxx' etc.

Interesting ideas, particularly the last one. Mixing both:
recovery_target_type = 'foo' # can be 'immediate'
recovery_target_value = 'value_of_foo' # does not matter for 'immediate'

Alternatively, the do-nothing option is to move the tests from guc.c to
StartupXLOG and do it only once in some defined order (which would also
break the current last-mention-wins behaviour).

My vote goes in favor of that..

+    else if (recovery_target_lsn != 0)
+        recovery_target = RECOVERY_TARGET_LSN;
This needs to check for InvalidXLogRecPtr.
-- 
Michael

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#11Abhijit Menon-Sen
ams@2ndQuadrant.com
In reply to: Michael Paquier (#10)
Re: Proposal for changes to recovery.conf API

At 2016-09-06 14:40:54 +0900, michael.paquier@gmail.com wrote:

my best advice here is to make all those recovery_target_* parameters
PGC_POSTMASTER so as they are loaded only once when the server starts,
and then we define the recovery target type used in the startup
process instead of trying to do so at GUC level.

I understand your approach in light of the GUC code, but I see things a
bit differently—the complexity comes largely from the specific handling
of recovery_target. I'll try to come up with a way to do it better. If
not, we have your suggestion to fall back on.

+    else if (recovery_target_lsn != 0)
+        recovery_target = RECOVERY_TARGET_LSN;
This needs to check for InvalidXLogRecPtr.

Of course, thanks. Fixed locally in all the relevant places. Will repost
whenever there are other substantive changes.

-- Abhijit

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#12Simon Riggs
simon@2ndquadrant.com
In reply to: Abhijit Menon-Sen (#11)
Re: Proposal for changes to recovery.conf API

On 6 September 2016 at 08:06, Abhijit Menon-Sen <ams@2ndquadrant.com> wrote:

At 2016-09-06 14:40:54 +0900, michael.paquier@gmail.com wrote:

my best advice here is to make all those recovery_target_* parameters
PGC_POSTMASTER so as they are loaded only once when the server starts,
and then we define the recovery target type used in the startup
process instead of trying to do so at GUC level.

I understand your approach in light of the GUC code, but I see things a
bit differently—the complexity comes largely from the specific handling
of recovery_target. I'll try to come up with a way to do it better. If
not, we have your suggestion to fall back on.

As I said upthread...
"We definitely want most of them set at RELOAD, especially recovery targets."
So PGC_POSTMASTER is not the objective.

How then to proceed?

I guess we could keep the old parameters and make them PGC_POSTMASTER,
but also provide a new parameter called recovery_target that
simplifies the API and is PGC_SIGHUP. That way we resolve the
annoyance of handling the current ones but keep compatibility for
those who can't move on, just yet.

--
Simon Riggs http://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#13Petr Jelinek
petr@2ndquadrant.com
In reply to: Abhijit Menon-Sen (#9)
Re: Proposal for changes to recovery.conf API

On 06/09/16 07:18, Abhijit Menon-Sen wrote:

One open issue is the various assign_recovery_target_xxx functions,
which Michael noted in his earlier review:

+static void
+assign_recovery_target_xid(const char *newval, void *extra)
+{
+	recovery_target_xid = *((TransactionId *) extra);
+
+	if (recovery_target_xid != InvalidTransactionId)
+		recovery_target = RECOVERY_TARGET_XID;
+	else if (recovery_target_name && *recovery_target_name)
+		recovery_target = RECOVERY_TARGET_NAME;
+	else if (recovery_target_time != 0)
+		recovery_target = RECOVERY_TARGET_TIME;
+	else if (recovery_target_lsn != 0)
+		recovery_target = RECOVERY_TARGET_LSN;
+	else
+		recovery_target = RECOVERY_TARGET_UNSET;
+}

(Note how recovery_target_lsn caused this�and three other functions
besides�to grow an extra branch.)

I don't like this code, but I'm not yet sure what to replace it with. I
think we should address the underlying problem�that the UI doesn't map
cleanly to what the code wants. There's been some discussion about this
earlier, but not any consensus that I could see.

Do we want something like this (easy to implement and document, perhaps
not especially convenient to use):

recovery_target = 'xid' # or 'time'/'name'/'lsn'/'immediate'
recovery_target_xid = xxx? # the only setting we care about now
recovery_target_otherthings = parsed_but_ignored

Or something like this (a bit harder to implement):

recovery_target = 'xid:xxx' # or 'time:xxx' etc.

Personally, I never liked the fact that we have several config variables
for this and then the last one is chosen (even when it was in
recovery.conf). We support one recovery_target at a time so it would
make sense to have single config option for it IMHO.

So +1 on the recovery_target = 'xid:xxx' idea.

--
Petr Jelinek http://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Training & Services

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#14Abhijit Menon-Sen
ams@2ndQuadrant.com
In reply to: Petr Jelinek (#13)
Re: Proposal for changes to recovery.conf API

At 2016-09-06 10:56:53 +0200, petr@2ndquadrant.com wrote:

So +1 on the recovery_target = 'xid:xxx' idea.

OK, I'll make it work that way. New patch in a couple of days.

-- Abhijit

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#15David Steele
david@pgmasters.net
In reply to: Petr Jelinek (#13)
Re: Proposal for changes to recovery.conf API

On 9/6/16 4:56 AM, Petr Jelinek wrote:

On 06/09/16 07:18, Abhijit Menon-Sen wrote:

Do we want something like this (easy to implement and document, perhaps
not especially convenient to use):

recovery_target = 'xid' # or 'time'/'name'/'lsn'/'immediate'
recovery_target_xid = xxx? # the only setting we care about now
recovery_target_otherthings = parsed_but_ignored

Or something like this (a bit harder to implement):

recovery_target = 'xid:xxx' # or 'time:xxx' etc.

Personally, I never liked the fact that we have several config variables
for this and then the last one is chosen (even when it was in
recovery.conf). We support one recovery_target at a time so it would
make sense to have single config option for it IMHO.

So +1 on the recovery_target = 'xid:xxx' idea.

I would rather not combine the type and target into a single field - how
about having two fields:

recovery_target_type = 'xid|time|name|immediate'
recovery_target = 'value'

recovery_target would be ignored when recovery_target_type = 'immediate'.

--
-David
david@pgmasters.net

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#16Robert Haas
robertmhaas@gmail.com
In reply to: Simon Riggs (#1)
Re: Proposal for changes to recovery.conf API

On Wed, Aug 31, 2016 at 9:45 PM, Simon Riggs <simon@2ndquadrant.com> wrote:

This is a summary of proposed changes to the recovery.conf API for
v10. These are based in part on earlier discussions, and represent a
minimal modification to current usage.

This looks great.

Proposed changes (with reference to patches from Abhijit Menon-Sen and myself)

* pg_ctl start -M (normal|recover|continue)
pg_ctl can now start the server directly as a standby, similar to the
way pg_ctl promote works. The internal implementation is also similar,
with pg_ctl writing a "recovery.trigger" file that initiates recovery
in the same way recovery.conf used to do. It is still possible to
manually add a file called "recovery.trigger" and have that work if
users desire that mechanism.
Different startup methods can be selected with the <option>-M</option>
option. <quote>Normal</quote> mode starts the server for read/write,
overriding any previous use in Recover mode.
<quote>Recover</quote> mode starts the server as a standby server which
expects to receive changes from a primary/master server using physical
streaming replication or is used for
performing a recovery from backup. <quote>Continue</quote> mode is
the default and will startup the server in whatever mode it was in at
last proper shutdown, or as modified by any trigger files present.
(Patch: recovery_startup_r10_api.v1b.patch)

I like this. I think your choice of naming is good, too. Michael
suggested something else downthread, but I think it's good to call
this "recover" because we don't know whether the user is doing
replication or PITR or whatever; "recover" is the internal name and is
fairly well-understood database jargon.

* $DATADIR/recovery.conf no longer triggers recovery
(Patch: recovery_startup_r10_api.v1b.patch)

* Recovery parameters would now be part of the main postgresql.conf
infrastructure
Any parameters set in $DATADIR/recovery.conf will be read after the
main parameter file, similar to the way that postgresql.conf.auto is
read.
(Abhijit)

Makes sense.

* pg_basebackup -R will continue to generate a parameter file called
recovery.conf as it does now, but will also create a file named
recovery.trigger.
(This part is WIP; patch doesn't include implementation for tar format yet)

Hmm, OK.

* Parameters
All of the parameters formerly set in recovery.conf can be set in
postgresql.conf using RELOAD
These parameters will have no defaults in postgresql.conf.sample
Setting them has no effect during normal running, or once recovery ends.
https://www.postgresql.org/docs/devel/static/archive-recovery-settings.html
https://www.postgresql.org/docs/devel/static/recovery-target-settings.html
https://www.postgresql.org/docs/devel/static/standby-settings.html
(Abhijit)

This sounds really good.

Related cleanup
* Promotion signal file is now called "promote.trigger" rather than
just "promote"
* Remove user configurable "trigger_file" mechanism - use
"promote.trigger" for all cases

I'm in favor of this. I don't think that it's very hard for authors
of backup tools to adapt to this new world, and I don't see that
allowing configurability here does anything other than create more
cases to worry about.

As you can see, I don't have a lot of substance to add, but I wanted
to lend my +1 to all of these proposals, which I think will improve
ease of use and probably lead to easier code maintenance, too.
Thanks!

--
Robert Haas
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#17Petr Jelinek
petr@2ndquadrant.com
In reply to: David Steele (#15)
Re: Proposal for changes to recovery.conf API

On 06/09/16 13:52, David Steele wrote:

On 9/6/16 4:56 AM, Petr Jelinek wrote:

On 06/09/16 07:18, Abhijit Menon-Sen wrote:

Do we want something like this (easy to implement and document, perhaps
not especially convenient to use):

recovery_target = 'xid' # or 'time'/'name'/'lsn'/'immediate'
recovery_target_xid = xxx? # the only setting we care about now
recovery_target_otherthings = parsed_but_ignored

Or something like this (a bit harder to implement):

recovery_target = 'xid:xxx' # or 'time:xxx' etc.

Personally, I never liked the fact that we have several config variables
for this and then the last one is chosen (even when it was in
recovery.conf). We support one recovery_target at a time so it would
make sense to have single config option for it IMHO.

So +1 on the recovery_target = 'xid:xxx' idea.

I would rather not combine the type and target into a single field - how
about having two fields:

recovery_target_type = 'xid|time|name|immediate'
recovery_target = 'value'

recovery_target would be ignored when recovery_target_type = 'immediate'.

That's also reasonable solution, I don't really have preference between
those. My main point was to get rid of the 5 or so variables where only
one will actually be used in the end.

--
Petr Jelinek http://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Training & Services

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#18Michael Paquier
michael.paquier@gmail.com
In reply to: Petr Jelinek (#17)
Re: Proposal for changes to recovery.conf API

On Tue, Sep 6, 2016 at 10:01 PM, Petr Jelinek <petr@2ndquadrant.com> wrote:

That's also reasonable solution, I don't really have preference between
those. My main point was to get rid of the 5 or so variables where only one
will actually be used in the end.

And no need to worry about the priority of the target types here. The
last value specified wins.
--
Michael

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#19David Steele
david@pgmasters.net
In reply to: Robert Haas (#16)
Re: Proposal for changes to recovery.conf API

On 9/6/16 8:07 AM, Robert Haas wrote:

On Wed, Aug 31, 2016 at 9:45 PM, Simon Riggs <simon@2ndquadrant.com> wrote:

Related cleanup
* Promotion signal file is now called "promote.trigger" rather than
just "promote"
* Remove user configurable "trigger_file" mechanism - use
"promote.trigger" for all cases

I'm in favor of this. I don't think that it's very hard for authors
of backup tools to adapt to this new world, and I don't see that
allowing configurability here does anything other than create more
cases to worry about.

+1 from a backup tool author.

--
-David
david@pgmasters.net

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#20Robert Haas
robertmhaas@gmail.com
In reply to: David Steele (#19)
Re: Proposal for changes to recovery.conf API

On Tue, Sep 6, 2016 at 10:11 AM, David Steele <david@pgmasters.net> wrote:

On 9/6/16 8:07 AM, Robert Haas wrote:

On Wed, Aug 31, 2016 at 9:45 PM, Simon Riggs <simon@2ndquadrant.com>
wrote:

Related cleanup
* Promotion signal file is now called "promote.trigger" rather than
just "promote"
* Remove user configurable "trigger_file" mechanism - use
"promote.trigger" for all cases

I'm in favor of this. I don't think that it's very hard for authors
of backup tools to adapt to this new world, and I don't see that
allowing configurability here does anything other than create more
cases to worry about.

+1 from a backup tool author.

It's time to wrap up this CommitFest, and this thread doesn't seem to
contain anything that looks like a committable patch. So, I'm marking
this "Returned with Feedback". I hope that the fact that there's been
no discussion for the last three weeks doesn't mean this effort is
dead; I would like very much to see it move forward.

Thanks,

--
Robert Haas
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#21Josh Berkus
josh@agliodbs.com
In reply to: Simon Riggs (#1)
Re: Proposal for changes to recovery.conf API

On 09/28/2016 10:13 AM, Robert Haas wrote:

On Tue, Sep 6, 2016 at 10:11 AM, David Steele <david@pgmasters.net> wrote:

On 9/6/16 8:07 AM, Robert Haas wrote:

On Wed, Aug 31, 2016 at 9:45 PM, Simon Riggs <simon@2ndquadrant.com>
wrote:

Related cleanup
* Promotion signal file is now called "promote.trigger" rather than
just "promote"
* Remove user configurable "trigger_file" mechanism - use
"promote.trigger" for all cases

I'm in favor of this. I don't think that it's very hard for authors
of backup tools to adapt to this new world, and I don't see that
allowing configurability here does anything other than create more
cases to worry about.

+1 from a backup tool author.

It's time to wrap up this CommitFest, and this thread doesn't seem to
contain anything that looks like a committable patch. So, I'm marking
this "Returned with Feedback". I hope that the fact that there's been
no discussion for the last three weeks doesn't mean this effort is
dead; I would like very much to see it move forward.

Has this gone anywhere? Given that we're in "break all the things" mode
for PostgreSQL 10, it would be the ideal time to consolidate
recovery.conf with pg.conf.

--
--
Josh Berkus
Red Hat OSAS
(any opinions are my own)

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#22Abhijit Menon-Sen
ams@2ndQuadrant.com
In reply to: Robert Haas (#20)
1 attachment(s)
Re: Proposal for changes to recovery.conf API

At 2016-09-28 13:13:56 -0400, robertmhaas@gmail.com wrote:

I hope that the fact that there's been no discussion for the last
three weeks doesn't mean this effort is dead; I would like very
much to see it move forward.

Here's an updated patch. Sorry, I got busy elswhere.

I struggled with the handling of recovery_target a little. For example,
one suggested alternative was:

recovery_target_type = xid
recovery_target_value = …

The problem with implementing it this way is that the _value setting
cannot be parsed without already having parsed the _type, and I didn't
want to force that sort of dependency.

What I've done instead is to make this work:

recovery_target = xid|time|name|lsn|immediate
recovery_target_xid = …
recovery_target_time = …
recovery_target_name = …
recovery_target_lsn = …

The recovery_target_xxx values are parsed as they used to be, but the
one that's used is the one that's set in recovery_target. That's easy to
explain, and the patch is much less intrusive, but I'm certainly open to
suggestions to improve this, and I have the time to work on this patch
with a view towards getting it committed in this cycle.

-- Abhijit

P.S. Sorry, I haven't been able to resolve the conflicts between Simon's
earlier recovery_startup_r10_api.v1b patch and the "pg_ctl promote -w"
changes in master. I was distracted by some illness in the family, but
I will post another update very soon.

Attachments:

recovery_guc_v20161101.patchtext/x-diff; charset=us-asciiDownload
From 231ccefbc99c07f00560f4e88a961db186db704e Mon Sep 17 00:00:00 2001
From: Abhijit Menon-Sen <ams@2ndQuadrant.com>
Date: Mon, 31 Oct 2016 11:32:51 +0530
Subject: Convert recovery.conf settings to GUCs

Based on unite_recoveryconf_postgresqlconf_v3.patch by Fujii Masao.
---
 contrib/pg_standby/pg_standby.c                 |   2 +-
 doc/src/sgml/backup.sgml                        |  31 +-
 doc/src/sgml/config.sgml                        | 480 ++++++++++++++++++++++
 doc/src/sgml/filelist.sgml                      |   1 -
 doc/src/sgml/func.sgml                          |   2 +-
 doc/src/sgml/high-availability.sgml             |  42 +-
 doc/src/sgml/pgstandby.sgml                     |   4 +-
 doc/src/sgml/postgres.sgml                      |   1 -
 doc/src/sgml/recovery-config.sgml               | 508 -----------------------
 doc/src/sgml/ref/pgarchivecleanup.sgml          |   6 +-
 doc/src/sgml/release-9.1.sgml                   |   5 +-
 doc/src/sgml/release.sgml                       |   2 +-
 src/backend/access/transam/recovery.conf.sample |  29 +-
 src/backend/access/transam/xlog.c               | 520 +++++++-----------------
 src/backend/access/transam/xlogarchive.c        |   8 +-
 src/backend/postmaster/startup.c                |   2 +
 src/backend/replication/walreceiver.c           |  24 ++
 src/backend/utils/misc/guc-file.l               |  18 +
 src/backend/utils/misc/guc.c                    | 430 ++++++++++++++++++++
 src/backend/utils/misc/postgresql.conf.sample   |  24 ++
 src/bin/pg_archivecleanup/pg_archivecleanup.c   |   2 +-
 src/include/access/xlog.h                       |  21 +
 src/include/access/xlog_internal.h              |   2 +
 src/include/utils/guc_tables.h                  |   2 +
 24 files changed, 1202 insertions(+), 964 deletions(-)
 delete mode 100644 doc/src/sgml/recovery-config.sgml

diff --git a/contrib/pg_standby/pg_standby.c b/contrib/pg_standby/pg_standby.c
index e4136f9..8fcb85c 100644
--- a/contrib/pg_standby/pg_standby.c
+++ b/contrib/pg_standby/pg_standby.c
@@ -520,7 +520,7 @@ usage(void)
 	printf("  -w MAXWAITTIME     max seconds to wait for a file (0=no limit) (default=0)\n");
 	printf("  -?, --help         show this help, then exit\n");
 	printf("\n"
-		   "Main intended use as restore_command in recovery.conf:\n"
+		   "Main intended use as restore_command in postgresql.conf:\n"
 		   "  restore_command = 'pg_standby [OPTION]... ARCHIVELOCATION %%f %%p %%r'\n"
 		   "e.g.\n"
 	"  restore_command = 'pg_standby /mnt/server/archiverdir %%f %%p %%r'\n");
diff --git a/doc/src/sgml/backup.sgml b/doc/src/sgml/backup.sgml
index 6eaed1e..2df0dc6 100644
--- a/doc/src/sgml/backup.sgml
+++ b/doc/src/sgml/backup.sgml
@@ -1190,8 +1190,15 @@ SELECT pg_stop_backup();
    </listitem>
    <listitem>
     <para>
-     Create a recovery command file <filename>recovery.conf</> in the cluster
-     data directory (see <xref linkend="recovery-config">). You might
+     Set up recovery parameters in <filename>postgresql.conf</> (see
+     <xref linkend="runtime-config-wal-archive-recovery"> and
+     <xref linkend="runtime-config-wal-recovery-target">).
+    </para>
+   </listitem>
+   <listitem>
+    <para>
+     Create a recovery trigger file <filename>recovery.trigger</>
+     in the cluster data directory. You might
      also want to temporarily modify <filename>pg_hba.conf</> to prevent
      ordinary users from connecting until you are sure the recovery was successful.
     </para>
@@ -1203,7 +1210,7 @@ SELECT pg_stop_backup();
      recovery be terminated because of an external error, the server can
      simply be restarted and it will continue recovery.  Upon completion
      of the recovery process, the server will rename
-     <filename>recovery.conf</> to <filename>recovery.done</> (to prevent
+     <filename>recovery.trigger</> to <filename>recovery.done</> (to prevent
      accidentally re-entering recovery mode later) and then
      commence normal database operations.
     </para>
@@ -1219,12 +1226,11 @@ SELECT pg_stop_backup();
    </para>
 
    <para>
-    The key part of all this is to set up a recovery configuration file that
-    describes how you want to recover and how far the recovery should
-    run.  You can use <filename>recovery.conf.sample</> (normally
-    located in the installation's <filename>share/</> directory) as a
-    prototype.  The one thing that you absolutely must specify in
-    <filename>recovery.conf</> is the <varname>restore_command</>,
+    The key part of all this is to set up recovery parameters that
+    specify how you want to recover and how far the recovery should
+    run. The one thing that you absolutely must specify in
+    <filename>postgresql.conf</> to recover from the backup is
+    the <varname>restore_command</>,
     which tells <productname>PostgreSQL</> how to retrieve archived
     WAL file segments.  Like the <varname>archive_command</>, this is
     a shell command string.  It can contain <literal>%f</>, which is
@@ -1286,7 +1292,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 <link linkend="recovery-target-settings">stopping point</link> in <filename>recovery.conf</>.  You can specify
+    required <link linkend="recovery-target-settings">stopping point</link> in <filename>postgresql.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
@@ -1383,8 +1389,9 @@ restore_command = 'cp /mnt/server/archivedir/%f %p'
     The default behavior of recovery is to recover along the same timeline
     that was current when the base backup was taken.  If you wish to recover
     into some child timeline (that is, you want to return to some state that
-    was itself generated after a recovery attempt), you need to specify the
-    target timeline ID in <filename>recovery.conf</>.  You cannot recover into
+    was itself generated after a recovery attempt), you need to set
+    <xref linkend="guc-recovery-target-timeline"> to the
+    target timeline ID in <filename>postgresql.conf</>.  You cannot recover into
     timelines that branched off earlier than the base backup.
    </para>
   </sect2>
diff --git a/doc/src/sgml/config.sgml b/doc/src/sgml/config.sgml
index adab2f8..c2442c5 100644
--- a/doc/src/sgml/config.sgml
+++ b/doc/src/sgml/config.sgml
@@ -2846,6 +2846,348 @@ include_dir 'conf.d'
      </variablelist>
     </sect2>
 
+    <sect2 id="runtime-config-wal-archive-recovery">
+     <title>Archive Recovery</title>
+
+     <variablelist>
+      <varlistentry id="guc-restore-command" xreflabel="restore_command">
+       <term><varname>restore_command</varname> (<type>string</type>)</term>
+       <indexterm>
+        <primary><varname>restore_command</> configuration parameter</primary>
+       </indexterm>
+       <listitem>
+        <para>
+         The shell command to execute to retrieve an archived segment of
+         the WAL file series. This parameter is required for archive recovery,
+         but optional for streaming replication.
+         Any <literal>%f</> in the string is
+         replaced by the name of the file to retrieve from the archive,
+         and any <literal>%p</> is replaced by the copy destination path name
+         on the server.
+         (The path name is relative to the current working directory,
+         i.e., the cluster's data directory.)
+         Any <literal>%r</> is replaced by the name of the file containing the
+         last valid restart point. That is the earliest file that must be kept
+         to allow a restore to be restartable, so this information can be used
+         to truncate the archive to just the minimum required to support
+         restarting from the current restore. <literal>%r</> is typically only
+         used by warm-standby configurations
+         (see <xref linkend="warm-standby">).
+         Write <literal>%%</> to embed an actual <literal>%</> character.
+        </para>
+        <para>
+         It is important for the command to return a zero exit status
+         only if it succeeds.  The command <emphasis>will</> be asked for file
+         names that are not present in the archive; it must return nonzero
+         when so asked.  Examples:
+<programlisting>
+restore_command = 'cp /mnt/server/archivedir/%f "%p"'
+restore_command = 'copy "C:\\server\\archivedir\\%f" "%p"'  # Windows
+</programlisting>
+        </para>
+        <para>
+         This parameter can only be set in the <filename>postgresql.conf</>
+         file or on the server command line. It only has effect during archive
+         recovery or in standby mode.
+        </para>
+       </listitem>
+      </varlistentry>
+
+      <varlistentry id="guc-archive-cleanup-command" xreflabel="archive_cleanup_command">
+       <term><varname>archive_cleanup_command</varname> (<type>string</type>)</term>
+       <indexterm>
+         <primary><varname>archive_cleanup_command</> configuration parameter</primary>
+       </indexterm>
+       <listitem>
+        <para>
+         The shell command that will be executed at every restartpoint.
+         The purpose of <varname>archive_cleanup_command</> is to
+         provide a mechanism for cleaning up old archived WAL files that
+         are no longer needed by the standby server.
+         Any <literal>%r</> is replaced by the name of the file containing the
+         last valid restart point.
+         That is the earliest file that must be <emphasis>kept</> to allow a
+         restore to be restartable, and so all files earlier than <literal>%r</>
+         may be safely removed.
+         This information can be used to truncate the archive to just the
+         minimum required to support restart from the current restore.
+         The <xref linkend="pgarchivecleanup"> module
+         is often used in <varname>archive_cleanup_command</> for
+         single-standby configurations, for example:
+<programlisting>archive_cleanup_command = 'pg_archivecleanup /mnt/server/archivedir %r'</programlisting>
+         Note however that if multiple standby servers are restoring from the
+         same archive directory, you will need to ensure that you do not delete
+         WAL files until they are no longer needed by any of the servers.
+         <varname>archive_cleanup_command</> would typically be used in a
+         warm-standby configuration (see <xref linkend="warm-standby">).
+         Write <literal>%%</> to embed an actual <literal>%</> character in the
+         command.
+        </para>
+        <para>
+         If the command returns a non-zero exit status then a WARNING log
+         message will be written.
+        </para>
+        <para>
+         This parameter can only be set in the <filename>postgresql.conf</>
+         file or on the server command line. It only has effect during archive
+         recovery or in standby mode.
+        </para>
+       </listitem>
+      </varlistentry>
+
+      <varlistentry id="guc-recovery-end-command" xreflabel="recovery_end_command">
+       <term><varname>recovery_end_command</varname> (<type>string</type>)</term>
+       <indexterm>
+         <primary><varname>recovery_end_command</> configuration parameter</primary>
+       </indexterm>
+       <listitem>
+        <para>
+         The shell command that will be executed once only
+         at the end of recovery. This parameter is optional. The purpose of the
+         <varname>recovery_end_command</> is to provide a mechanism for cleanup
+         following replication or recovery.
+         Any <literal>%r</> is replaced by the name of the file containing the
+         last valid restart point, like in <varname>archive_cleanup_command</>.
+        </para>
+        <para>
+         If the command returns a non-zero exit status then a WARNING log
+         message will be written and the database will proceed to start up
+         anyway.  An exception is that if the command was terminated by a
+         signal, the database will not proceed with startup.
+        </para>
+        <para>
+         This parameter can only be set in the <filename>postgresql.conf</>
+         file or on the server command line. It only has effect during archive
+         recovery or in standby mode.
+        </para>
+       </listitem>
+      </varlistentry>
+     </variablelist>
+    </sect2>
+
+    <sect2 id="runtime-config-wal-recovery-target">
+     <title>Recovery Target</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_lsn</>, <varname>recovery_target_name</>,
+      <varname>recovery_target_time</>, or <varname>recovery_target_xid</>
+      can be used; if more than one of these is specified in the configuration
+      file, the last entry will be used.
+     </para>
+
+     <variablelist>
+     <varlistentry id="recovery-target" xreflabel="recovery_target">
+      <term><varname>recovery_target</varname><literal> = 'immediate'</literal>
+      <indexterm>
+        <primary><varname>recovery_target</> recovery parameter</primary>
+      </indexterm>
+      </term>
+      <listitem>
+       <para>
+        This parameter specifies that recovery should end as soon as a
+        consistent state is reached, i.e. 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>
+
+     <variablelist>
+      <varlistentry id="guc-recovery-target-name" xreflabel="recovery_target_name">
+       <term><varname>recovery_target_name</varname> (<type>string</type>)</term>
+       <indexterm>
+        <primary><varname>recovery_target_name</> configuration parameter</primary>
+       </indexterm>
+       <listitem>
+        <para>
+         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</>,
+         <varname>recovery_target_time</> or
+         <varname>recovery_target_xid</> can be specified.  The default
+         value is an empty string, which will recover to the end of the WAL log.
+        </para>
+        <para>
+         This parameter can only be set in the <filename>postgresql.conf</>
+         file or on the server command line. It only has effect during archive
+         recovery or in standby mode.
+        </para>
+       </listitem>
+      </varlistentry>
+
+      <varlistentry id="guc-recovery-target-time" xreflabel="recovery_target_time">
+       <term><varname>recovery_target_time</varname> (<type>string</type>)</term>
+       <indexterm>
+        <primary><varname>recovery_target_time</> configuration parameter</primary>
+       </indexterm>
+       <listitem>
+        <para>
+         Specifies the time stamp up to which recovery will proceed.
+         This parameter must be specified in the date/time format
+         (see <xref linkend="datatype-datetime-input"> for details).
+         At most one of <varname>recovery_target_time</>,
+         <varname>recovery_target_name</> or
+         <varname>recovery_target_xid</> can be specified.
+         The default value is an empty string, which will recover to
+         the end of the WAL log. The precise stopping point is also
+         influenced by <varname>recovery_target_inclusive</>.
+        </para>
+        <para>
+         This parameter can only be set in the <filename>postgresql.conf</>
+         file or on the server command line. It only has effect during archive
+         recovery or in standby mode.
+        </para>
+       </listitem>
+      </varlistentry>
+
+      <varlistentry id="guc-recovery-target-xid" xreflabel="recovery_target_xid">
+       <term><varname>recovery_target_xid</varname> (<type>string</type>)</term>
+       <indexterm>
+        <primary><varname>recovery_target_xid</> configuration parameter</primary>
+       </indexterm>
+       <listitem>
+        <para>
+         Specifies the transaction ID up to which recovery will proceed.
+         Keep in mind that while transaction IDs are assigned sequentially
+         at transaction 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</>,
+         <varname>recovery_target_name</> or
+         <varname>recovery_target_time</> can be specified.
+         The default value is an empty string, which will recover to the end of
+         the WAL log. The precise stopping point is also influenced by
+         <varname>recovery_target_inclusive</>.
+        </para>
+        <para>
+         This parameter can only be set in the <filename>postgresql.conf</>
+         file or on the server command line. It only has effect during archive
+         recovery or in standby mode.
+        </para>
+       </listitem>
+      </varlistentry>
+
+      <varlistentry id="guc-recovery-target-inclusive" xreflabel="recovery_target_inclusive">
+       <term><varname>recovery_target_inclusive</varname> (<type>boolean</type>)</term>
+       <indexterm>
+        <primary><varname>recovery_target_inclusive</> configuration parameter</primary>
+       </indexterm>
+       <listitem>
+        <para>
+         Specifies whether we stop just after the specified recovery target
+         (<literal>on</>), or just before the recovery target (<literal>off</>).
+         Applies to both <varname>recovery_target_time</>
+         and <varname>recovery_target_xid</>, whichever one is
+         specified for this recovery.  This indicates whether transactions
+         having exactly the target commit time or ID, respectively, will
+         be included in the recovery.  Default is <literal>on</>.
+        </para>
+        <para>
+         This parameter can only be set in the <filename>postgresql.conf</>
+         file or on the server command line. It only has effect during archive
+         recovery or in standby mode.
+        </para>
+       </listitem>
+      </varlistentry>
+
+      <varlistentry id="guc-recovery-target-timeline" xreflabel="recovery_target_timeline">
+       <term><varname>recovery_target_timeline</varname> (<type>string</type>)</term>
+       <indexterm>
+        <primary><varname>recovery_target_timeline</> configuration parameter</primary>
+       </indexterm>
+       <listitem>
+        <para>
+         Specifies recovering into a particular timeline.  The default value is
+         an empty string, which will recover along the same timeline that was
+         current when the base backup was taken. Setting this to
+         <literal>latest</> recovers to the latest timeline found in the archive,
+         which is useful in a standby server. Other than that you only need to
+         set this parameter in complex re-recovery situations, where you need
+         to return to a state that itself was reached after a point-in-time
+         recovery. See <xref linkend="backup-timelines"> for discussion.
+        </para>
+        <para>
+         This parameter can only be set at server start. It only has effect
+         during archive recovery or in standby mode.
+        </para>
+       </listitem>
+      </varlistentry>
+
+      <varlistentry id="recovery-target-lsn" xreflabel="recovery_target_lsn">
+       <term><varname>recovery_target_lsn</varname> (<type>pg_lsn</type>)
+       <indexterm>
+         <primary><varname>recovery_target_lsn</> recovery parameter</primary>
+       </indexterm>
+       </term>
+       <listitem>
+        <para>
+         This parameter specifies the LSN of the transaction log location up
+         to which recovery will proceed. The precise stopping point is also
+         influenced by <xref linkend="recovery-target-inclusive">. This
+         parameter is parsed using the system data type
+         <link linkend="datatype-pg-lsn"><type>pg_lsn</></link>.
+        </para>
+       </listitem>
+      </varlistentry>
+
+      <varlistentry id="guc-recovery-target-action" xreflabel="recovery_target_action">
+       <term><varname>recovery_target_action</varname> (<type>enum</type>)
+       <indexterm>
+         <primary><varname>recovery_target_action</> recovery parameter</primary>
+       </indexterm>
+       </term>
+       <listitem>
+        <para>
+         Specifies what action the server should take once the recovery target is
+         reached. The default is <literal>pause</>, which means recovery will
+         be paused. <literal>promote</> means the recovery process will finish
+         and the server will start to accept connections.
+         Finally <literal>shutdown</> will stop the server after reaching the
+         recovery target.
+        </para>
+        <para>
+         The intended use of the <literal>pause</> setting is to allow queries
+         to be executed against the database to check if this recovery target
+         is the most desirable point for recovery.
+         The paused state can be resumed by
+         using <function>pg_xlog_replay_resume()</> (see
+         <xref linkend="functions-recovery-control-table">), which then
+         causes recovery to end. If this recovery target is not the
+         desired stopping point, then shut down the server, change the
+         recovery target settings to a later target and restart to
+         continue recovery.
+        </para>
+        <para>
+         The <literal>shutdown</> setting is useful to have the instance ready
+         at the exact replay point desired.  The instance will still be able to
+         replay more WAL records (and in fact will have to replay WAL records
+         since the last checkpoint next time it is started).
+        </para>
+        <para>
+         Note that because <filename>recovery.trigger</> will not be renamed when
+         <varname>recovery_target_action</> is set to <literal>shutdown</>,
+         any subsequent start will end with immediate shutdown unless the
+         configuration is changed or the <filename>recovery.trigger</> file is
+         removed manually.
+        </para>
+        <para>
+         This setting has no effect if no recovery target is set.
+         If <xref linkend="guc-hot-standby"> is not enabled, a setting of
+         <literal>pause</> will act the same as <literal>shutdown</>.
+        </para>
+       </listitem>
+      </varlistentry>
+
+     </variablelist>
+     </sect2>
+
    </sect1>
 
    <sect1 id="runtime-config-replication">
@@ -3149,6 +3491,93 @@ include_dir 'conf.d'
 
     <variablelist>
 
+     <varlistentry id="guc-standby-mode" xreflabel="standby_mode">
+      <term><varname>standby_mode</varname> (<type>boolean</type>)</term>
+      <indexterm>
+       <primary><varname>standby_mode</> configuration parameter</primary>
+      </indexterm>
+      <listitem>
+       <para>
+        Specifies whether to start the <productname>PostgreSQL</> server as
+        a standby when recovery trigger file <filename>recovery.trigger</> exists.
+        The default value is <literal>off</>.
+        If this parameter is <literal>on</>, the server will not
+        stop recovery when the end of archived WAL is reached,
+        but will keep trying to continue recovery by fetching new WAL segments
+        using <varname>restore_command</> and/or by connecting to
+        the primary server as specified by the <varname>primary_conninfo</>
+        setting.
+       </para>
+       <para>
+        This parameter can only be set at server start. It only has effect
+        if recovery trigger file <filename>recovery.trigger</> exists.
+       </para>
+      </listitem>
+     </varlistentry>
+
+     <varlistentry id="guc-primary-conninfo" xreflabel="primary_conninfo">
+      <term><varname>primary_conninfo</varname> (<type>string</type>)</term>
+      <indexterm>
+        <primary><varname>primary_conninfo</> configuration parameter</primary>
+      </indexterm>
+      <listitem>
+       <para>
+        Specifies a connection string to be used for the standby server
+        to connect with the primary. This string is in the format
+        accepted by the libpq <function>PQconnectdb</function> function,
+        described in <xref linkend="libpq-connect">. If any option is
+        unspecified in this string, then the corresponding environment
+        variable (see <xref linkend="libpq-envars">) is checked. If the
+        environment variable is not set either, then defaults are used.
+        If this parameter is an empty string (the default), no attempt is
+        made to connect to the master.
+       </para>
+       <para>
+        The connection string should specify the host name (or address)
+        of the primary server, as well as the port number if it is not
+        the same as the standby server's default.
+        Also specify a user name corresponding to a role that has the
+        <literal>REPLICATION</> and <literal>LOGIN</> privileges on the
+        primary (see
+        <xref linkend="streaming-replication-authentication">).
+        A password needs to be provided too, if the primary demands password
+        authentication.  It can be provided in the
+        <varname>primary_conninfo</varname> string, or in a separate
+        <filename>~/.pgpass</> file on the standby server (use
+        <literal>replication</> as the database name).
+        Do not specify a database name in the
+        <varname>primary_conninfo</varname> string.
+       </para>
+       <para>
+        This parameter can only be set in the <filename>postgresql.conf</>
+        file or on the server command line. It only has effect in standby mode.
+       </para>
+       <para>
+        If this parameter is changed while replication is in progress,
+        the standby terminates replication, and then tries to restart
+        replication with new setting.
+       </para>
+      </listitem>
+     </varlistentry>
+
+     <varlistentry id="primary-slot-name" xreflabel="primary_slot_name">
+      <term><varname>primary_slot_name</varname> (<type>string</type>)
+      <indexterm>
+        <primary><varname>primary_slot_name</> recovery parameter</primary>
+      </indexterm>
+      </term>
+      <listitem>
+       <para>
+        Optionally specifies an existing replication slot to be used when
+        connecting to the primary via streaming replication to control
+        resource removal on the upstream node
+        (see <xref linkend="streaming-replication-slots">).
+        This setting has no effect if <varname>primary_conninfo</> is not
+        set.
+       </para>
+      </listitem>
+     </varlistentry>
+
      <varlistentry id="guc-hot-standby" xreflabel="hot_standby">
       <term><varname>hot_standby</varname> (<type>boolean</type>)
       <indexterm>
@@ -3229,6 +3658,57 @@ include_dir 'conf.d'
       </listitem>
      </varlistentry>
 
+     <varlistentry id="recovery-min-apply-delay" xreflabel="recovery_min_apply_delay">
+      <term><varname>recovery_min_apply_delay</varname> (<type>integer</type>)
+      <indexterm>
+        <primary><varname>recovery_min_apply_delay</> recovery parameter</primary>
+      </indexterm>
+      </term>
+      <listitem>
+       <para>
+        By default, a standby server restores WAL records from the
+        primary as soon as possible. It may be useful to have a time-delayed
+        copy of the data, offering opportunities to correct data loss errors.
+        This parameter allows you to delay recovery by a fixed period of time,
+        measured in milliseconds if no unit is specified.  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>
+        It is possible that the replication delay between servers exceeds the
+        value of this parameter, in which case no delay is added.
+        Note that the delay is calculated between the WAL time stamp as written
+        on master and the current time on the standby. Delays in transfer
+        because of network lag or cascading replication configurations
+        may reduce the actual wait time significantly. If the system
+        clocks on master and standby are not synchronized, this may lead to
+        recovery applying records earlier than expected; but that is not a
+        major issue because useful settings of this parameter are much larger
+        than typical time deviations between servers.
+       </para>
+       <para>
+        The delay occurs only on WAL records for transaction commits.
+        Other records are replayed as quickly as possible, which
+        is not a problem because MVCC visibility rules ensure their effects
+        are not visible until the corresponding commit record is applied.
+       </para>
+       <para>
+        The delay occurs once the database in recovery has reached a consistent
+        state, until the standby is promoted or triggered. After that the standby
+        will end recovery without further waiting.
+       </para>
+       <para>
+        This parameter is intended for use with streaming replication deployments;
+        however, if the parameter is specified it will be honored in all cases.
+        Synchronous replication is not affected by this setting because there is
+        not yet any setting to request synchronous apply of transaction commits.
+        <varname>hot_standby_feedback</> will be delayed by use of this feature
+        which could lead to bloat on the master; use both together with care.
+       </para>
+      </listitem>
+     </varlistentry>
+
      <varlistentry id="guc-wal-receiver-status-interval" xreflabel="wal_receiver_status_interval">
       <term><varname>wal_receiver_status_interval</varname> (<type>integer</type>)
       <indexterm>
diff --git a/doc/src/sgml/filelist.sgml b/doc/src/sgml/filelist.sgml
index 69649a7..486f2fc 100644
--- a/doc/src/sgml/filelist.sgml
+++ b/doc/src/sgml/filelist.sgml
@@ -45,7 +45,6 @@
 <!ENTITY manage-ag     SYSTEM "manage-ag.sgml">
 <!ENTITY monitoring    SYSTEM "monitoring.sgml">
 <!ENTITY regress       SYSTEM "regress.sgml">
-<!ENTITY recovery-config SYSTEM "recovery-config.sgml">
 <!ENTITY runtime       SYSTEM "runtime.sgml">
 <!ENTITY config        SYSTEM "config.sgml">
 <!ENTITY user-manag    SYSTEM "user-manag.sgml">
diff --git a/doc/src/sgml/func.sgml b/doc/src/sgml/func.sgml
index 2e64cc4..86fa984 100644
--- a/doc/src/sgml/func.sgml
+++ b/doc/src/sgml/func.sgml
@@ -18133,7 +18133,7 @@ postgres=# select pg_start_backup('label_goes_here');
     <function>pg_create_restore_point</> creates a named transaction log
     record that can be used as recovery target, and returns the corresponding
     transaction log location.  The given name can then be used with
-    <xref linkend="recovery-target-name"> to specify the point up to which
+    <xref linkend="guc-recovery-target-name"> to specify the point up to which
     recovery will proceed.  Avoid creating multiple restore points with the
     same name, since recovery will stop at the first one whose name matches
     the recovery target.
diff --git a/doc/src/sgml/high-availability.sgml b/doc/src/sgml/high-availability.sgml
index 5bedaf2..dd6b3f9 100644
--- a/doc/src/sgml/high-availability.sgml
+++ b/doc/src/sgml/high-availability.sgml
@@ -591,7 +591,7 @@ protocol to make nodes agree on a serializable transactional order.
    <para>
     In standby mode, the server continuously applies WAL received from the
     master server. The standby server can read WAL from a WAL archive
-    (see <xref linkend="restore-command">) or directly from the master
+    (see <xref linkend="guc-restore-command">) or directly from the master
     over a TCP connection (streaming replication). The standby server will
     also attempt to restore any WAL found in the standby cluster's
     <filename>pg_wal</> directory. That typically happens after a server
@@ -660,8 +660,8 @@ protocol to make nodes agree on a serializable transactional order.
    <para>
     To set up the standby server, restore the base backup taken from primary
     server (see <xref linkend="backup-pitr-recovery">). Create a recovery
-    command file <filename>recovery.conf</> in the standby's cluster data
-    directory, and turn on <varname>standby_mode</>. Set
+    trigger file <filename>recovery.trigger</> in the standby's cluster data
+    directory. Turn on <varname>standby_mode</> and set
     <varname>restore_command</> to a simple command to copy files from
     the WAL archive. If you plan to have multiple standby servers for high
     availability purposes, set <varname>recovery_target_timeline</> to
@@ -697,7 +697,7 @@ protocol to make nodes agree on a serializable transactional order.
 
    <para>
     If you're using a WAL archive, its size can be minimized using the <xref
-    linkend="archive-cleanup-command"> parameter to remove files that are no
+    linkend="guc-archive-cleanup-command"> parameter to remove files that are no
     longer required by the standby server.
     The <application>pg_archivecleanup</> utility is designed specifically to
     be used with <varname>archive_cleanup_command</> in typical single-standby
@@ -708,7 +708,7 @@ protocol to make nodes agree on a serializable transactional order.
    </para>
 
    <para>
-    A simple example of a <filename>recovery.conf</> is:
+    A simple example of standby settings is:
 <programlisting>
 standby_mode = 'on'
 primary_conninfo = 'host=192.168.1.50 port=5432 user=foo password=foopass'
@@ -766,8 +766,8 @@ archive_cleanup_command = 'pg_archivecleanup /path/to/archive %r'
     To use streaming replication, set up a file-based log-shipping standby
     server as described in <xref linkend="warm-standby">. The step that
     turns a file-based log-shipping standby into streaming replication
-    standby is setting <varname>primary_conninfo</> setting in the
-    <filename>recovery.conf</> file to point to the primary server. Set
+    standby is setting <varname>primary_conninfo</> to
+    point to the primary server. Set
     <xref linkend="guc-listen-addresses"> and authentication options
     (see <filename>pg_hba.conf</>) on the primary so that the standby server
     can connect to the <literal>replication</> pseudo-database on the primary
@@ -827,15 +827,14 @@ host    replication     foo             192.168.1.100/32        md5
     </para>
     <para>
      The host name and port number of the primary, connection user name,
-     and password are specified in the <filename>recovery.conf</> file.
+     and password are specified in <varname>primary_conninfo</>.
      The password can also be set in the <filename>~/.pgpass</> file on the
      standby (specify <literal>replication</> in the <replaceable>database</>
      field).
      For example, if the primary is running on host IP <literal>192.168.1.50</>,
      port <literal>5432</literal>, the account name for replication is
      <literal>foo</>, and the password is <literal>foopass</>, the administrator
-     can add the following line to the <filename>recovery.conf</> file on the
-     standby:
+     can set <varname>primary_conninfo</> on the standby like this:
 
 <programlisting>
 # The standby connects to the primary that is running on host 192.168.1.50
@@ -940,7 +939,7 @@ postgres=# SELECT * FROM pg_replication_slots;
 (1 row)
 </programlisting>
      To configure the standby to use this slot, <varname>primary_slot_name</>
-     should be configured in the standby's <filename>recovery.conf</>.
+     should be configured in the standby's <filename>postgresql.conf</>.
      Here is a simple example:
 <programlisting>
 standby_mode = 'on'
@@ -1387,8 +1386,8 @@ synchronous_standby_names = '2 (s1, s2, s3)'
    <para>
     To trigger failover of a log-shipping standby server,
     run <command>pg_ctl promote</> or create a trigger
-    file with the file name and path specified by the <varname>trigger_file</>
-    setting in <filename>recovery.conf</>. If you're planning to use
+    file with the file name and path specified by the <varname>trigger_file</>.
+    If you're planning to use
     <command>pg_ctl promote</> to fail over, <varname>trigger_file</> is
     not required. If you're setting up the reporting servers that are
     only used to offload read-only queries from the primary, not for high
@@ -1433,8 +1432,7 @@ synchronous_standby_names = '2 (s1, s2, s3)'
     The magic that makes the two loosely coupled servers work together is
     simply a <varname>restore_command</> used on the standby that,
     when asked for the next WAL file, waits for it to become available from
-    the primary. The <varname>restore_command</> is specified in the
-    <filename>recovery.conf</> file on the standby server. Normal recovery
+    the primary. Normal recovery
     processing would request a file from the WAL archive, reporting failure
     if the file was unavailable.  For standby processing it is normal for
     the next WAL file to be unavailable, so the standby must wait for
@@ -1521,8 +1519,14 @@ if (!triggered)
      </listitem>
      <listitem>
       <para>
+       Create a recovery trigger file <filename>recovery.trigger</>
+       in the standby's cluster data directory.
+      </para>
+     </listitem>
+     <listitem>
+      <para>
        Begin recovery on the standby server from the local WAL
-       archive, using a <filename>recovery.conf</> that specifies a
+       archive, specifying a
        <varname>restore_command</> that waits as described
        previously (see <xref linkend="backup-pitr-recovery">).
       </para>
@@ -2018,9 +2022,9 @@ if (!triggered)
    <title>Administrator's Overview</title>
 
    <para>
-    If <varname>hot_standby</> is turned <literal>on</> in
-    <filename>postgresql.conf</> and there is a <filename>recovery.conf</>
-    file present, the server will run in Hot Standby mode.
+    If <varname>hot_standby</> is turned <literal>on</>
+    and there is a recovery trigger file
+    <filename>recovery.trigger</> present, the server will run in Hot Standby mode.
     However, it may take some time for Hot Standby connections to be allowed,
     because the server will not accept connections until it has completed
     sufficient recovery to provide a consistent state against which queries
diff --git a/doc/src/sgml/pgstandby.sgml b/doc/src/sgml/pgstandby.sgml
index 80c6f60..c9e6185 100644
--- a/doc/src/sgml/pgstandby.sgml
+++ b/doc/src/sgml/pgstandby.sgml
@@ -46,8 +46,8 @@
 
   <para>
    To configure a standby
-   server to use <application>pg_standby</>, put this into its
-   <filename>recovery.conf</filename> configuration file:
+   server to use <application>pg_standby</>, specify
+   <xref linkend="guc-restore-command"> like this:
 <programlisting>
 restore_command = 'pg_standby <replaceable>archiveDir</> %f %p %r'
 </programlisting>
diff --git a/doc/src/sgml/postgres.sgml b/doc/src/sgml/postgres.sgml
index 9143917..db42f65 100644
--- a/doc/src/sgml/postgres.sgml
+++ b/doc/src/sgml/postgres.sgml
@@ -156,7 +156,6 @@
   &maintenance;
   &backup;
   &high-availability;
-  &recovery-config;
   &monitoring;
   &diskusage;
   &wal;
diff --git a/doc/src/sgml/recovery-config.sgml b/doc/src/sgml/recovery-config.sgml
deleted file mode 100644
index 8c24ae2..0000000
--- a/doc/src/sgml/recovery-config.sgml
+++ /dev/null
@@ -1,508 +0,0 @@
-<!-- doc/src/sgml/recovery-config.sgml -->
-
-<chapter id="recovery-config">
-  <title>Recovery Configuration</title>
-
-  <indexterm>
-   <primary>configuration</primary>
-   <secondary>of recovery</secondary>
-   <tertiary>of a standby server</tertiary>
-  </indexterm>
-
-   <para>
-    This chapter describes the settings available in the
-    <filename>recovery.conf</><indexterm><primary>recovery.conf</></>
-    file. They apply only for the duration of the
-    recovery.  They must be reset for any subsequent recovery you wish to
-    perform.  They cannot be changed once recovery has begun.
-   </para>
-
-   <para>
-     Settings in <filename>recovery.conf</> are specified in the format
-     <literal>name = 'value'</>. One parameter is specified per line.
-     Hash marks (<literal>#</literal>) designate the rest of the
-     line as a comment.  To embed a single quote in a parameter
-     value, write two quotes (<literal>''</>).
-   </para>
-
-   <para>
-    A sample file, <filename>share/recovery.conf.sample</>,
-    is provided in the installation's <filename>share/</> directory.
-   </para>
-
-  <sect1 id="archive-recovery-settings">
-
-    <title>Archive Recovery Settings</title>
-     <variablelist>
-
-     <varlistentry id="restore-command" xreflabel="restore_command">
-      <term><varname>restore_command</varname> (<type>string</type>)
-      <indexterm>
-        <primary><varname>restore_command</> recovery parameter</primary>
-      </indexterm>
-      </term>
-      <listitem>
-       <para>
-        The local shell command to execute to retrieve an archived segment of
-        the WAL file series. This parameter is required for archive recovery,
-        but optional for streaming replication.
-        Any <literal>%f</> in the string is
-        replaced by the name of the file to retrieve from the archive,
-        and any <literal>%p</> is replaced by the copy destination path name
-        on the server.
-        (The path name is relative to the current working directory,
-        i.e., the cluster's data directory.)
-        Any <literal>%r</> is replaced by the name of the file containing the
-        last valid restart point. That is the earliest file that must be kept
-        to allow a restore to be restartable, so this information can be used
-        to truncate the archive to just the minimum required to support
-        restarting from the current restore. <literal>%r</> is typically only
-        used by warm-standby configurations
-        (see <xref linkend="warm-standby">).
-        Write <literal>%%</> to embed an actual <literal>%</> character.
-       </para>
-
-       <para>
-        It is important for the command to return a zero exit status
-        only if it succeeds.  The command <emphasis>will</> be asked for file
-        names that are not present in the archive; it must return nonzero
-        when so asked.  Examples:
-<programlisting>
-restore_command = 'cp /mnt/server/archivedir/%f "%p"'
-restore_command = 'copy "C:\\server\\archivedir\\%f" "%p"'  # Windows
-</programlisting>
-        An exception is that if the command was terminated by a signal (other
-        than <systemitem>SIGTERM</systemitem>, which is used as part of a
-        database server shutdown) or an error by the shell (such as command
-        not found), then recovery will abort and the server will not start up.
-       </para>
-      </listitem>
-     </varlistentry>
-
-     <varlistentry id="archive-cleanup-command" xreflabel="archive_cleanup_command">
-      <term><varname>archive_cleanup_command</varname> (<type>string</type>)
-      <indexterm>
-        <primary><varname>archive_cleanup_command</> recovery parameter</primary>
-      </indexterm>
-      </term>
-      <listitem>
-       <para>
-        This optional parameter specifies a shell command that will be executed
-        at every restartpoint.  The purpose of
-        <varname>archive_cleanup_command</> is to provide a mechanism for
-        cleaning up old archived WAL files that are no longer needed by the
-        standby server.
-        Any <literal>%r</> is replaced by the name of the file containing the
-        last valid restart point.
-        That is the earliest file that must be <emphasis>kept</> to allow a
-        restore to be restartable, and so all files earlier than <literal>%r</>
-        may be safely removed.
-        This information can be used to truncate the archive to just the
-        minimum required to support restart from the current restore.
-        The <xref linkend="pgarchivecleanup"> module
-        is often used in <varname>archive_cleanup_command</> for
-        single-standby configurations, for example:
-<programlisting>archive_cleanup_command = 'pg_archivecleanup /mnt/server/archivedir %r'</programlisting>
-        Note however that if multiple standby servers are restoring from the
-        same archive directory, you will need to ensure that you do not delete
-        WAL files until they are no longer needed by any of the servers.
-        <varname>archive_cleanup_command</> would typically be used in a
-        warm-standby configuration (see <xref linkend="warm-standby">).
-        Write <literal>%%</> to embed an actual <literal>%</> character in the
-        command.
-       </para>
-       <para>
-        If the command returns a nonzero exit status then a warning log
-        message will be written.  An exception is that if the command was
-        terminated by a signal or an error by the shell (such as command not
-        found), a fatal error will be raised.
-       </para>
-      </listitem>
-     </varlistentry>
-
-     <varlistentry id="recovery-end-command" xreflabel="recovery_end_command">
-      <term><varname>recovery_end_command</varname> (<type>string</type>)
-      <indexterm>
-        <primary><varname>recovery_end_command</> recovery parameter</primary>
-      </indexterm>
-      </term>
-      <listitem>
-       <para>
-        This parameter specifies a shell command that will be executed once only
-        at the end of recovery. This parameter is optional. The purpose of the
-        <varname>recovery_end_command</> is to provide a mechanism for cleanup
-        following replication or recovery.
-        Any <literal>%r</> is replaced by the name of the file containing the
-        last valid restart point, like in <xref linkend="archive-cleanup-command">.
-       </para>
-       <para>
-        If the command returns a nonzero exit status then a warning log
-        message will be written and the database will proceed to start up
-        anyway.  An exception is that if the command was terminated by a
-        signal or an error by the shell (such as command not found), the
-        database will not proceed with startup.
-       </para>
-      </listitem>
-     </varlistentry>
-
-    </variablelist>
-
-  </sect1>
-
-  <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_lsn</>, <varname>recovery_target_name</>,
-      <varname>recovery_target_time</>, or <varname>recovery_target_xid</>
-      can be used; if more than one of these is specified in the configuration
-      file, the last entry will be used.
-     </para>
-
-     <variablelist>
-     <varlistentry id="recovery-target" xreflabel="recovery_target">
-      <term><varname>recovery_target</varname><literal> = 'immediate'</literal>
-      <indexterm>
-        <primary><varname>recovery_target</> recovery parameter</primary>
-      </indexterm>
-      </term>
-      <listitem>
-       <para>
-        This parameter specifies that recovery should end as soon as a
-        consistent state is reached, i.e. 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>)
-      <indexterm>
-        <primary><varname>recovery_target_name</> recovery parameter</primary>
-      </indexterm>
-      </term>
-      <listitem>
-       <para>
-        This parameter specifies the named restore point (created with
-        <function>pg_create_restore_point()</>) to which recovery will proceed.
-       </para>
-      </listitem>
-     </varlistentry>
-
-     <varlistentry id="recovery-target-time" xreflabel="recovery_target_time">
-      <term><varname>recovery_target_time</varname> (<type>timestamp</type>)
-      <indexterm>
-        <primary><varname>recovery_target_time</> recovery parameter</primary>
-      </indexterm>
-      </term>
-      <listitem>
-       <para>
-        This parameter specifies the time stamp up to which recovery
-        will proceed.
-        The precise stopping point is also influenced by
-        <xref linkend="recovery-target-inclusive">.
-       </para>
-      </listitem>
-     </varlistentry>
-
-     <varlistentry id="recovery-target-xid" xreflabel="recovery_target_xid">
-      <term><varname>recovery_target_xid</varname> (<type>string</type>)
-      <indexterm>
-        <primary><varname>recovery_target_xid</> recovery parameter</primary>
-      </indexterm>
-      </term>
-      <listitem>
-       <para>
-        This parameter specifies the transaction ID up to which recovery
-        will proceed. Keep in mind
-        that while transaction IDs are assigned sequentially at transaction
-        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.
-        The precise stopping point is also influenced by
-        <xref linkend="recovery-target-inclusive">.
-       </para>
-      </listitem>
-     </varlistentry>
-
-     <varlistentry id="recovery-target-lsn" xreflabel="recovery_target_lsn">
-      <term><varname>recovery_target_lsn</varname> (<type>pg_lsn</type>)
-      <indexterm>
-        <primary><varname>recovery_target_lsn</> recovery parameter</primary>
-      </indexterm>
-      </term>
-      <listitem>
-       <para>
-        This parameter specifies the LSN of the transaction log location up
-        to which recovery will proceed. The precise stopping point is also
-        influenced by <xref linkend="recovery-target-inclusive">. This
-        parameter is parsed using the system data type
-        <link linkend="datatype-pg-lsn"><type>pg_lsn</></link>.
-       </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">
-      <term><varname>recovery_target_inclusive</varname> (<type>boolean</type>)
-      <indexterm>
-        <primary><varname>recovery_target_inclusive</> recovery parameter</primary>
-      </indexterm>
-      </term>
-      <listitem>
-       <para>
-        Specifies whether to stop just after the specified recovery target
-        (<literal>true</literal>), or just before the recovery target
-        (<literal>false</literal>).
-        Applies when either <xref linkend="recovery-target-time">
-        or <xref linkend="recovery-target-xid"> is specified.
-        This setting controls whether transactions
-        having exactly the target commit time or ID, respectively, will
-        be included in the recovery.  Default is <literal>true</>.
-       </para>
-      </listitem>
-     </varlistentry>
-
-     <varlistentry id="recovery-target-timeline"
-                   xreflabel="recovery_target_timeline">
-      <term><varname>recovery_target_timeline</varname> (<type>string</type>)
-      <indexterm>
-        <primary><varname>recovery_target_timeline</> recovery parameter</primary>
-      </indexterm>
-      </term>
-      <listitem>
-       <para>
-        Specifies recovering into a particular timeline.  The default is
-        to recover along the same timeline that was current when the
-        base backup was taken. Setting this to <literal>latest</> recovers
-        to the latest timeline found in the archive, which is useful in
-        a standby server. Other than that you only need to set this parameter
-        in complex re-recovery situations, where you need to return to
-        a state that itself was reached after a point-in-time recovery.
-        See <xref linkend="backup-timelines"> for discussion.
-       </para>
-      </listitem>
-     </varlistentry>
-
-     <varlistentry id="recovery-target-action"
-                   xreflabel="recovery_target_action">
-      <term><varname>recovery_target_action</varname> (<type>enum</type>)
-      <indexterm>
-        <primary><varname>recovery_target_action</> recovery parameter</primary>
-      </indexterm>
-      </term>
-      <listitem>
-       <para>
-        Specifies what action the server should take once the recovery target is
-        reached. The default is <literal>pause</>, which means recovery will
-        be paused. <literal>promote</> means the recovery process will finish
-        and the server will start to accept connections.
-        Finally <literal>shutdown</> will stop the server after reaching the
-        recovery target.
-       </para>
-       <para>
-        The intended use of the <literal>pause</> setting is to allow queries
-        to be executed against the database to check if this recovery target
-        is the most desirable point for recovery.
-        The paused state can be resumed by
-        using <function>pg_xlog_replay_resume()</> (see
-        <xref linkend="functions-recovery-control-table">), which then
-        causes recovery to end. If this recovery target is not the
-        desired stopping point, then shut down the server, change the
-        recovery target settings to a later target and restart to
-        continue recovery.
-       </para>
-       <para>
-        The <literal>shutdown</> setting is useful to have the instance ready
-        at the exact replay point desired.  The instance will still be able to
-        replay more WAL records (and in fact will have to replay WAL records
-        since the last checkpoint next time it is started).
-       </para>
-       <para>
-        Note that because <filename>recovery.conf</> will not be renamed when
-        <varname>recovery_target_action</> is set to <literal>shutdown</>,
-        any subsequent start will end with immediate shutdown unless the
-        configuration is changed or the <filename>recovery.conf</> file is
-        removed manually.
-       </para>
-       <para>
-        This setting has no effect if no recovery target is set.
-        If <xref linkend="guc-hot-standby"> is not enabled, a setting of
-        <literal>pause</> will act the same as <literal>shutdown</>.
-       </para>
-      </listitem>
-     </varlistentry>
-
-    </variablelist>
-   </sect1>
-
-  <sect1 id="standby-settings">
-
-    <title>Standby Server Settings</title>
-     <variablelist>
-
-       <varlistentry id="standby-mode" xreflabel="standby_mode">
-        <term><varname>standby_mode</varname> (<type>boolean</type>)
-        <indexterm>
-          <primary><varname>standby_mode</> recovery parameter</primary>
-        </indexterm>
-        </term>
-        <listitem>
-         <para>
-          Specifies whether to start the <productname>PostgreSQL</> server as
-          a standby. If this parameter is <literal>on</>, the server will
-          not stop recovery when the end of archived WAL is reached, but
-          will keep trying to continue recovery by fetching new WAL segments
-          using <varname>restore_command</>
-          and/or by connecting to the primary server as specified by the
-          <varname>primary_conninfo</> setting.
-         </para>
-        </listitem>
-       </varlistentry>
-       <varlistentry id="primary-conninfo" xreflabel="primary_conninfo">
-        <term><varname>primary_conninfo</varname> (<type>string</type>)
-        <indexterm>
-          <primary><varname>primary_conninfo</> recovery parameter</primary>
-        </indexterm>
-        </term>
-        <listitem>
-         <para>
-          Specifies a connection string to be used for the standby server
-          to connect with the primary. This string is in the format
-          described in <xref linkend="libpq-connstring">. If any option is
-          unspecified in this string, then the corresponding environment
-          variable (see <xref linkend="libpq-envars">) is checked. If the
-          environment variable is not set either, then
-          defaults are used.
-         </para>
-         <para>
-          The connection string should specify the host name (or address)
-          of the primary server, as well as the port number if it is not
-          the same as the standby server's default.
-          Also specify a user name corresponding to a suitably-privileged role
-          on the primary (see
-          <xref linkend="streaming-replication-authentication">).
-          A password needs to be provided too, if the primary demands password
-          authentication.  It can be provided in the
-          <varname>primary_conninfo</varname> string, or in a separate
-          <filename>~/.pgpass</> file on the standby server (use
-          <literal>replication</> as the database name).
-          Do not specify a database name in the
-          <varname>primary_conninfo</varname> string.
-         </para>
-         <para>
-          This setting has no effect if <varname>standby_mode</> is <literal>off</>.
-         </para>
-        </listitem>
-       </varlistentry>
-       <varlistentry id="primary-slot-name" xreflabel="primary_slot_name">
-        <term><varname>primary_slot_name</varname> (<type>string</type>)
-        <indexterm>
-          <primary><varname>primary_slot_name</> recovery parameter</primary>
-        </indexterm>
-        </term>
-        <listitem>
-         <para>
-          Optionally specifies an existing replication slot to be used when
-          connecting to the primary via streaming replication to control
-          resource removal on the upstream node
-          (see <xref linkend="streaming-replication-slots">).
-          This setting has no effect if <varname>primary_conninfo</> is not
-          set.
-         </para>
-        </listitem>
-       </varlistentry>
-       <varlistentry id="trigger-file" xreflabel="trigger_file">
-        <term><varname>trigger_file</varname> (<type>string</type>)
-        <indexterm>
-          <primary><varname>trigger_file</> recovery parameter</primary>
-        </indexterm>
-        </term>
-        <listitem>
-         <para>
-          Specifies a trigger file whose presence ends recovery in the
-          standby.  Even if this value is not set, you can still promote
-          the standby using <command>pg_ctl promote</>.
-          This setting has no effect if <varname>standby_mode</> is <literal>off</>.
-         </para>
-        </listitem>
-       </varlistentry>
-
-     <varlistentry id="recovery-min-apply-delay" xreflabel="recovery_min_apply_delay">
-      <term><varname>recovery_min_apply_delay</varname> (<type>integer</type>)
-      <indexterm>
-        <primary><varname>recovery_min_apply_delay</> recovery parameter</primary>
-      </indexterm>
-      </term>
-      <listitem>
-       <para>
-        By default, a standby server restores WAL records from the
-        primary as soon as possible. It may be useful to have a time-delayed
-        copy of the data, offering opportunities to correct data loss errors.
-        This parameter allows you to delay recovery by a fixed period of time,
-        measured in milliseconds if no unit is specified.  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>
-        It is possible that the replication delay between servers exceeds the
-        value of this parameter, in which case no delay is added.
-        Note that the delay is calculated between the WAL time stamp as written
-        on master and the current time on the standby. Delays in transfer
-        because of network lag or cascading replication configurations
-        may reduce the actual wait time significantly. If the system
-        clocks on master and standby are not synchronized, this may lead to
-        recovery applying records earlier than expected; but that is not a
-        major issue because useful settings of this parameter are much larger
-        than typical time deviations between servers.
-       </para>
-       <para>
-        The delay occurs only on WAL records for transaction commits.
-        Other records are replayed as quickly as possible, which
-        is not a problem because MVCC visibility rules ensure their effects
-        are not visible until the corresponding commit record is applied.
-       </para>
-       <para>
-        The delay occurs once the database in recovery has reached a consistent
-        state, until the standby is promoted or triggered. After that the standby
-        will end recovery without further waiting.
-       </para>
-       <para>
-        This parameter is intended for use with streaming replication deployments;
-        however, if the parameter is specified it will be honored in all cases.
-
-        <varname>hot_standby_feedback</> will be delayed by use of this feature
-        which could lead to bloat on the master; use both together with care.
-
-        <warning>
-         <para>
-          Synchronous replication is affected by this setting when <varname>synchronous_commit</varname>
-          is set to <literal>remote_apply</literal>; every <literal>COMMIT</literal>
-          will need to wait to be applied.
-         </para>
-        </warning>
-       </para>
-      </listitem>
-     </varlistentry>
-
-     </variablelist>
-   </sect1>
-
-</chapter>
diff --git a/doc/src/sgml/ref/pgarchivecleanup.sgml b/doc/src/sgml/ref/pgarchivecleanup.sgml
index abe01be..dc29854 100644
--- a/doc/src/sgml/ref/pgarchivecleanup.sgml
+++ b/doc/src/sgml/ref/pgarchivecleanup.sgml
@@ -38,8 +38,8 @@
 
   <para>
    To configure a standby
-   server to use <application>pg_archivecleanup</>, put this into its
-   <filename>recovery.conf</filename> configuration file:
+   server to use <application>pg_archivecleanup</>, specify
+   <xref linkend="guc-archive-cleanup-command"> like this:
 <programlisting>
 archive_cleanup_command = 'pg_archivecleanup <replaceable>archivelocation</> %r'
 </programlisting>
@@ -47,7 +47,7 @@ archive_cleanup_command = 'pg_archivecleanup <replaceable>archivelocation</> %r'
    files should be removed.
   </para>
   <para>
-   When used within <xref linkend="archive-cleanup-command">, all WAL files
+   When used within <varname>archive_cleanup_command</>, all WAL files
    logically preceding the value of the <literal>%r</> argument will be removed
    from <replaceable>archivelocation</>. This minimizes the number of files
    that need to be retained, while preserving crash-restart capability.  Use of
diff --git a/doc/src/sgml/release-9.1.sgml b/doc/src/sgml/release-9.1.sgml
index edacfbf..cfef818 100644
--- a/doc/src/sgml/release-9.1.sgml
+++ b/doc/src/sgml/release-9.1.sgml
@@ -9811,7 +9811,7 @@ Branch: REL9_0_STABLE [9d6af7367] 2015-08-15 11:02:34 -0400
        <para>
         These named restore points can be specified as recovery
         targets using the new <filename>recovery.conf</> setting
-        <link linkend="recovery-target-name"><varname>recovery_target_name</></link>.
+        <link linkend="guc-recovery-target-name"><varname>recovery_target_name</></link>.
        </para>
       </listitem>
 
@@ -9843,8 +9843,7 @@ Branch: REL9_0_STABLE [9d6af7367] 2015-08-15 11:02:34 -0400
 
       <listitem>
        <para>
-        Allow <link
-        linkend="recovery-config"><filename>recovery.conf</></link>
+        Allow <filename>recovery.conf</>
         to use the same quoting behavior as <filename>postgresql.conf</>
         (Dimitri Fontaine)
        </para>
diff --git a/doc/src/sgml/release.sgml b/doc/src/sgml/release.sgml
index 472c1f6..4237a66 100644
--- a/doc/src/sgml/release.sgml
+++ b/doc/src/sgml/release.sgml
@@ -6,7 +6,7 @@ Typical markup:
 &<>                             use & escapes
 PostgreSQL                      <productname>
 postgresql.conf, pg_hba.conf,
-        recovery.conf           <filename>
+        recovery.trigger        <filename>
 [A-Z][A-Z_ ]+[A-Z_]             <command>, <literal>, <envar>, <acronym>
 [A-Za-z_][A-Za-z0-9_]+()        <function>
 -[-A-Za-z_]+                    <option>
diff --git a/src/backend/access/transam/recovery.conf.sample b/src/backend/access/transam/recovery.conf.sample
index 7a16751..15ce784 100644
--- a/src/backend/access/transam/recovery.conf.sample
+++ b/src/backend/access/transam/recovery.conf.sample
@@ -2,23 +2,14 @@
 # PostgreSQL recovery config file
 # -------------------------------
 #
-# Edit this file to provide the parameters that PostgreSQL needs to
-# perform an archive recovery of a database, or to act as a replication
-# standby.
+# PostgreSQL 10.0 or later, recovery.conf is no longer used. Instead,
+# specify all recovery parameters in postgresql.conf and create
+# recovery.trigger to enter archive recovery or standby mode.
 #
-# If "recovery.conf" is present in the PostgreSQL data directory, it is
-# read on postmaster startup.  After successful recovery, it is renamed
-# to "recovery.done" to ensure that we do not accidentally re-enter
-# archive recovery or standby mode.
+# If you must use recovery.conf, specify "include directives" in
+# postgresql.conf like this:
 #
-# This file consists of lines of the form:
-#
-#   name = value
-#
-# Comments are introduced with '#'.
-#
-# The complete list of option names and allowed values can be found
-# in the PostgreSQL documentation.
+#   include 'recovery.conf'
 #
 #---------------------------------------------------------------------------
 # ARCHIVE RECOVERY PARAMETERS
@@ -131,14 +122,6 @@
 #
 #primary_slot_name = ''
 #
-# By default, a standby server keeps restoring XLOG records from the
-# primary indefinitely. If you want to stop the standby mode, finish recovery
-# and open the system in read/write mode, specify a path to a trigger file.
-# The server will poll the trigger file path periodically and start as a
-# primary server when it's found.
-#
-#trigger_file = ''
-#
 # By default, a standby server restores XLOG records from the primary as
 # soon as possible. If you want to explicitly delay the replay of committed
 # transactions from the master, specify a minimum apply delay. For example,
diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c
index b8b5612..215f7ce 100644
--- a/src/backend/access/transam/xlog.c
+++ b/src/backend/access/transam/xlog.c
@@ -77,7 +77,6 @@
 extern uint32 bootstrap_data_checksum_version;
 
 /* File path names (all relative to $PGDATA) */
-#define RECOVERY_COMMAND_FILE	"recovery.conf"
 #define RECOVERY_SIGNAL_FILE	"recovery.trigger"
 #define PROMOTE_SIGNAL_FILE		"promote.trigger"
 
@@ -100,6 +99,25 @@ int			wal_level = WAL_LEVEL_MINIMAL;
 int			CommitDelay = 0;	/* precommit delay in microseconds */
 int			CommitSiblings = 5; /* # concurrent xacts needed to sleep */
 int			wal_retrieve_retry_interval = 5000;
+char	   *restore_command = NULL;
+char	   *archive_cleanup_command = NULL;
+char	   *recovery_end_command = NULL;
+bool		standby_mode = false;
+char	   *primary_conninfo = NULL;
+char	   *primary_slot_name = NULL;
+int			recovery_min_apply_delay = 0;
+RecoveryTargetType	recovery_target = RECOVERY_TARGET_UNSET;
+TransactionId	recovery_target_xid = InvalidTransactionId;
+TimestampTz	recovery_target_time = 0;
+char	   *recovery_target_name = NULL;
+XLogRecPtr recovery_target_lsn;
+bool		recovery_target_inclusive = true;
+char	   *recovery_target_timeline_string = NULL;
+char	   *recovery_target_action_string = NULL;
+char	   *recovery_target_lsn_string = NULL;
+char	   *recovery_target_string = NULL;
+TimeLineID	recovery_target_timeline = 0;
+RecoveryTargetAction recovery_target_action = RECOVERY_TARGET_ACTION_PAUSE;
 
 #ifdef WAL_DEBUG
 bool		XLOG_DEBUG = false;
@@ -244,38 +262,21 @@ bool		InArchiveRecovery = false;
 /* Was the last xlog file restored from archive, or local? */
 static bool restoredFromArchive = false;
 
-/* options taken from recovery.conf for archive recovery */
-char	   *recoveryRestoreCommand = NULL;
-static char *recoveryEndCommand = NULL;
-static char *archiveCleanupCommand = NULL;
-static RecoveryTargetType recoveryTarget = RECOVERY_TARGET_UNSET;
-static bool recoveryTargetInclusive = true;
-static RecoveryTargetAction recoveryTargetAction = RECOVERY_TARGET_ACTION_PAUSE;
-static TransactionId recoveryTargetXid;
-static TimestampTz recoveryTargetTime;
-static char *recoveryTargetName;
-static XLogRecPtr recoveryTargetLSN;
-static int	recovery_min_apply_delay = 0;
 static TimestampTz recoveryDelayUntilTime;
 
-/* options taken from recovery.conf for XLOG streaming */
-static bool StandbyModeRequested = false;
-static char *PrimaryConnInfo = NULL;
-static char *PrimarySlotName = NULL;
-
-/* are we currently in standby mode? */
-bool		StandbyMode = false;
-
 /*
  * if recoveryStopsBefore/After returns true, it saves information of the stop
  * point here
  */
+static RecoveryTargetType recoveryStopTarget;
 static TransactionId recoveryStopXid;
 static TimestampTz recoveryStopTime;
 static XLogRecPtr recoveryStopLSN;
 static char recoveryStopName[MAXFNAMELEN];
 static bool recoveryStopAfter;
 
+static TimestampTz recoveryDelayUntilTime;
+
 /*
  * During normal operation, the only timeline we care about is ThisTimeLineID.
  * During recovery, however, things are more complicated.  To simplify life
@@ -577,10 +578,10 @@ typedef struct XLogCtlData
 	TimeLineID	PrevTimeLineID;
 
 	/*
-	 * archiveCleanupCommand is read from recovery.conf but needs to be in
-	 * shared memory so that the checkpointer process can access it.
+	 * archive_cleanup_command is read from recovery.conf but needs to
+	 * be in shared memory so that the checkpointer process can access it.
 	 */
-	char		archiveCleanupCommand[MAXPGPATH];
+	char		archive_cleanup_command[MAXPGPATH];
 
 	/*
 	 * SharedRecoveryInProgress indicates if we're still in crash or archive
@@ -786,7 +787,7 @@ static bool holdingAllLocks = false;
 static MemoryContext walDebugCxt = NULL;
 #endif
 
-static void readRecoveryCommandFile(void);
+static void CheckRecoveryParameters(void);
 static void exitArchiveRecovery(TimeLineID endTLI, XLogRecPtr endOfLog);
 static bool recoveryStopsBefore(XLogReaderState *record);
 static bool recoveryStopsAfter(XLogReaderState *record);
@@ -3982,7 +3983,7 @@ ReadRecord(XLogReaderState *xlogreader, XLogRecPtr RecPtr, int emode,
 			/*
 			 * We only end up here without a message when XLogPageRead()
 			 * failed - in that case we already logged something. In
-			 * StandbyMode that only happens if we have been triggered, so we
+			 * standby_mode that only happens if we have been triggered, so we
 			 * shouldn't loop anymore in that case.
 			 */
 			if (errormsg)
@@ -4040,8 +4041,6 @@ ReadRecord(XLogReaderState *xlogreader, XLogRecPtr RecPtr, int emode,
 				ereport(DEBUG1,
 						(errmsg_internal("reached end of WAL in pg_wal, entering archive recovery")));
 				InArchiveRecovery = true;
-				if (StandbyModeRequested)
-					StandbyMode = true;
 
 				/* initialize minRecoveryPoint to this record */
 				LWLockAcquire(ControlFileLock, LW_EXCLUSIVE);
@@ -4071,7 +4070,7 @@ ReadRecord(XLogReaderState *xlogreader, XLogRecPtr RecPtr, int emode,
 			}
 
 			/* In standby mode, loop back to retry. Otherwise, give up. */
-			if (StandbyMode && !CheckForPromoteTrigger())
+			if (standby_mode && !CheckForPromoteTrigger())
 				continue;
 			else
 				return NULL;
@@ -4932,316 +4931,67 @@ str_time(pg_time_t tnow)
 }
 
 /*
- * See if there is a recovery command file (recovery.trigger), and if so
- * read in parameters for archive recovery and XLOG streaming.
- *
- * The file is parsed using the main configuration parser.
+ * Check recovery parameters and determine recovery target timeline.
  */
 static void
-readRecoveryCommandFile(void)
+CheckRecoveryParameters(void)
 {
-	FILE	   *fd;
-	TimeLineID	rtli = 0;
-	bool		rtliGiven = false;
-	ConfigVariable *item,
-			   *head = NULL,
-			   *tail = NULL;
-	bool		recoveryTargetActionSet = false;
-
-
-	fd = AllocateFile(RECOVERY_COMMAND_FILE, "r");
-	if (fd == NULL)
-	{
-		if (errno == ENOENT)
-			return;				/* not there, so no archive recovery */
-		ereport(FATAL,
-				(errcode_for_file_access(),
-				 errmsg("could not open recovery command file \"%s\": %m",
-						RECOVERY_COMMAND_FILE)));
-	}
-
-	/*
-	 * Since we're asking ParseConfigFp() to report errors as FATAL, there's
-	 * no need to check the return value.
-	 */
-	(void) ParseConfigFp(fd, RECOVERY_COMMAND_FILE, 0, FATAL, &head, &tail);
-
-	FreeFile(fd);
-
-	for (item = head; item; item = item->next)
-	{
-		if (strcmp(item->name, "restore_command") == 0)
-		{
-			recoveryRestoreCommand = pstrdup(item->value);
-			ereport(DEBUG2,
-					(errmsg_internal("restore_command = '%s'",
-									 recoveryRestoreCommand)));
-		}
-		else if (strcmp(item->name, "recovery_end_command") == 0)
-		{
-			recoveryEndCommand = pstrdup(item->value);
-			ereport(DEBUG2,
-					(errmsg_internal("recovery_end_command = '%s'",
-									 recoveryEndCommand)));
-		}
-		else if (strcmp(item->name, "archive_cleanup_command") == 0)
-		{
-			archiveCleanupCommand = pstrdup(item->value);
-			ereport(DEBUG2,
-					(errmsg_internal("archive_cleanup_command = '%s'",
-									 archiveCleanupCommand)));
-		}
-		else if (strcmp(item->name, "recovery_target_action") == 0)
-		{
-			if (strcmp(item->value, "pause") == 0)
-				recoveryTargetAction = RECOVERY_TARGET_ACTION_PAUSE;
-			else if (strcmp(item->value, "promote") == 0)
-				recoveryTargetAction = RECOVERY_TARGET_ACTION_PROMOTE;
-			else if (strcmp(item->value, "shutdown") == 0)
-				recoveryTargetAction = RECOVERY_TARGET_ACTION_SHUTDOWN;
-			else
-				ereport(ERROR,
-						(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
-				errmsg("invalid value for recovery parameter \"%s\": \"%s\"",
-					   "recovery_target_action",
-					   item->value),
-						 errhint("Valid values are \"pause\", \"promote\", and \"shutdown\".")));
-
-			ereport(DEBUG2,
-					(errmsg_internal("recovery_target_action = '%s'",
-									 item->value)));
-
-			recoveryTargetActionSet = true;
-		}
-		else if (strcmp(item->name, "recovery_target_timeline") == 0)
-		{
-			rtliGiven = true;
-			if (strcmp(item->value, "latest") == 0)
-				rtli = 0;
-			else
-			{
-				errno = 0;
-				rtli = (TimeLineID) strtoul(item->value, NULL, 0);
-				if (errno == EINVAL || errno == ERANGE)
-					ereport(FATAL,
-							(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
-							 errmsg("recovery_target_timeline is not a valid number: \"%s\"",
-									item->value)));
-			}
-			if (rtli)
-				ereport(DEBUG2,
-				   (errmsg_internal("recovery_target_timeline = %u", rtli)));
-			else
-				ereport(DEBUG2,
-					 (errmsg_internal("recovery_target_timeline = latest")));
-		}
-		else if (strcmp(item->name, "recovery_target_xid") == 0)
-		{
-			errno = 0;
-			recoveryTargetXid = (TransactionId) strtoul(item->value, NULL, 0);
-			if (errno == EINVAL || errno == ERANGE)
-				ereport(FATAL,
-						(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
-				  errmsg("recovery_target_xid is not a valid number: \"%s\"",
-						 item->value)));
-			ereport(DEBUG2,
-					(errmsg_internal("recovery_target_xid = %u",
-									 recoveryTargetXid)));
-			recoveryTarget = RECOVERY_TARGET_XID;
-		}
-		else if (strcmp(item->name, "recovery_target_time") == 0)
-		{
-			recoveryTarget = RECOVERY_TARGET_TIME;
-
-			/*
-			 * Convert the time string given by the user to TimestampTz form.
-			 */
-			recoveryTargetTime =
-				DatumGetTimestampTz(DirectFunctionCall3(timestamptz_in,
-												CStringGetDatum(item->value),
-												ObjectIdGetDatum(InvalidOid),
-														Int32GetDatum(-1)));
-			ereport(DEBUG2,
-					(errmsg_internal("recovery_target_time = '%s'",
-								   timestamptz_to_str(recoveryTargetTime))));
-		}
-		else if (strcmp(item->name, "recovery_target_name") == 0)
-		{
-			recoveryTarget = RECOVERY_TARGET_NAME;
-
-			recoveryTargetName = pstrdup(item->value);
-			if (strlen(recoveryTargetName) >= MAXFNAMELEN)
-				ereport(FATAL,
-						(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
-						 errmsg("recovery_target_name is too long (maximum %d characters)",
-								MAXFNAMELEN - 1)));
-
-			ereport(DEBUG2,
-					(errmsg_internal("recovery_target_name = '%s'",
-									 recoveryTargetName)));
-		}
-		else if (strcmp(item->name, "recovery_target_lsn") == 0)
-		{
-			recoveryTarget = RECOVERY_TARGET_LSN;
-
-			/*
-			 * Convert the LSN string given by the user to XLogRecPtr form.
-			 */
-			recoveryTargetLSN =
-				DatumGetLSN(DirectFunctionCall3(pg_lsn_in,
-												CStringGetDatum(item->value),
-												ObjectIdGetDatum(InvalidOid),
-														Int32GetDatum(-1)));
-			ereport(DEBUG2,
-					(errmsg_internal("recovery_target_lsn = '%X/%X'",
-									 (uint32) (recoveryTargetLSN >> 32),
-									 (uint32) recoveryTargetLSN)));
-		}
-		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 value for recovery parameter \"%s\": \"%s\"",
-					   "recovery_target",
-					   item->value),
-					   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)
-		{
-			/*
-			 * does nothing if a recovery_target is not also set
-			 */
-			if (!parse_bool(item->value, &recoveryTargetInclusive))
-				ereport(ERROR,
-						(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
-						 errmsg("parameter \"%s\" requires a Boolean value",
-								"recovery_target_inclusive")));
-			ereport(DEBUG2,
-					(errmsg_internal("recovery_target_inclusive = %s",
-									 item->value)));
-		}
-		else if (strcmp(item->name, "standby_mode") == 0)
-		{
-			if (!parse_bool(item->value, &StandbyModeRequested))
-				ereport(ERROR,
-						(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
-						 errmsg("parameter \"%s\" requires a Boolean value",
-								"standby_mode")));
-			ereport(DEBUG2,
-					(errmsg_internal("standby_mode = '%s'", item->value)));
-		}
-		else if (strcmp(item->name, "primary_conninfo") == 0)
-		{
-			PrimaryConnInfo = pstrdup(item->value);
-			ereport(DEBUG2,
-					(errmsg_internal("primary_conninfo = '%s'",
-									 PrimaryConnInfo)));
-		}
-		else if (strcmp(item->name, "primary_slot_name") == 0)
-		{
-			ReplicationSlotValidateName(item->value, ERROR);
-			PrimarySlotName = pstrdup(item->value);
-			ereport(DEBUG2,
-					(errmsg_internal("primary_slot_name = '%s'",
-									 PrimarySlotName)));
-		}
-		else if (strcmp(item->name, "recovery_min_apply_delay") == 0)
-		{
-			const char *hintmsg;
-
-			if (!parse_int(item->value, &recovery_min_apply_delay, GUC_UNIT_MS,
-						   &hintmsg))
-				ereport(ERROR,
-						(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
-						 errmsg("parameter \"%s\" requires a temporal value",
-								"recovery_min_apply_delay"),
-						 hintmsg ? errhint("%s", _(hintmsg)) : 0));
-			ereport(DEBUG2,
-					(errmsg_internal("recovery_min_apply_delay = '%s'", item->value)));
-		}
-		else
-			ereport(FATAL,
-					(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
-					 errmsg("unrecognized recovery parameter \"%s\"",
-							item->name)));
-	}
-
 	/*
 	 * Check for compulsory parameters
 	 */
-	if (StandbyModeRequested)
-	{
-		if (PrimaryConnInfo == NULL && recoveryRestoreCommand == NULL)
-			ereport(WARNING,
-					(errmsg("recovery command file \"%s\" specified neither primary_conninfo nor restore_command",
-							RECOVERY_COMMAND_FILE),
-					 errhint("The database server will regularly poll the pg_wal subdirectory to check for files placed there.")));
-	}
-	else
-	{
-		if (recoveryRestoreCommand == NULL)
-			ereport(FATAL,
-					(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
-					 errmsg("recovery command file \"%s\" must specify restore_command when standby mode is not enabled",
-							RECOVERY_COMMAND_FILE)));
-	}
+	if (standby_mode && !restore_command && !primary_conninfo)
+		ereport(WARNING,
+				(errmsg("neither primary_conninfo nor restore_command is specified"),
+				 errhint("The database server will regularly poll the pg_xlog subdirectory to "
+						 "check for files placed there until either of them is set in postgresql.conf.")));
+
+	CheckRestoreCommandSet();
 
 	/*
-	 * Override any inconsistent requests. Not that this is a change of
+	 * Override any inconsistent requests. Note that this is a change of
 	 * behaviour in 9.5; prior to this we simply ignored a request to pause if
 	 * hot_standby = off, which was surprising behaviour.
 	 */
-	if (recoveryTargetAction == RECOVERY_TARGET_ACTION_PAUSE &&
-		recoveryTargetActionSet &&
+	if (recovery_target_action == RECOVERY_TARGET_ACTION_PAUSE &&
+		strcmp(recovery_target_action_string, "") == 0 &&
 		!EnableHotStandby)
-		recoveryTargetAction = RECOVERY_TARGET_ACTION_SHUTDOWN;
-
-	/*
-	 * We don't support standby_mode in standalone backends; that requires
-	 * other processes such as the WAL receiver to be alive.
-	 */
-	if (StandbyModeRequested && !IsUnderPostmaster)
-		ereport(FATAL,
-				(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
-			errmsg("standby mode is not supported by single-user servers")));
-
-	/* Enable fetching from archive recovery area */
-	ArchiveRecoveryRequested = true;
-
-	/*
-	 * If user specified recovery_target_timeline, validate it or compute the
-	 * "latest" value.  We can't do this until after we've gotten the restore
-	 * command and set InArchiveRecovery, because we need to fetch timeline
-	 * history files from the archive.
-	 */
-	if (rtliGiven)
-	{
-		if (rtli)
-		{
-			/* Timeline 1 does not have a history file, all else should */
-			if (rtli != 1 && !existsTimeLineHistory(rtli))
-				ereport(FATAL,
-						(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
-						 errmsg("recovery target timeline %u does not exist",
-								rtli)));
-			recoveryTargetTLI = rtli;
-			recoveryTargetIsLatest = false;
-		}
-		else
+		recovery_target_action = RECOVERY_TARGET_ACTION_SHUTDOWN;
+
+ 	/*
+ 	 * We don't support standby_mode in standalone backends; that requires
+ 	 * other processes such as the WAL receiver to be alive.
+ 	 */
+	if (standby_mode && !IsUnderPostmaster)
+ 		ereport(FATAL,
+ 				(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ 			errmsg("standby mode is not supported by single-user servers")));
+ 
+ 	/*
+ 	 * If user specified recovery_target_timeline, validate it or compute the
+ 	 * "latest" value.  We can't do this until after we've gotten the restore
+ 	 * command and set InArchiveRecovery, because we need to fetch timeline
+ 	 * history files from the archive.
+ 	 */
+	if (strcmp(recovery_target_timeline_string, "") != 0)
+ 	{
+		if (recovery_target_timeline)
+ 		{
+ 			/* Timeline 1 does not have a history file, all else should */
+			if (recovery_target_timeline != 1 && !existsTimeLineHistory(recovery_target_timeline))
+ 				ereport(FATAL,
+ 						(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+ 						 errmsg("recovery target timeline %u does not exist",
+								recovery_target_timeline)));
+			recoveryTargetTLI = recovery_target_timeline;
+ 			recoveryTargetIsLatest = false;
+ 		}
+ 		else
 		{
 			/* We start the "latest" search from pg_control's timeline */
 			recoveryTargetTLI = findNewestTimeLine(recoveryTargetTLI);
 			recoveryTargetIsLatest = true;
 		}
 	}
-
-	FreeConfigVariables(head);
 }
 
 /*
@@ -5404,7 +5154,7 @@ recoveryStopsBefore(XLogReaderState *record)
 	TransactionId recordXid;
 
 	/* Check if we should stop as soon as reaching consistency */
-	if (recoveryTarget == RECOVERY_TARGET_IMMEDIATE && reachedConsistency)
+	if (recovery_target == RECOVERY_TARGET_IMMEDIATE && reachedConsistency)
 	{
 		ereport(LOG,
 				(errmsg("recovery stopping after reaching consistency")));
@@ -5418,9 +5168,9 @@ recoveryStopsBefore(XLogReaderState *record)
 	}
 
 	/* Check if target LSN has been reached */
-	if (recoveryTarget == RECOVERY_TARGET_LSN &&
-		!recoveryTargetInclusive &&
-		record->ReadRecPtr >= recoveryTargetLSN)
+	if (recovery_target == RECOVERY_TARGET_LSN &&
+		!recovery_target_inclusive &&
+		record->ReadRecPtr >= recovery_target_lsn)
 	{
 		recoveryStopAfter = false;
 		recoveryStopXid = InvalidTransactionId;
@@ -5475,7 +5225,7 @@ recoveryStopsBefore(XLogReaderState *record)
 	else
 		return false;
 
-	if (recoveryTarget == RECOVERY_TARGET_XID && !recoveryTargetInclusive)
+	if (recovery_target == RECOVERY_TARGET_XID && !recovery_target_inclusive)
 	{
 		/*
 		 * There can be only one transaction end record with this exact
@@ -5486,10 +5236,10 @@ recoveryStopsBefore(XLogReaderState *record)
 		 * they complete. A higher numbered xid will complete before you about
 		 * 50% of the time...
 		 */
-		stopsHere = (recordXid == recoveryTargetXid);
+		stopsHere = (recordXid == recovery_target_xid);
 	}
 
-	if (recoveryTarget == RECOVERY_TARGET_TIME &&
+	if (recovery_target == RECOVERY_TARGET_TIME &&
 		getRecordTimestamp(record, &recordXtime))
 	{
 		/*
@@ -5497,14 +5247,15 @@ recoveryStopsBefore(XLogReaderState *record)
 		 * we stop after the last one, if we are inclusive, or stop at the
 		 * first one if we are exclusive
 		 */
-		if (recoveryTargetInclusive)
-			stopsHere = (recordXtime > recoveryTargetTime);
+		if (recovery_target_inclusive)
+			stopsHere = (recordXtime > recovery_target_time);
 		else
-			stopsHere = (recordXtime >= recoveryTargetTime);
+			stopsHere = (recordXtime >= recovery_target_time);
 	}
 
 	if (stopsHere)
 	{
+		recoveryStopTarget = recovery_target;
 		recoveryStopAfter = false;
 		recoveryStopXid = recordXid;
 		recoveryStopTime = recordXtime;
@@ -5551,14 +5302,14 @@ recoveryStopsAfter(XLogReaderState *record)
 	 * There can be many restore points that share the same name; we stop at
 	 * the first one.
 	 */
-	if (recoveryTarget == RECOVERY_TARGET_NAME &&
+	if (recovery_target == RECOVERY_TARGET_NAME &&
 		rmid == RM_XLOG_ID && info == XLOG_RESTORE_POINT)
 	{
 		xl_restore_point *recordRestorePointData;
 
 		recordRestorePointData = (xl_restore_point *) XLogRecGetData(record);
 
-		if (strcmp(recordRestorePointData->rp_name, recoveryTargetName) == 0)
+		if (strcmp(recordRestorePointData->rp_name, recovery_target_name) == 0)
 		{
 			recoveryStopAfter = true;
 			recoveryStopXid = InvalidTransactionId;
@@ -5575,9 +5326,9 @@ recoveryStopsAfter(XLogReaderState *record)
 	}
 
 	/* Check if the target LSN has been reached */
-	if (recoveryTarget == RECOVERY_TARGET_LSN &&
-		recoveryTargetInclusive &&
-		record->ReadRecPtr >= recoveryTargetLSN)
+	if (recovery_target == RECOVERY_TARGET_LSN &&
+		recovery_target_inclusive &&
+		record->ReadRecPtr >= recovery_target_lsn)
 	{
 		recoveryStopAfter = true;
 		recoveryStopXid = InvalidTransactionId;
@@ -5640,8 +5391,8 @@ recoveryStopsAfter(XLogReaderState *record)
 		 * they complete. A higher numbered xid will complete before you about
 		 * 50% of the time...
 		 */
-		if (recoveryTarget == RECOVERY_TARGET_XID && recoveryTargetInclusive &&
-			recordXid == recoveryTargetXid)
+		if (recovery_target == RECOVERY_TARGET_XID && recovery_target_inclusive &&
+			recordXid == recovery_target_xid)
 		{
 			recoveryStopAfter = true;
 			recoveryStopXid = recordXid;
@@ -5670,7 +5421,7 @@ recoveryStopsAfter(XLogReaderState *record)
 	}
 
 	/* Check if we should stop as soon as reaching consistency */
-	if (recoveryTarget == RECOVERY_TARGET_IMMEDIATE && reachedConsistency)
+	if (recovery_target == RECOVERY_TARGET_IMMEDIATE && reachedConsistency)
 	{
 		ereport(LOG,
 				(errmsg("recovery stopping after reaching consistency")));
@@ -5968,6 +5719,19 @@ CheckRequiredParameterValues(void)
 }
 
 /*
+ * Check to see if restore_command must be set for archive recovery
+ * when standby mode is not enabled
+ */
+void
+CheckRestoreCommandSet(void)
+{
+	if (InArchiveRecovery && !standby_mode && !restore_command)
+		ereport(FATAL,
+				(errmsg("restore_command must be specified for archive recovery "
+						"when standby mode is not enabled")));
+}
+
+/*
  * This must be called ONCE during postmaster or standalone-backend startup
  */
 void
@@ -6078,40 +5842,32 @@ StartupXLOG(void)
 	 * Check for recovery trigger file, and if so set up state for offline
 	 * recovery
 	 */
-	CheckForRecoveryTrigger();
-	readRecoveryCommandFile(); // remove this later
-
-	/*
-	 * Save archive_cleanup_command in shared memory so that other processes
-	 * can see it.
-	 */
-	strlcpy(XLogCtl->archiveCleanupCommand,
-			archiveCleanupCommand ? archiveCleanupCommand : "",
-			sizeof(XLogCtl->archiveCleanupCommand));
+	if (CheckForRecoveryTrigger())
+		CheckRecoveryParameters();
 
 	if (ArchiveRecoveryRequested)
 	{
-		if (StandbyModeRequested)
+		if (standby_mode)
 			ereport(LOG,
 					(errmsg("entering standby mode")));
-		else if (recoveryTarget == RECOVERY_TARGET_XID)
+		else if (recovery_target == RECOVERY_TARGET_XID)
 			ereport(LOG,
 					(errmsg("starting point-in-time recovery to XID %u",
-							recoveryTargetXid)));
-		else if (recoveryTarget == RECOVERY_TARGET_TIME)
+							recovery_target_xid)));
+		else if (recovery_target == RECOVERY_TARGET_TIME)
 			ereport(LOG,
 					(errmsg("starting point-in-time recovery to %s",
-							timestamptz_to_str(recoveryTargetTime))));
-		else if (recoveryTarget == RECOVERY_TARGET_NAME)
+							timestamptz_to_str(recovery_target_time))));
+		else if (recovery_target == RECOVERY_TARGET_NAME)
 			ereport(LOG,
 					(errmsg("starting point-in-time recovery to \"%s\"",
-							recoveryTargetName)));
-		else if (recoveryTarget == RECOVERY_TARGET_LSN)
+							recovery_target_name)));
+		else if (recovery_target == RECOVERY_TARGET_LSN)
 			ereport(LOG,
 					(errmsg("starting point-in-time recovery to WAL position (LSN) \"%X/%X\"",
-							(uint32) (recoveryTargetLSN >> 32),
-							(uint32) recoveryTargetLSN)));
-		else if (recoveryTarget == RECOVERY_TARGET_IMMEDIATE)
+							(uint32) (recovery_target_lsn >> 32),
+							(uint32) recovery_target_lsn)));
+		else if (recovery_target == RECOVERY_TARGET_IMMEDIATE)
 			ereport(LOG,
 					(errmsg("starting point-in-time recovery to earliest consistent point")));
 		else
@@ -6123,7 +5879,7 @@ StartupXLOG(void)
 	 * Take ownership of the wakeup latch if we're going to sleep during
 	 * recovery.
 	 */
-	if (StandbyModeRequested)
+	if (standby_mode)
 		OwnLatch(&XLogCtl->recoveryWakeupLatch);
 
 	/* Set up XLOG reader facility */
@@ -6147,8 +5903,6 @@ StartupXLOG(void)
 		 * archive recovery directly.
 		 */
 		InArchiveRecovery = true;
-		if (StandbyModeRequested)
-			StandbyMode = true;
 
 		/*
 		 * When a backup_label file is present, we want to roll forward from
@@ -6274,8 +6028,6 @@ StartupXLOG(void)
 			 ControlFile->state == DB_SHUTDOWNED))
 		{
 			InArchiveRecovery = true;
-			if (StandbyModeRequested)
-				StandbyMode = true;
 		}
 
 		/*
@@ -6291,7 +6043,7 @@ StartupXLOG(void)
 					(errmsg("checkpoint record is at %X/%X",
 				   (uint32) (checkPointLoc >> 32), (uint32) checkPointLoc)));
 		}
-		else if (StandbyMode)
+		else if (standby_mode)
 		{
 			/*
 			 * The last valid checkpoint record required for a streaming
@@ -7010,7 +6762,7 @@ StartupXLOG(void)
 				 * this, Resource Managers may choose to do permanent
 				 * corrective actions at end of recovery.
 				 */
-				switch (recoveryTargetAction)
+				switch (recovery_target_action)
 				{
 					case RECOVERY_TARGET_ACTION_SHUTDOWN:
 
@@ -7079,7 +6831,7 @@ StartupXLOG(void)
 	 * We don't need the latch anymore. It's not strictly necessary to disown
 	 * it, but let's do it for the sake of tidiness.
 	 */
-	if (StandbyModeRequested)
+	if (standby_mode)
 		DisownLatch(&XLogCtl->recoveryWakeupLatch);
 
 	/*
@@ -7087,7 +6839,7 @@ StartupXLOG(void)
 	 * recovery to force fetching the files (which would be required at end of
 	 * recovery, e.g., timeline history file) from archive or pg_wal.
 	 */
-	StandbyMode = false;
+	SetConfigOption("standby_mode", "false", PGC_POSTMASTER, PGC_S_OVERRIDE);
 
 	/*
 	 * Re-fetch the last valid or last applied record, so we can identify the
@@ -7170,27 +6922,27 @@ StartupXLOG(void)
 		 * Create a comment for the history file to explain why and where
 		 * timeline changed.
 		 */
-		if (recoveryTarget == RECOVERY_TARGET_XID)
+		if (recovery_target == RECOVERY_TARGET_XID)
 			snprintf(reason, sizeof(reason),
 					 "%s transaction %u",
 					 recoveryStopAfter ? "after" : "before",
 					 recoveryStopXid);
-		else if (recoveryTarget == RECOVERY_TARGET_TIME)
+		else if (recovery_target == RECOVERY_TARGET_TIME)
 			snprintf(reason, sizeof(reason),
 					 "%s %s\n",
 					 recoveryStopAfter ? "after" : "before",
 					 timestamptz_to_str(recoveryStopTime));
-		else if (recoveryTarget == RECOVERY_TARGET_LSN)
+		else if (recovery_target == RECOVERY_TARGET_LSN)
 			snprintf(reason, sizeof(reason),
 					 "%s LSN %X/%X\n",
 					 recoveryStopAfter ? "after" : "before",
 					 (uint32 ) (recoveryStopLSN >> 32),
 					 (uint32) recoveryStopLSN);
-		else if (recoveryTarget == RECOVERY_TARGET_NAME)
+		else if (recovery_target == RECOVERY_TARGET_NAME)
 			snprintf(reason, sizeof(reason),
 					 "at restore point \"%s\"",
 					 recoveryStopName);
-		else if (recoveryTarget == RECOVERY_TARGET_IMMEDIATE)
+		else if (recovery_target == RECOVERY_TARGET_IMMEDIATE)
 			snprintf(reason, sizeof(reason), "reached consistency");
 		else
 			snprintf(reason, sizeof(reason), "no recovery target specified");
@@ -7329,8 +7081,8 @@ StartupXLOG(void)
 		/*
 		 * And finally, execute the recovery_end_command, if any.
 		 */
-		if (recoveryEndCommand)
-			ExecuteRecoveryCommand(recoveryEndCommand,
+		if (recovery_end_command)
+			ExecuteRecoveryCommand(recovery_end_command,
 								   "recovery_end_command",
 								   true);
 	}
@@ -9011,8 +8763,8 @@ CreateRestartPoint(int flags)
 	/*
 	 * Finally, execute archive_cleanup_command, if any.
 	 */
-	if (XLogCtl->archiveCleanupCommand[0])
-		ExecuteRecoveryCommand(XLogCtl->archiveCleanupCommand,
+	if (archive_cleanup_command)
+		ExecuteRecoveryCommand(archive_cleanup_command,
 							   "archive_cleanup_command",
 							   false);
 
@@ -11193,7 +10945,7 @@ next_record_is_invalid:
 	readSource = 0;
 
 	/* In standby-mode, keep trying */
-	if (StandbyMode)
+	if (standby_mode)
 		goto retry;
 	else
 		return -1;
@@ -11279,7 +11031,7 @@ WaitForWALToBecomeAvailable(XLogRecPtr RecPtr, bool randAccess,
 					 * trigger file, we still finish replaying as much as we
 					 * can from archive and pg_wal before failover.
 					 */
-					if (StandbyMode && CheckForPromoteTrigger())
+					if (standby_mode && CheckForPromoteTrigger())
 					{
 						ShutdownWalRcv();
 						return false;
@@ -11289,7 +11041,7 @@ WaitForWALToBecomeAvailable(XLogRecPtr RecPtr, bool randAccess,
 					 * Not in standby mode, and we've now tried the archive
 					 * and pg_wal.
 					 */
-					if (!StandbyMode)
+					if (!standby_mode)
 						return false;
 
 					/*
@@ -11302,7 +11054,7 @@ WaitForWALToBecomeAvailable(XLogRecPtr RecPtr, bool randAccess,
 					 * that when we later jump backwards to start redo at
 					 * RedoStartLSN, we will have the logs streamed already.
 					 */
-					if (PrimaryConnInfo)
+					if (primary_conninfo)
 					{
 						XLogRecPtr	ptr;
 						TimeLineID	tli;
@@ -11323,8 +11075,8 @@ WaitForWALToBecomeAvailable(XLogRecPtr RecPtr, bool randAccess,
 									 tli, curFileTLI);
 						}
 						curFileTLI = tli;
-						RequestXLogStreaming(tli, ptr, PrimaryConnInfo,
-											 PrimarySlotName);
+						RequestXLogStreaming(tli, ptr, primary_conninfo,
+											 primary_slot_name);
 						receivedUpto = 0;
 					}
 
diff --git a/src/backend/access/transam/xlogarchive.c b/src/backend/access/transam/xlogarchive.c
index b919164..1b9a265 100644
--- a/src/backend/access/transam/xlogarchive.c
+++ b/src/backend/access/transam/xlogarchive.c
@@ -67,7 +67,7 @@ RestoreArchivedFile(char *path, const char *xlogfname,
 	TimeLineID	restartTli;
 
 	/* In standby mode, restore_command might not be supplied */
-	if (recoveryRestoreCommand == NULL)
+	if (restore_command == NULL)
 		goto not_available;
 
 	/*
@@ -150,7 +150,7 @@ RestoreArchivedFile(char *path, const char *xlogfname,
 	endp = xlogRestoreCmd + MAXPGPATH - 1;
 	*endp = '\0';
 
-	for (sp = recoveryRestoreCommand; *sp; sp++)
+	for (sp = restore_command; *sp; sp++)
 	{
 		if (*sp == '%')
 		{
@@ -236,7 +236,7 @@ RestoreArchivedFile(char *path, const char *xlogfname,
 				 * incorrectly conclude we've reached the end of WAL and we're
 				 * done recovering ...
 				 */
-				if (StandbyMode && stat_buf.st_size < expectedSize)
+				if (standby_mode && stat_buf.st_size < expectedSize)
 					elevel = DEBUG1;
 				else
 					elevel = FATAL;
@@ -409,7 +409,7 @@ ExecuteRecoveryCommand(char *command, char *commandName, bool failOnSignal)
 
 		ereport((signaled && failOnSignal) ? FATAL : WARNING,
 		/*------
-		   translator: First %s represents a recovery.conf parameter name like
+		   translator: First %s represents a postgresql.conf parameter name like
 		  "recovery_end_command", the 2nd is the value of that parameter, the
 		  third an already translated error message. */
 				(errmsg("%s \"%s\": %s", commandName,
diff --git a/src/backend/postmaster/startup.c b/src/backend/postmaster/startup.c
index a7ae7e3..1a6adac 100644
--- a/src/backend/postmaster/startup.c
+++ b/src/backend/postmaster/startup.c
@@ -153,6 +153,8 @@ HandleStartupProcInterrupts(void)
 	{
 		got_SIGHUP = false;
 		ProcessConfigFile(PGC_SIGHUP);
+
+		CheckRestoreCommandSet();
 	}
 
 	/*
diff --git a/src/backend/replication/walreceiver.c b/src/backend/replication/walreceiver.c
index 2bb3dce..bf277da 100644
--- a/src/backend/replication/walreceiver.c
+++ b/src/backend/replication/walreceiver.c
@@ -416,9 +416,33 @@ WalReceiverMain(void)
 
 				if (got_SIGHUP)
 				{
+					char	*conninfo = pstrdup(primary_conninfo);
+					char	*slotname = pstrdup(primary_slot_name);
+
 					got_SIGHUP = false;
 					ProcessConfigFile(PGC_SIGHUP);
 					XLogWalRcvSendHSFeedback(true);
+
+					/*
+					 * If primary_conninfo has been changed while walreceiver is running,
+					 * shut down walreceiver so that a new walreceiver is started and
+					 * initiates replication with the new primary_conninfo.
+					 */
+					if (strcmp(conninfo, primary_conninfo) != 0)
+						ereport(FATAL,
+								(errcode(ERRCODE_ADMIN_SHUTDOWN),
+								 errmsg("terminating walreceiver process because primary_conninfo was changed")));
+
+					/*
+					 * And the same for primary_slot_name.
+					 */
+					if (strcmp(slotname, primary_slot_name) != 0)
+						ereport(FATAL,
+								(errcode(ERRCODE_ADMIN_SHUTDOWN),
+								 errmsg("terminating walreceiver process because primary_slot_name was changed")));
+
+					pfree(conninfo);
+					pfree(slotname);
 				}
 
 				/* See if we can read data immediately */
diff --git a/src/backend/utils/misc/guc-file.l b/src/backend/utils/misc/guc-file.l
index dae5015..101d3f5 100644
--- a/src/backend/utils/misc/guc-file.l
+++ b/src/backend/utils/misc/guc-file.l
@@ -197,6 +197,8 @@ ProcessConfigFileInternal(GucContext context, bool applySettings, int elevel)
 	 */
 	if (DataDir)
 	{
+		struct stat stat_buf;
+
 		if (!ParseConfigFile(PG_AUTOCONF_FILENAME, false,
 							 NULL, 0, 0, elevel,
 							 &head, &tail))
@@ -206,6 +208,22 @@ ProcessConfigFileInternal(GucContext context, bool applySettings, int elevel)
 			ConfFileWithError = PG_AUTOCONF_FILENAME;
 			goto bail_out;
 		}
+
+		/*
+		 * For backwards compatibility, we also read recovery.conf if
+		 * it exists.
+		 */
+
+		if (stat("recovery.conf", &stat_buf) == 0 &&
+			!ParseConfigFile("recovery.conf", false,
+							 NULL, 0, 0, elevel,
+							 &head, &tail))
+		{
+			/* Syntax error(s) detected in the file, so bail out */
+			error = true;
+			ConfFileWithError = "recovery.conf";
+			goto bail_out;
+		}
 	}
 	else
 	{
diff --git a/src/backend/utils/misc/guc.c b/src/backend/utils/misc/guc.c
index 65660c1..2afe672 100644
--- a/src/backend/utils/misc/guc.c
+++ b/src/backend/utils/misc/guc.c
@@ -31,6 +31,7 @@
 #include "access/transam.h"
 #include "access/twophase.h"
 #include "access/xact.h"
+#include "access/xlog_internal.h"
 #include "catalog/namespace.h"
 #include "commands/async.h"
 #include "commands/prepare.h"
@@ -77,6 +78,7 @@
 #include "utils/guc_tables.h"
 #include "utils/memutils.h"
 #include "utils/pg_locale.h"
+#include "utils/pg_lsn.h"
 #include "utils/plancache.h"
 #include "utils/portal.h"
 #include "utils/ps_status.h"
@@ -182,6 +184,21 @@ static void assign_application_name(const char *newval, void *extra);
 static bool check_cluster_name(char **newval, void **extra, GucSource source);
 static const char *show_unix_socket_permissions(void);
 static const char *show_log_file_mode(void);
+static bool check_recovery_target(char **newval, void **extra, GucSource source);
+static void assign_recovery_target(const char *newval, void *extra);
+static bool check_recovery_target_xid(char **newval, void **extra, GucSource source);
+static void assign_recovery_target_xid(const char *newval, void *extra);
+static bool check_recovery_target_name(char **newval, void **extra, GucSource source);
+static void assign_recovery_target_name(const char *newval, void *extra);
+static bool check_recovery_target_time(char **newval, void **extra, GucSource source);
+static void assign_recovery_target_time(const char *newval, void *extra);
+static bool check_recovery_target_lsn(char **newval, void **extra, GucSource source);
+static void assign_recovery_target_lsn(const char *newval, void *extra);
+static bool check_recovery_target_timeline(char **newval, void **extra, GucSource source);
+static void assign_recovery_target_timeline(const char *newval, void *extra);
+static bool check_recovery_target_action(char **newval, void **extra, GucSource source);
+static void assign_recovery_target_action(const char *newval, void *extra);
+static bool check_primary_slot_name(char **newval, void **extra, GucSource source);
 
 /* Private functions in guc-file.l that need to be called from guc.c */
 static ConfigVariable *ProcessConfigFileInternal(GucContext context,
@@ -512,6 +529,8 @@ static bool data_checksums;
 static int	wal_segment_size;
 static bool integer_datetimes;
 static bool assert_enabled;
+static char *recovery_target_xid_string;
+static char *recovery_target_time_string;
 
 /* should be static, but commands/variable.c needs to get at this */
 char	   *role_string;
@@ -593,6 +612,10 @@ const char *const config_group_names[] =
 	gettext_noop("Write-Ahead Log / Checkpoints"),
 	/* WAL_ARCHIVING */
 	gettext_noop("Write-Ahead Log / Archiving"),
+	/* WAL_ARCHIVE_RECOVERY */
+	gettext_noop("Write-Ahead Log / Archive Recovery"),
+	/* WAL_RECOVERY_TARGET */
+	gettext_noop("Write-Ahead Log / Recovery Target"),
 	/* REPLICATION */
 	gettext_noop("Replication"),
 	/* REPLICATION_SENDING */
@@ -1559,6 +1582,26 @@ static struct config_bool ConfigureNamesBool[] =
 	},
 
 	{
+		{"recovery_target_inclusive", PGC_SIGHUP, WAL_RECOVERY_TARGET,
+			gettext_noop("Sets whether to include or exclude transaction with recovery target."),
+			NULL
+		},
+		&recovery_target_inclusive,
+		true,
+		NULL, NULL, NULL
+	},
+
+	{
+		{"standby_mode", PGC_POSTMASTER, REPLICATION_STANDBY,
+			gettext_noop("Sets whether to start the server as a standby."),
+			NULL
+		},
+		&standby_mode,
+		false,
+		NULL, NULL, NULL
+	},
+
+	{
 		{"hot_standby", PGC_POSTMASTER, REPLICATION_STANDBY,
 			gettext_noop("Allows connections and queries during recovery."),
 			NULL
@@ -1799,6 +1842,17 @@ static struct config_int ConfigureNamesInt[] =
 	},
 
 	{
+		{"recovery_min_apply_delay", PGC_SIGHUP, REPLICATION_STANDBY,
+			gettext_noop("Sets the minimum delay to apply changes during recovery."),
+			NULL,
+			GUC_UNIT_MS
+		},
+		&recovery_min_apply_delay,
+		0, 0, INT_MAX,
+		NULL, NULL, NULL
+	},
+
+	{
 		{"wal_receiver_status_interval", PGC_SIGHUP, REPLICATION_STANDBY,
 			gettext_noop("Sets the maximum interval between WAL receiver status reports to the primary."),
 			NULL,
@@ -2998,6 +3052,128 @@ static struct config_string ConfigureNamesString[] =
 	},
 
 	{
+		{"restore_command", PGC_SIGHUP, WAL_ARCHIVE_RECOVERY,
+			gettext_noop("Sets the shell command that will retrieve an archived WAL file."),
+			NULL
+		},
+		&restore_command,
+		"",
+		NULL, NULL, NULL
+	},
+
+	{
+		{"archive_cleanup_command", PGC_SIGHUP, WAL_ARCHIVE_RECOVERY,
+			gettext_noop("Sets the shell command that will be executed at every restartpoint."),
+			NULL
+		},
+		&archive_cleanup_command,
+		"",
+		NULL, NULL, NULL
+	},
+
+	{
+		{"recovery_end_command", PGC_SIGHUP, WAL_ARCHIVE_RECOVERY,
+			gettext_noop("Sets the shell command that will be executed once only at the end of recovery."),
+			NULL
+		},
+		&recovery_end_command,
+		"",
+		NULL, NULL, NULL
+	},
+
+	{
+		{"recovery_target", PGC_SIGHUP, WAL_RECOVERY_TARGET,
+			gettext_noop("Sets the desired recovery target."),
+			NULL
+		},
+		&recovery_target_string,
+		"",
+		check_recovery_target, assign_recovery_target, NULL
+	},
+
+	{
+		{"recovery_target_xid", PGC_SIGHUP, WAL_RECOVERY_TARGET,
+			gettext_noop("Sets the transaction ID up to which recovery will proceed."),
+			NULL
+		},
+		&recovery_target_xid_string,
+		"",
+		check_recovery_target_xid, assign_recovery_target_xid, NULL
+	},
+
+	{
+		{"recovery_target_name", PGC_SIGHUP, WAL_RECOVERY_TARGET,
+			gettext_noop("Sets the named restore point."),
+			NULL
+		},
+		&recovery_target_name,
+		"",
+		check_recovery_target_name, assign_recovery_target_name, NULL
+	},
+
+	{
+		{"recovery_target_time", PGC_SIGHUP, WAL_RECOVERY_TARGET,
+			gettext_noop("Sets the time stamp up to which recovery will proceed."),
+			NULL
+		},
+		&recovery_target_time_string,
+		"",
+		check_recovery_target_time, assign_recovery_target_time, NULL
+	},
+
+	{
+		{"recovery_target_lsn", PGC_SIGHUP, WAL_RECOVERY_TARGET,
+			gettext_noop("Sets the LSN up to which recovery will proceed."),
+			NULL
+		},
+		&recovery_target_lsn_string,
+		"",
+		check_recovery_target_lsn, assign_recovery_target_lsn, NULL
+	},
+
+	{
+		{"recovery_target_timeline", PGC_SIGHUP, WAL_RECOVERY_TARGET,
+			gettext_noop("Sets recoverying into a particular timeline."),
+			NULL
+		},
+		&recovery_target_timeline_string,
+		"",
+		check_recovery_target_timeline, assign_recovery_target_timeline, NULL
+	},
+
+	{
+		{"recovery_target_action", PGC_SIGHUP, WAL_RECOVERY_TARGET,
+			gettext_noop("Sets the action to perform upon reaching the recovery target."),
+			NULL
+		},
+		&recovery_target_action_string,
+		"",
+		check_recovery_target_action, assign_recovery_target_action, NULL
+	},
+
+	{
+		{"primary_conninfo", PGC_SIGHUP, REPLICATION_STANDBY,
+			gettext_noop("Sets the connection string to be used to connect with the primary."),
+			NULL,
+			GUC_SUPERUSER_ONLY
+		},
+		&primary_conninfo,
+		"",
+		NULL, NULL, NULL
+	},
+
+	{
+		{"primary_slot_name", PGC_SIGHUP, REPLICATION_STANDBY,
+			gettext_noop("Sets the name of the replication slot to use on the primary."),
+			NULL,
+			GUC_SUPERUSER_ONLY
+		},
+		&primary_slot_name,
+		"",
+		check_primary_slot_name, NULL, NULL
+	},
+
+	{
 		{"client_encoding", PGC_USERSET, CLIENT_CONN_LOCALE,
 			gettext_noop("Sets the client's character set encoding."),
 			NULL,
@@ -10327,4 +10503,258 @@ show_log_file_mode(void)
 	return buf;
 }
 
+static bool
+check_recovery_target(char **newval, void **extra, GucSource source)
+{
+	RecoveryTargetType *myextra;
+	RecoveryTargetType rt = RECOVERY_TARGET_UNSET;
+
+	if (strcmp(*newval, "xid") == 0)
+		rt = RECOVERY_TARGET_XID;
+	else if (strcmp(*newval, "time") == 0)
+		rt = RECOVERY_TARGET_TIME;
+	else if (strcmp(*newval, "name") == 0)
+		rt = RECOVERY_TARGET_NAME;
+	else if (strcmp(*newval, "lsn") == 0)
+		rt = RECOVERY_TARGET_LSN;
+	else if (strcmp(*newval, "immediate") == 0)
+		rt = RECOVERY_TARGET_IMMEDIATE;
+	else if (strcmp(*newval, "") != 0)
+	{
+		GUC_check_errdetail("recovery_target is not valid: \"%s\"", *newval);
+		return false;
+	}
+
+	myextra = (RecoveryTargetType *) guc_malloc(ERROR, sizeof(RecoveryTargetType));
+	*myextra = rt;
+	*extra = (void *) myextra;
+
+	return true;
+}
+
+static void
+assign_recovery_target(const char *newval, void *extra)
+{
+	recovery_target = *((RecoveryTargetType *) extra);
+}
+
+static bool
+check_recovery_target_xid(char **newval, void **extra, GucSource source)
+{
+	TransactionId   xid;
+	TransactionId   *myextra;
+
+	if (strcmp(*newval, "") == 0)
+		xid = InvalidTransactionId;
+	else
+	{
+		errno = 0;
+		xid = (TransactionId) strtoul(*newval, NULL, 0);
+		if (errno == EINVAL || errno == ERANGE)
+		{
+			GUC_check_errdetail("recovery_target_xid is not a valid number: \"%s\"",
+								*newval);
+			return false;
+		}
+	}
+
+	myextra = (TransactionId *) guc_malloc(ERROR, sizeof(TransactionId));
+	*myextra = xid;
+	*extra = (void *) myextra;
+
+	return true;
+}
+
+static void
+assign_recovery_target_xid(const char *newval, void *extra)
+{
+	recovery_target_xid = *((TransactionId *) extra);
+}
+
+static bool
+check_recovery_target_name(char **newval, void **extra, GucSource source)
+{
+	if (strlen(*newval) >= MAXFNAMELEN)
+	{
+		GUC_check_errdetail("recovery_target_name is too long (maximum %d characters)",
+							MAXFNAMELEN - 1);
+		return false;
+	}
+	return true;
+}
+
+static void
+assign_recovery_target_name(const char *newval, void *extra)
+{
+	if (newval && *newval)
+		recovery_target_name = newval;
+}
+
+static bool
+check_recovery_target_time(char **newval, void **extra, GucSource source)
+{
+	TimestampTz     time;
+	TimestampTz     *myextra;
+	MemoryContext oldcontext = CurrentMemoryContext;
+
+	PG_TRY();
+	{
+		time = (strcmp(*newval, "") == 0) ?
+			0 :
+			DatumGetTimestampTz(DirectFunctionCall3(timestamptz_in,
+													CStringGetDatum(*newval),
+													ObjectIdGetDatum(InvalidOid),
+													Int32GetDatum(-1)));
+	}
+	PG_CATCH();
+	{
+		ErrorData  *edata;
+
+		/* Save error info */
+		MemoryContextSwitchTo(oldcontext);
+		edata = CopyErrorData();
+		FlushErrorState();
+
+		/* Pass the error message */
+		GUC_check_errdetail("%s", edata->message);
+		FreeErrorData(edata);
+		return false;
+	}
+	PG_END_TRY();
+
+	myextra = (TimestampTz *) guc_malloc(ERROR, sizeof(TimestampTz));
+	*myextra = time;
+	*extra = (void *) myextra;
+
+	return true;
+}
+
+static void
+assign_recovery_target_time(const char *newval, void *extra)
+{
+	recovery_target_time = *((TimestampTz *) extra);
+}
+
+static bool
+check_recovery_target_lsn(char **newval, void **extra, GucSource source)
+{
+	XLogRecPtr		lsn;
+	XLogRecPtr	   *myextra;
+	MemoryContext oldcontext = CurrentMemoryContext;
+
+	PG_TRY();
+	{
+		lsn = (strcmp(*newval, "") == 0) ?
+			InvalidXLogRecPtr :
+			DatumGetLSN(DirectFunctionCall3(pg_lsn_in,
+											CStringGetDatum(*newval),
+											ObjectIdGetDatum(InvalidOid),
+											Int32GetDatum(-1)));
+	}
+	PG_CATCH();
+	{
+		ErrorData  *edata;
+
+		/* Save error info */
+		MemoryContextSwitchTo(oldcontext);
+		edata = CopyErrorData();
+		FlushErrorState();
+
+		/* Pass the error message */
+		GUC_check_errdetail("%s", edata->message);
+		FreeErrorData(edata);
+		return false;
+	}
+	PG_END_TRY();
+
+	myextra = (XLogRecPtr *) guc_malloc(ERROR, sizeof(XLogRecPtr));
+	*myextra = lsn;
+	*extra = (void *) myextra;
+
+	return true;
+}
+
+static void
+assign_recovery_target_lsn(const char *newval, void *extra)
+{
+	recovery_target_lsn = *((XLogRecPtr *) extra);
+}
+
+static bool
+check_recovery_target_timeline(char **newval, void **extra, GucSource source)
+{
+	TimeLineID	tli = 0;
+	TimeLineID	*myextra;
+
+	if (strcmp(*newval, "") == 0 || strcmp(*newval, "latest") == 0)
+		tli = 0;
+	else
+	{
+		errno = 0;
+		tli = (TimeLineID) strtoul(*newval, NULL, 0);
+		if (errno == EINVAL || errno == ERANGE)
+		{
+			GUC_check_errdetail("recovery_target_timeline is not a valid number: \"%s\"",
+								*newval);
+			return false;
+		}
+	}
+
+	myextra = (TimeLineID *) guc_malloc(ERROR, sizeof(TimeLineID));
+	*myextra = tli;
+	*extra = (void *) myextra;
+
+	return true;
+}
+
+static void
+assign_recovery_target_timeline(const char *newval, void *extra)
+{
+	recovery_target_timeline = *((TimeLineID *) extra);
+}
+
+static bool
+check_recovery_target_action(char **newval, void **extra, GucSource source)
+{
+	RecoveryTargetAction rta = RECOVERY_TARGET_ACTION_PAUSE;
+	RecoveryTargetAction *myextra;
+
+	if (strcmp(*newval, "pause") == 0)
+		rta = RECOVERY_TARGET_ACTION_PAUSE;
+	else if (strcmp(*newval, "promote") == 0)
+		rta = RECOVERY_TARGET_ACTION_PROMOTE;
+	else if (strcmp(*newval, "shutdown") == 0)
+		rta = RECOVERY_TARGET_ACTION_SHUTDOWN;
+	else if (strcmp(*newval, "") != 0)
+	{
+		GUC_check_errdetail("recovery_target_action is not valid: \"%s\"", *newval);
+		return false;
+	}
+
+	myextra = (RecoveryTargetAction *) guc_malloc(ERROR, sizeof(RecoveryTargetAction));
+	*myextra = rta;
+	*extra = (void *) myextra;
+
+	return true;
+}
+
+static void
+assign_recovery_target_action(const char *newval, void *extra)
+{
+	recovery_target_action = *((RecoveryTargetAction *) extra);
+}
+
+static bool
+check_primary_slot_name(char **newval, void **extra, GucSource source)
+{
+	if (strcmp(*newval,"") != 0 &&
+		!ReplicationSlotValidateName(*newval, WARNING))
+	{
+		GUC_check_errdetail("primary_slot_name is not valid: \"%s\"", *newval);
+		return false;
+	}
+
+	return true;
+}
+
 #include "guc-file.c"
diff --git a/src/backend/utils/misc/postgresql.conf.sample b/src/backend/utils/misc/postgresql.conf.sample
index 7c2daa5..3002a33 100644
--- a/src/backend/utils/misc/postgresql.conf.sample
+++ b/src/backend/utils/misc/postgresql.conf.sample
@@ -222,6 +222,26 @@
 #archive_timeout = 0		# force a logfile segment switch after this
 				# number of seconds; 0 disables
 
+# - Archive Recovery -
+#restore_command = ''		# command to use to restore an archived logfile segment
+				# placeholders: %p = path of file to restore
+				#               %f = file name only
+				# e.g. 'cp /mnt/server/archivedir/%f %p'
+#archive_cleanup_command = ''	# command to execute at every restartpoint
+#recovery_end_command = ''	# command to execute at completion of recovery
+
+# - Recovery Target -
+#recovery_target_action = 'pause' 	# 'pause', 'promote', 'shutdown'
+#recovery_target=immediate		# 'xid', 'time', 'name', 'lsn',
+					# or 'immediate'
+#recovery_target_xid = ''
+#recovery_target_name = ''
+#recovery_target_time = ''
+#recovery_target_lsn = ''
+#recovery_target_timeline = ''		# timeline ID or 'latest'
+					# (change requires restart)
+#recovery_target_inclusive = on
+
 
 #------------------------------------------------------------------------------
 # REPLICATION
@@ -254,6 +274,10 @@
 
 # These settings are ignored on a master server.
 
+#standby_mode = off			# "on" starts the server as a standby
+					# (change requires restart)
+#primary_conninfo = ''			# connection string to connect to the master
+#trigger_file = ''			# trigger file to promote the standby
 #hot_standby = off			# "on" allows queries during recovery
 					# (change requires restart)
 #max_standby_archive_delay = 30s	# max delay before canceling queries
diff --git a/src/bin/pg_archivecleanup/pg_archivecleanup.c b/src/bin/pg_archivecleanup/pg_archivecleanup.c
index 319038f..abbe367 100644
--- a/src/bin/pg_archivecleanup/pg_archivecleanup.c
+++ b/src/bin/pg_archivecleanup/pg_archivecleanup.c
@@ -270,7 +270,7 @@ usage(void)
 	printf("  -x EXT         clean up files if they have this extension\n");
 	printf("  -?, --help     show this help, then exit\n");
 	printf("\n"
-		   "For use as archive_cleanup_command in recovery.conf when standby_mode = on:\n"
+		   "For use as archive_cleanup_command in postgresql.conf when standby_mode = on:\n"
 		   "  archive_cleanup_command = 'pg_archivecleanup [OPTION]... ARCHIVELOCATION %%r'\n"
 		   "e.g.\n"
 		   "  archive_cleanup_command = 'pg_archivecleanup /mnt/server/archiverdir %%r'\n");
diff --git a/src/include/access/xlog.h b/src/include/access/xlog.h
index c9f332c..5a735bd 100644
--- a/src/include/access/xlog.h
+++ b/src/include/access/xlog.h
@@ -106,6 +106,26 @@ extern bool fullPageWrites;
 extern bool wal_log_hints;
 extern bool wal_compression;
 extern bool log_checkpoints;
+extern char *restore_command;
+extern char *archive_cleanup_command;
+extern char *recovery_end_command;
+extern bool standby_mode;
+extern char *primary_conninfo;
+extern char *primary_slot_name;
+extern char *trigger_file;
+extern RecoveryTargetType recovery_target;
+extern TransactionId recovery_target_xid;
+extern TimestampTz recovery_target_time;
+extern XLogRecPtr recovery_target_lsn;
+extern char *recovery_target_name;
+extern bool recovery_target_inclusive;
+extern bool pause_at_recovery_target;
+extern char *recovery_target_timeline_string;
+extern char *recovery_target_action_string;
+extern char *recovery_target_lsn_string;
+extern char *recovery_target_string;
+extern TimeLineID recovery_target_timeline;
+extern int recovery_min_apply_delay;
 
 extern int	CheckPointSegments;
 
@@ -268,6 +288,7 @@ extern void RemovePromoteSignalFiles(void);
 extern bool CheckPromoteSignal(void);
 extern void WakeupRecovery(void);
 extern void SetWalWriterSleeping(bool sleeping);
+extern void CheckRestoreCommandSet(void);
 
 extern void XLogRequestWalReceiverReply(void);
 
diff --git a/src/include/access/xlog_internal.h b/src/include/access/xlog_internal.h
index ceb0462..ab4a377 100644
--- a/src/include/access/xlog_internal.h
+++ b/src/include/access/xlog_internal.h
@@ -255,6 +255,8 @@ typedef enum
 	RECOVERY_TARGET_ACTION_SHUTDOWN
 } RecoveryTargetAction;
 
+extern RecoveryTargetAction recovery_target_action;
+
 /*
  * Method table for resource managers.
  *
diff --git a/src/include/utils/guc_tables.h b/src/include/utils/guc_tables.h
index b695f61..7f4389c 100644
--- a/src/include/utils/guc_tables.h
+++ b/src/include/utils/guc_tables.h
@@ -68,6 +68,8 @@ enum config_group
 	WAL_SETTINGS,
 	WAL_CHECKPOINTS,
 	WAL_ARCHIVING,
+	WAL_ARCHIVE_RECOVERY,
+	WAL_RECOVERY_TARGET,
 	REPLICATION,
 	REPLICATION_SENDING,
 	REPLICATION_MASTER,
-- 
2.1.4

#23Robert Haas
robertmhaas@gmail.com
In reply to: Abhijit Menon-Sen (#22)
Re: Proposal for changes to recovery.conf API

On Mon, Oct 31, 2016 at 7:44 PM, Abhijit Menon-Sen <ams@2ndquadrant.com> wrote:

At 2016-09-28 13:13:56 -0400, robertmhaas@gmail.com wrote:

I hope that the fact that there's been no discussion for the last

three weeks doesn't mean this effort is dead; I would like very
much to see it move forward.

Here's an updated patch. Sorry, I got busy elswhere.

I struggled with the handling of recovery_target a little. For example,
one suggested alternative was:

recovery_target_type = xid
recovery_target_value = …

The problem with implementing it this way is that the _value setting
cannot be parsed without already having parsed the _type, and I didn't
want to force that sort of dependency.

What I've done instead is to make this work:

recovery_target = xid|time|name|lsn|immediate
recovery_target_xid = …
recovery_target_time = …
recovery_target_name = …
recovery_target_lsn = …

The recovery_target_xxx values are parsed as they used to be, but the
one that's used is the one that's set in recovery_target. That's easy to
explain, and the patch is much less intrusive, but I'm certainly open to
suggestions to improve this, and I have the time to work on this patch
with a view towards getting it committed in this cycle.

I liked Heikki's suggestion (at some point quite a while ago now) of
recovery_target = 'xid 123' or recovery_target='lsn 0/723' or
whatever.

--
Robert Haas
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#24Michael Paquier
michael.paquier@gmail.com
In reply to: Robert Haas (#23)
Re: Proposal for changes to recovery.conf API

On Tue, Nov 1, 2016 at 11:31 PM, Robert Haas <robertmhaas@gmail.com> wrote:

I liked Heikki's suggestion (at some point quite a while ago now) of
recovery_target = 'xid 123' or recovery_target='lsn 0/723' or
whatever.

My vote goes for having two separate parameters, because as we know
that there will be always two fields in this parameter, there is no
need to complicate the GUC machinery with a new special case when
parsing its value. Having two parameters would also make easier the
life of anybody maintaining a library parsing parameters for values
and doing in-place updates of those values. For example, I maintain a
set of routines in Python doing that with some fancy regex routines,
and that would avoid having to handle a special case when willing to
update for example the same recovery target with a new value.
--
Michael

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#25Simon Riggs
simon@2ndquadrant.com
In reply to: Michael Paquier (#24)
Re: Proposal for changes to recovery.conf API

On 4 November 2016 at 10:04, Michael Paquier <michael.paquier@gmail.com> wrote:

On Tue, Nov 1, 2016 at 11:31 PM, Robert Haas <robertmhaas@gmail.com> wrote:

I liked Heikki's suggestion (at some point quite a while ago now) of
recovery_target = 'xid 123' or recovery_target='lsn 0/723' or
whatever.

My vote goes for having two separate parameters, because as we know
that there will be always two fields in this parameter, there is no
need to complicate the GUC machinery with a new special case when
parsing its value. Having two parameters would also make easier the
life of anybody maintaining a library parsing parameters for values
and doing in-place updates of those values. For example, I maintain a
set of routines in Python doing that with some fancy regex routines,
and that would avoid having to handle a special case when willing to
update for example the same recovery target with a new value.

Parameters are required to all make sense independently, so two
parameters is off the table, IMHO.

The choice is one parameter, as Robert mentions again, or lots of
parameters (or both of those).

Since the "lots of parameters" approach is almost exactly what we have
now, I think we should do that first, get this patch committed and
then sit back and discuss an improved API and see what downsides it
introduces. Further delay on this isn't helpful for other patches
going in.

--
Simon Riggs http://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#26Robert Haas
robertmhaas@gmail.com
In reply to: Michael Paquier (#24)
Re: Proposal for changes to recovery.conf API

On Fri, Nov 4, 2016 at 6:04 AM, Michael Paquier
<michael.paquier@gmail.com> wrote:

On Tue, Nov 1, 2016 at 11:31 PM, Robert Haas <robertmhaas@gmail.com> wrote:

I liked Heikki's suggestion (at some point quite a while ago now) of
recovery_target = 'xid 123' or recovery_target='lsn 0/723' or
whatever.

My vote goes for having two separate parameters, because as we know
that there will be always two fields in this parameter, ...

That's not even true today: when the target is immediate, it has no
associated parameter value. And who knows what the future may hold?

--
Robert Haas
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#27Abhijit Menon-Sen
ams@2ndQuadrant.com
In reply to: Simon Riggs (#25)
Re: Proposal for changes to recovery.conf API

At 2016-11-04 10:35:05 +0000, simon@2ndquadrant.com wrote:

Since the "lots of parameters" approach is almost exactly what we have
now, I think we should do that first, get this patch committed and
then sit back and discuss an improved API and see what downsides it
introduces.

Thanks, I agree strongly with this.

Someone (Michael?) earlier mentioned the potential for introducing bugs
with this patch (the idea of merging recovery.conf into postgresql.conf
at all, not this particular incarnation).

I think the current proposed approach with

recovery_target=xid
recovery_target_xid=xxx

is preferable because (a) it doesn't introduce much new code, e.g., new
parsing functions, nor (b) need many changes in the documentation—all we
need is to say that of the various recovery_target_xxx parameters, the
one that has priority is the one named by recovery_target.

If I were to introduce recovery_target='xid xxx', I would at a minimum
need to factor out (or duplicate) parsing and error handling code, make
a type/value union-in-struct to store in the GUC *extra, then make sure
that we handle the older values in a backwards-compatible way, and move
a bunch of documentation around.

Can it be done? Yes, of course; and I'll do it if that's the consensus.
But it would be a backwards-compatible change to the current approach
anyway, and I think it would be better to put in the simple way now
before discussing the new API further.

Let's get the basic new *functionality* out so that people can play with
it, I'm sure we'll find a few non-API things that need tweaking as a
result of the change.

-- Abhijit

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#28Andres Freund
andres@anarazel.de
In reply to: Abhijit Menon-Sen (#22)
Re: Proposal for changes to recovery.conf API

Hi,

On 2016-11-01 05:14:58 +0530, Abhijit Menon-Sen wrote:

Subject: Convert recovery.conf settings to GUCs

I really want to get this in, we've been back and forth about this for
far too long.

<para>
-     Create a recovery command file <filename>recovery.conf</> in the cluster
-     data directory (see <xref linkend="recovery-config">). You might
+     Set up recovery parameters in <filename>postgresql.conf</> (see
+     <xref linkend="runtime-config-wal-archive-recovery"> and
+     <xref linkend="runtime-config-wal-recovery-target">).
+    </para>
+   </listitem>
+   <listitem>
+    <para>
+     Create a recovery trigger file <filename>recovery.trigger</>
+     in the cluster data directory. You might
also want to temporarily modify <filename>pg_hba.conf</> to prevent
ordinary users from connecting until you are sure the recovery was successful.
</para>

I think this means we need to rephrase a number of docs and code
references to trigger files, because they'll currently often refer to
the promotion trigger, no?

diff --git a/src/backend/access/transam/recovery.conf.sample b/src/backend/access/transam/recovery.conf.sample
index 7a16751..15ce784 100644
--- a/src/backend/access/transam/recovery.conf.sample
+++ b/src/backend/access/transam/recovery.conf.sample
@@ -2,23 +2,14 @@
# PostgreSQL recovery config file
# -------------------------------
#
-# Edit this file to provide the parameters that PostgreSQL needs to
-# perform an archive recovery of a database, or to act as a replication
-# standby.
+# PostgreSQL 10.0 or later, recovery.conf is no longer used. Instead,
+# specify all recovery parameters in postgresql.conf and create
+# recovery.trigger to enter archive recovery or standby mode.
#
-# If "recovery.conf" is present in the PostgreSQL data directory, it is
-# read on postmaster startup.  After successful recovery, it is renamed
-# to "recovery.done" to ensure that we do not accidentally re-enter
-# archive recovery or standby mode.
+# If you must use recovery.conf, specify "include directives" in
+# postgresql.conf like this:
#
-# This file consists of lines of the form:
-#
-#   name = value
-#
-# Comments are introduced with '#'.
-#
-# The complete list of option names and allowed values can be found
-# in the PostgreSQL documentation.
+#   include 'recovery.conf'

This should probably warn about the differences in "trigger" behaviour
of recovery.conf being present.

#---------------------------------------------------------------------------
# ARCHIVE RECOVERY PARAMETERS
@@ -131,14 +122,6 @@
#
#primary_slot_name = ''

I don't think it makes much sense to still list all this?

+bool standby_mode = false;

I'm very tempted to rename this during the move to GUCs.

+char *primary_conninfo = NULL;
+char *primary_slot_name = NULL;

Slightly less so, but still tempted to also rename these. They're not
actually necessarily pointing towards a primary, and namespace-wise
they're not grouped with recovery_*, which has become more important now
that recovery.conf isn't a separate namespace anymore.

-static int recovery_min_apply_delay = 0;
static TimestampTz recoveryDelayUntilTime;

+static TimestampTz recoveryDelayUntilTime;
+

Isn't this a repeated definition?

/*
* During normal operation, the only timeline we care about is ThisTimeLineID.
* During recovery, however, things are more complicated. To simplify life
@@ -577,10 +578,10 @@ typedef struct XLogCtlData
TimeLineID PrevTimeLineID;

/*
-	 * archiveCleanupCommand is read from recovery.conf but needs to be in
-	 * shared memory so that the checkpointer process can access it.
+	 * archive_cleanup_command is read from recovery.conf but needs to
+	 * be in shared memory so that the checkpointer process can access it.
*/

References recovery.conf. It'd be a good idea to grep for all remaining
references to recovery.conf.

+static bool
+check_recovery_target(char **newval, void **extra, GucSource source)
+{
+	RecoveryTargetType *myextra;
+	RecoveryTargetType rt = RECOVERY_TARGET_UNSET;
+
+	if (strcmp(*newval, "xid") == 0)
+		rt = RECOVERY_TARGET_XID;
+	else if (strcmp(*newval, "time") == 0)
+		rt = RECOVERY_TARGET_TIME;
+	else if (strcmp(*newval, "name") == 0)
+		rt = RECOVERY_TARGET_NAME;
+	else if (strcmp(*newval, "lsn") == 0)
+		rt = RECOVERY_TARGET_LSN;
+	else if (strcmp(*newval, "immediate") == 0)
+		rt = RECOVERY_TARGET_IMMEDIATE;
+	else if (strcmp(*newval, "") != 0)
+	{
+		GUC_check_errdetail("recovery_target is not valid: \"%s\"", *newval);
+		return false;
+	}
+
+	myextra = (RecoveryTargetType *) guc_malloc(ERROR, sizeof(RecoveryTargetType));
+	*myextra = rt;
+	*extra = (void *) myextra;
+
+	return true;
+}

Shouldn't this instead be an enum type GUC?

+static bool
+check_recovery_target_time(char **newval, void **extra, GucSource source)
+{
+	TimestampTz     time;
+	TimestampTz     *myextra;
+	MemoryContext oldcontext = CurrentMemoryContext;
+
+	PG_TRY();
+	{
+		time = (strcmp(*newval, "") == 0) ?
+			0 :
+			DatumGetTimestampTz(DirectFunctionCall3(timestamptz_in,
+													CStringGetDatum(*newval),
+													ObjectIdGetDatum(InvalidOid),
+													Int32GetDatum(-1)));
+	}
+	PG_CATCH();
+	{
+		ErrorData  *edata;
+
+		/* Save error info */
+		MemoryContextSwitchTo(oldcontext);
+		edata = CopyErrorData();
+		FlushErrorState();
+
+		/* Pass the error message */
+		GUC_check_errdetail("%s", edata->message);
+		FreeErrorData(edata);
+		return false;
+	}
+	PG_END_TRY();

Hm, I'm not happy to do that kind of thing. What if there's ever any
database interaction added to timestamptz_in?

It's also problematic because the parsing of timestamps depends on the
timezone GUC - which might not yet have taken effect...

+static bool
+check_recovery_target_lsn(char **newval, void **extra, GucSource source)
+{
+	XLogRecPtr		lsn;
+	XLogRecPtr	   *myextra;
+	MemoryContext oldcontext = CurrentMemoryContext;
+
+	PG_TRY();
+	{
+		lsn = (strcmp(*newval, "") == 0) ?
+			InvalidXLogRecPtr :
+			DatumGetLSN(DirectFunctionCall3(pg_lsn_in,
+											CStringGetDatum(*newval),
+											ObjectIdGetDatum(InvalidOid),
+											Int32GetDatum(-1)));
+	}
+	PG_CATCH();
+	{
+		ErrorData  *edata;
+
+		/* Save error info */
+		MemoryContextSwitchTo(oldcontext);
+		edata = CopyErrorData();
+		FlushErrorState();
+
+		/* Pass the error message */
+		GUC_check_errdetail("%s", edata->message);
+		FreeErrorData(edata);
+		return false;
+	}
+	PG_END_TRY();

That seems like a pretty pointless use of pg_lsn_in, given the problems
that PG_CATCHing an error without rethrowing brings.

+static bool
+check_recovery_target_action(char **newval, void **extra, GucSource source)
+{
+	RecoveryTargetAction rta = RECOVERY_TARGET_ACTION_PAUSE;
+	RecoveryTargetAction *myextra;
+
+	if (strcmp(*newval, "pause") == 0)
+		rta = RECOVERY_TARGET_ACTION_PAUSE;
+	else if (strcmp(*newval, "promote") == 0)
+		rta = RECOVERY_TARGET_ACTION_PROMOTE;
+	else if (strcmp(*newval, "shutdown") == 0)
+		rta = RECOVERY_TARGET_ACTION_SHUTDOWN;
+	else if (strcmp(*newval, "") != 0)
+	{
+		GUC_check_errdetail("recovery_target_action is not valid: \"%s\"", *newval);
+		return false;
+	}
+
+	myextra = (RecoveryTargetAction *) guc_malloc(ERROR, sizeof(RecoveryTargetAction));
+	*myextra = rta;
+	*extra = (void *) myextra;
+
+	return true;
+}

Should be an enum.

+static bool
+check_primary_slot_name(char **newval, void **extra, GucSource source)
+{
+	if (strcmp(*newval,"") != 0 &&
+		!ReplicationSlotValidateName(*newval, WARNING))
+	{
+		GUC_check_errdetail("primary_slot_name is not valid: \"%s\"", *newval);
+		return false;
+	}
+
+	return true;
+}

ReplicationSlotValidateName will emit WARNINGs this way - hm. That'll
often loose detail about what the problem actually is, e.g. at server
startup.

+#standby_mode = off			# "on" starts the server as a standby
+					# (change requires restart)
+#primary_conninfo = ''			# connection string to connect to the master
+#trigger_file = ''			# trigger file to promote the standby

Too general a name imo.

Greetings,

Andres Freund

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#29Robert Haas
robertmhaas@gmail.com
In reply to: Abhijit Menon-Sen (#27)
Re: Proposal for changes to recovery.conf API

On Fri, Nov 4, 2016 at 10:17 AM, Abhijit Menon-Sen <ams@2ndquadrant.com> wrote:

At 2016-11-04 10:35:05 +0000, simon@2ndquadrant.com wrote:

Since the "lots of parameters" approach is almost exactly what we have
now, I think we should do that first, get this patch committed and
then sit back and discuss an improved API and see what downsides it
introduces.

Thanks, I agree strongly with this.

Someone (Michael?) earlier mentioned the potential for introducing bugs
with this patch (the idea of merging recovery.conf into postgresql.conf
at all, not this particular incarnation).

I think the current proposed approach with

recovery_target=xid
recovery_target_xid=xxx

is preferable because (a) it doesn't introduce much new code, e.g., new
parsing functions, nor (b) need many changes in the documentation—all we
need is to say that of the various recovery_target_xxx parameters, the
one that has priority is the one named by recovery_target.

If I were to introduce recovery_target='xid xxx', I would at a minimum
need to factor out (or duplicate) parsing and error handling code, make
a type/value union-in-struct to store in the GUC *extra, then make sure
that we handle the older values in a backwards-compatible way, and move
a bunch of documentation around.

Just to be clear, the sum total of the additional "parsing" we are
talking about is looking for the first sequence of 1 or more spaces in
the input string and separating the stuff before them from the stuff
after them. I agree that there's some work there, but I'm surprised
to hear that you think it's a lot of work.

--
Robert Haas
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#30Robert Haas
robertmhaas@gmail.com
In reply to: Andres Freund (#28)
Re: Proposal for changes to recovery.conf API

On Sat, Nov 12, 2016 at 11:09 AM, Andres Freund <andres@anarazel.de> wrote:

I'm very tempted to rename this during the move to GUCs

...

Slightly less so, but still tempted to also rename these. They're not
actually necessarily pointing towards a primary, and namespace-wise
they're not grouped with recovery_*, which has become more important now
that recovery.conf isn't a separate namespace anymore.

-1 for renaming these. I don't think the current names are
particularly bad, and I think trying to agree on what would be better
could easily sink the whole patch.

--
Robert Haas
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#31Simon Riggs
simon@2ndquadrant.com
In reply to: Robert Haas (#30)
Re: Proposal for changes to recovery.conf API

On 14 November 2016 at 15:50, Robert Haas <robertmhaas@gmail.com> wrote:

On Sat, Nov 12, 2016 at 11:09 AM, Andres Freund <andres@anarazel.de> wrote:

I'm very tempted to rename this during the move to GUCs

...

Slightly less so, but still tempted to also rename these. They're not
actually necessarily pointing towards a primary, and namespace-wise
they're not grouped with recovery_*, which has become more important now
that recovery.conf isn't a separate namespace anymore.

-1 for renaming these. I don't think the current names are
particularly bad, and I think trying to agree on what would be better
could easily sink the whole patch.

OK, so we can move forward. Thanks.

I'm going to be doing final review and commit this week, at the
Developer meeting on Thurs and on Friday, with input in person and on
list.

--
Simon Riggs http://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#32Simon Riggs
simon@2ndquadrant.com
In reply to: Simon Riggs (#31)
Re: Proposal for changes to recovery.conf API

On 29 November 2016 at 15:13, Simon Riggs <simon@2ndquadrant.com> wrote:

On 14 November 2016 at 15:50, Robert Haas <robertmhaas@gmail.com> wrote:

On Sat, Nov 12, 2016 at 11:09 AM, Andres Freund <andres@anarazel.de> wrote:

I'm very tempted to rename this during the move to GUCs

...

Slightly less so, but still tempted to also rename these. They're not
actually necessarily pointing towards a primary, and namespace-wise
they're not grouped with recovery_*, which has become more important now
that recovery.conf isn't a separate namespace anymore.

-1 for renaming these. I don't think the current names are
particularly bad, and I think trying to agree on what would be better
could easily sink the whole patch.

OK, so we can move forward. Thanks.

I'm going to be doing final review and commit this week, at the
Developer meeting on Thurs and on Friday, with input in person and on
list.

err... no I'm not, based on review feedback in Tokyo.

New schedule is roughly this...
* agree changes over next week
* make changes and submit new patch by 1 Jan
* commit patch by 7 Jan

Overview of details agreed in Tokyo, now subject to further comments
from hackers

* Move recovery.conf parameters into postgresql.conf
Allow reload of most parameters, allow ALTER SYSTEM
Provide visibility of values through GUC interface

* recovery.conf is replaced by recovery.trigger -> recovery.done

* pg_basebackup -R
will write recovery.trigger to data directory
insert parameters postgresql.conf.auto, if possible

* backwards compatibility - read recovery.conf from $DATADIR -
presence of recovery.conf will cause ERROR
* backwards compatibility - some parameter names will change, so
allows others to changes also if needed

* Add docs: "Guide to changes in recovery.conf in 10.0"

* recovery_target as a single parameter, using proposed "xid 666"
"target value" format

* remove hot_standby parameter altogether, in line with earlier changes

* trigger_file renamed to promote_trigger_file

* standby_mode = on| off -> default to on

* pg_ctl recover - not as part of this patch

I'll work on the patch from here on, to allow me to work towards commit.

Comments please.

--
Simon Riggs http://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#33Robert Haas
robertmhaas@gmail.com
In reply to: Simon Riggs (#32)
Re: Proposal for changes to recovery.conf API

On Thu, Dec 1, 2016 at 7:31 PM, Simon Riggs <simon@2ndquadrant.com> wrote:

* Move recovery.conf parameters into postgresql.conf
Allow reload of most parameters, allow ALTER SYSTEM
Provide visibility of values through GUC interface

+1.

* recovery.conf is replaced by recovery.trigger -> recovery.done

+1.

* pg_basebackup -R
will write recovery.trigger to data directory
insert parameters postgresql.conf.auto, if possible

Don't understand that last line; otherwise, +1.

* backwards compatibility - read recovery.conf from $DATADIR -
presence of recovery.conf will cause ERROR

+1.

* backwards compatibility - some parameter names will change, so
allows others to changes also if needed

Don't understand this.

* Add docs: "Guide to changes in recovery.conf in 10.0"

Hmm, we don't usually write the docs in terms of how things are
different from a previous version. Might seem strange in 5 years.
Not sure what's best, here.

* recovery_target as a single parameter, using proposed "xid 666"
"target value" format

+1.

* remove hot_standby parameter altogether, in line with earlier changes

That seems a little surprising. We don't think anyone ever wants to
refuse connections during archive recovery?

* trigger_file renamed to promote_trigger_file

Why?

* standby_mode = on| off -> default to on

+1.

* pg_ctl recover - not as part of this patch

+1.

--
Robert Haas
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#34Michael Paquier
michael.paquier@gmail.com
In reply to: Robert Haas (#33)
Re: Proposal for changes to recovery.conf API

On Fri, Dec 2, 2016 at 10:10 AM, Robert Haas <robertmhaas@gmail.com> wrote:

On Thu, Dec 1, 2016 at 7:31 PM, Simon Riggs <simon@2ndquadrant.com> wrote:

* pg_basebackup -R
will write recovery.trigger to data directory
insert parameters postgresql.conf.auto, if possible

Don't understand that last line; otherwise, +1.

pg_basebackup -R creates a recovery.conf now, by appending the
parameters to postgresql.conf.auto we are sure that:
1) there is no need to check for the existence of recovery.conf as it
could be included by postgresql.conf with something like an
include_if_exists
2) postgresql.conf.auto is loaded automatically without any additional
tweaks needed in the GUC parsing code paths.

* Add docs: "Guide to changes in recovery.conf in 10.0"

Hmm, we don't usually write the docs in terms of how things are
different from a previous version. Might seem strange in 5 years.
Not sure what's best, here.

A good chunk in the release notes would make sense as well?

* recovery_target as a single parameter, using proposed "xid 666"
"target value" format

+1.

* remove hot_standby parameter altogether, in line with earlier changes

That seems a little surprising. We don't think anyone ever wants to
refuse connections during archive recovery?

I suggested that yesterday. We have talked as well about merging
standby_mode with hot_standby, but at the end most use cases I have
seen involve looking at pg_is_in_recovery() these days to determine if
a node is out of recovery of not, and this makes particularly more
sense since 9.6 where wal_level = archive <=> hot_standby. The thought
behind that is also partially that people complain that replication is
too hard to understand for people.

* trigger_file renamed to promote_trigger_file

Why?

Because this is a trigger file aimed at doing promotion, not something else.
--
Michael

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#35Robert Haas
robertmhaas@gmail.com
In reply to: Michael Paquier (#34)
Re: Proposal for changes to recovery.conf API

On Thu, Dec 1, 2016 at 8:28 PM, Michael Paquier
<michael.paquier@gmail.com> wrote:

On Fri, Dec 2, 2016 at 10:10 AM, Robert Haas <robertmhaas@gmail.com> wrote:

On Thu, Dec 1, 2016 at 7:31 PM, Simon Riggs <simon@2ndquadrant.com> wrote:

* pg_basebackup -R
will write recovery.trigger to data directory
insert parameters postgresql.conf.auto, if possible

Don't understand that last line; otherwise, +1.

pg_basebackup -R creates a recovery.conf now, by appending the
parameters to postgresql.conf.auto we are sure that:
1) there is no need to check for the existence of recovery.conf as it
could be included by postgresql.conf with something like an
include_if_exists
2) postgresql.conf.auto is loaded automatically without any additional
tweaks needed in the GUC parsing code paths.

Well, as to #1, we're making that an error IIUC. But I see the point
of #2, for sure. So sounds good to me.

* Add docs: "Guide to changes in recovery.conf in 10.0"

Hmm, we don't usually write the docs in terms of how things are
different from a previous version. Might seem strange in 5 years.
Not sure what's best, here.

A good chunk in the release notes would make sense as well?

Or instead.

* remove hot_standby parameter altogether, in line with earlier changes

That seems a little surprising. We don't think anyone ever wants to
refuse connections during archive recovery?

I suggested that yesterday. We have talked as well about merging
standby_mode with hot_standby, but at the end most use cases I have
seen involve looking at pg_is_in_recovery() these days to determine if
a node is out of recovery of not, and this makes particularly more
sense since 9.6 where wal_level = archive <=> hot_standby. The thought
behind that is also partially that people complain that replication is
too hard to understand for people.

If it were up to me, I'd keep the hot_standby parameter around but
default it to 'on'.

* trigger_file renamed to promote_trigger_file

Why?

Because this is a trigger file aimed at doing promotion, not something else.

Oh, I see. Makes sense. I was confused.

--
Robert Haas
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#36Magnus Hagander
magnus@hagander.net
In reply to: Michael Paquier (#34)
Re: Proposal for changes to recovery.conf API

On Fri, Dec 2, 2016 at 2:28 AM, Michael Paquier <michael.paquier@gmail.com>
wrote:

On Fri, Dec 2, 2016 at 10:10 AM, Robert Haas <robertmhaas@gmail.com>
wrote:

On Thu, Dec 1, 2016 at 7:31 PM, Simon Riggs <simon@2ndquadrant.com>

wrote:

* pg_basebackup -R
will write recovery.trigger to data directory
insert parameters postgresql.conf.auto, if possible

Don't understand that last line; otherwise, +1.

pg_basebackup -R creates a recovery.conf now, by appending the
parameters to postgresql.conf.auto we are sure that:
1) there is no need to check for the existence of recovery.conf as it
could be included by postgresql.conf with something like an
include_if_exists
2) postgresql.conf.auto is loaded automatically without any additional
tweaks needed in the GUC parsing code paths.

I'd also add the point that once there, you can deal with it the same way
as other parameters with say ALTER SYSTEM if you wish, so it integrates
with existing processes better than creating a separate config file and
including it. And since postgresql.conf.auto has a very well defined
format, editing it by machine (pg_basebackup that is) is fairly simple and
safe.

* Add docs: "Guide to changes in recovery.conf in 10.0"

Hmm, we don't usually write the docs in terms of how things are
different from a previous version. Might seem strange in 5 years.
Not sure what's best, here.

A good chunk in the release notes would make sense as well?

It would.

And in fairness, having such a "guide to changes" chapter in each release
probably *would* be a good idea. But it would take resources to make that.
The release notes are good, but having a more hand-holding version
explaining incompatible changes in "regular sentences" would probably be
quite useful to users.

* recovery_target as a single parameter, using proposed "xid 666"
"target value" format

+1.

* remove hot_standby parameter altogether, in line with earlier changes

That seems a little surprising. We don't think anyone ever wants to
refuse connections during archive recovery?

Sure. But pg_hba.conf does that, and listen_addresses does that. We don't
really need yet-another-parameter for it.

I suggested that yesterday. We have talked as well about merging
standby_mode with hot_standby, but at the end most use cases I have
seen involve looking at pg_is_in_recovery() these days to determine if
a node is out of recovery of not, and this makes particularly more
sense since 9.6 where wal_level = archive <=> hot_standby. The thought
behind that is also partially that people complain that replication is
too hard to understand for people.

In particular on the topic of confusion it would help. And just in general
not have mostly unnecessary parameters is a win.

//Magnus

#37Haribabu Kommi
kommi.haribabu@gmail.com
In reply to: Magnus Hagander (#36)
Re: Proposal for changes to recovery.conf API

On Fri, Dec 2, 2016 at 12:58 PM, Magnus Hagander <magnus@hagander.net>
wrote:

As there was a feedback from others related to the patch and doesn't find
any
update from author.

Closed in 2016-11 commitfest with "returned with feedback" status.
Please feel free to update the status once you submit the updated patch or
if the current status doesn't reflect the actual status of the patch.

Regards,
Hari Babu
Fujitsu Australia

#38Michael Paquier
michael.paquier@gmail.com
In reply to: Haribabu Kommi (#37)
Re: Proposal for changes to recovery.conf API

On Mon, Dec 5, 2016 at 11:34 AM, Haribabu Kommi
<kommi.haribabu@gmail.com> wrote:

As there was a feedback from others related to the patch and doesn't find
any
update from author.

Closed in 2016-11 commitfest with "returned with feedback" status.
Please feel free to update the status once you submit the updated patch or
if the current status doesn't reflect the actual status of the patch.

Having a consensus here is already a great step forward. I am sure
that a new version will be sent for the next CF.
--
Michael

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#39Josh Berkus
josh@agliodbs.com
In reply to: Simon Riggs (#1)
Re: Proposal for changes to recovery.conf API

On 12/04/2016 07:21 PM, Michael Paquier wrote:

On Mon, Dec 5, 2016 at 11:34 AM, Haribabu Kommi
<kommi.haribabu@gmail.com> wrote:

As there was a feedback from others related to the patch and doesn't find
any
update from author.

Closed in 2016-11 commitfest with "returned with feedback" status.
Please feel free to update the status once you submit the updated patch or
if the current status doesn't reflect the actual status of the patch.

Having a consensus here is already a great step forward. I am sure
that a new version will be sent for the next CF.

Please let's make sure this gets done for 10. We're planning on
breaking a lot of config things in 10, so it would be MUCH easier on
users if we do the recovery.conf changes in that version as well.

--
--
Josh Berkus
Red Hat OSAS
(any opinions are my own)

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#40Josh Berkus
josh@agliodbs.com
In reply to: Simon Riggs (#1)
Re: Proposal for changes to recovery.conf API

On 12/01/2016 05:58 PM, Magnus Hagander wrote:

* Add docs: "Guide to changes in recovery.conf in 10.0"

Hmm, we don't usually write the docs in terms of how things are
different from a previous version. Might seem strange in 5 years.
Not sure what's best, here.

A good chunk in the release notes would make sense as well?

It would.

And in fairness, having such a "guide to changes" chapter in each
release probably *would* be a good idea. But it would take resources to
make that. The release notes are good, but having a more hand-holding
version explaining incompatible changes in "regular sentences" would
probably be quite useful to users.

We will have enough major changes in 10.0 to warrant writing one of
these. Maybe not as part of the official docs, but as a set of wiki
pages or similar.

--
--
Josh Berkus
Red Hat OSAS
(any opinions are my own)

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#41Tom Lane
tgl@sss.pgh.pa.us
In reply to: Josh Berkus (#40)
Re: Proposal for changes to recovery.conf API

Josh Berkus <josh@agliodbs.com> writes:

On 12/01/2016 05:58 PM, Magnus Hagander wrote:

And in fairness, having such a "guide to changes" chapter in each
release probably *would* be a good idea. But it would take resources to
make that. The release notes are good, but having a more hand-holding
version explaining incompatible changes in "regular sentences" would
probably be quite useful to users.

We will have enough major changes in 10.0 to warrant writing one of
these. Maybe not as part of the official docs, but as a set of wiki
pages or similar.

Seems to me this is exactly the release notes' turf. If you think the
release notes aren't clear enough, step right up and help improve them.

My own take on it is that the release notes are already a massive
amount of work, and putting duplicative material in a bunch of other
places isn't going to make things better, it'll just increase the
maintenance burden.

regards, tom lane

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#42Josh Berkus
josh@agliodbs.com
In reply to: Simon Riggs (#1)
Re: Proposal for changes to recovery.conf API

On 12/08/2016 04:16 PM, Tom Lane wrote:

Josh Berkus <josh@agliodbs.com> writes:

On 12/01/2016 05:58 PM, Magnus Hagander wrote:

And in fairness, having such a "guide to changes" chapter in each
release probably *would* be a good idea. But it would take resources to
make that. The release notes are good, but having a more hand-holding
version explaining incompatible changes in "regular sentences" would
probably be quite useful to users.

We will have enough major changes in 10.0 to warrant writing one of
these. Maybe not as part of the official docs, but as a set of wiki
pages or similar.

Seems to me this is exactly the release notes' turf. If you think the
release notes aren't clear enough, step right up and help improve them.

My own take on it is that the release notes are already a massive
amount of work, and putting duplicative material in a bunch of other
places isn't going to make things better, it'll just increase the
maintenance burden.

This would mean adding literally pages of material to the release notes.
In the past, folks have been very negative on anything which would make
the release notes longer. Are you sure?

--
--
Josh Berkus
Red Hat OSAS
(any opinions are my own)

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#43Michael Paquier
michael.paquier@gmail.com
In reply to: Josh Berkus (#42)
Re: Proposal for changes to recovery.conf API

On Fri, Dec 9, 2016 at 9:34 AM, Josh Berkus <josh@agliodbs.com> wrote:

On 12/08/2016 04:16 PM, Tom Lane wrote:

Josh Berkus <josh@agliodbs.com> writes:

On 12/01/2016 05:58 PM, Magnus Hagander wrote:

And in fairness, having such a "guide to changes" chapter in each
release probably *would* be a good idea. But it would take resources to
make that. The release notes are good, but having a more hand-holding
version explaining incompatible changes in "regular sentences" would
probably be quite useful to users.

We will have enough major changes in 10.0 to warrant writing one of
these. Maybe not as part of the official docs, but as a set of wiki
pages or similar.

Seems to me this is exactly the release notes' turf. If you think the
release notes aren't clear enough, step right up and help improve them.

My own take on it is that the release notes are already a massive
amount of work, and putting duplicative material in a bunch of other
places isn't going to make things better, it'll just increase the
maintenance burden.

This would mean adding literally pages of material to the release notes.
In the past, folks have been very negative on anything which would make
the release notes longer. Are you sure?

As that's a per-version information, that seems adapted to me. There
could be as well in the release notes a link to the portion of the
docs holding this manual. Definitely this should be self-contained in
the docs, and not mention the wiki. My 2c.
--
Michael

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#44Bruce Momjian
bruce@momjian.us
In reply to: Michael Paquier (#43)
Re: Proposal for changes to recovery.conf API

On Fri, Dec 9, 2016 at 09:46:44AM +0900, Michael Paquier wrote:

My own take on it is that the release notes are already a massive
amount of work, and putting duplicative material in a bunch of other
places isn't going to make things better, it'll just increase the
maintenance burden.

This would mean adding literally pages of material to the release notes.
In the past, folks have been very negative on anything which would make
the release notes longer. Are you sure?

As that's a per-version information, that seems adapted to me. There
could be as well in the release notes a link to the portion of the
docs holding this manual. Definitely this should be self-contained in
the docs, and not mention the wiki. My 2c.

Yes, that is the usual approach.

--
Bruce Momjian <bruce@momjian.us> http://momjian.us
EnterpriseDB http://enterprisedb.com

+ As you are, so once was I.  As I am, so you will be. +
+                      Ancient Roman grave inscription +

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#45Josh Berkus
josh@agliodbs.com
In reply to: Simon Riggs (#32)
Re: Proposal for changes to recovery.conf API

On 12/14/2016 08:06 AM, Bruce Momjian wrote:

On Fri, Dec 9, 2016 at 09:46:44AM +0900, Michael Paquier wrote:

My own take on it is that the release notes are already a massive
amount of work, and putting duplicative material in a bunch of other
places isn't going to make things better, it'll just increase the
maintenance burden.

This would mean adding literally pages of material to the release notes.
In the past, folks have been very negative on anything which would make
the release notes longer. Are you sure?

As that's a per-version information, that seems adapted to me. There
could be as well in the release notes a link to the portion of the
docs holding this manual. Definitely this should be self-contained in
the docs, and not mention the wiki. My 2c.

Yes, that is the usual approach.

So where in the docs should these go, then? We don't (currently) have a
place for this kind of doc. Appendices?

--
--
Josh Berkus
Red Hat OSAS
(any opinions are my own)

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#46Bruce Momjian
bruce@momjian.us
In reply to: Josh Berkus (#45)
Re: Proposal for changes to recovery.conf API

On Wed, Dec 14, 2016 at 02:29:07PM -0800, Josh Berkus wrote:

On 12/14/2016 08:06 AM, Bruce Momjian wrote:

On Fri, Dec 9, 2016 at 09:46:44AM +0900, Michael Paquier wrote:

My own take on it is that the release notes are already a massive
amount of work, and putting duplicative material in a bunch of other
places isn't going to make things better, it'll just increase the
maintenance burden.

This would mean adding literally pages of material to the release notes.
In the past, folks have been very negative on anything which would make
the release notes longer. Are you sure?

As that's a per-version information, that seems adapted to me. There
could be as well in the release notes a link to the portion of the
docs holding this manual. Definitely this should be self-contained in
the docs, and not mention the wiki. My 2c.

Yes, that is the usual approach.

So where in the docs should these go, then? We don't (currently) have a
place for this kind of doc. Appendices?

You are saying this is more massive than any other change we have made
in the past? In general, what need to be documented?

--
Bruce Momjian <bruce@momjian.us> http://momjian.us
EnterpriseDB http://enterprisedb.com

+ As you are, so once was I.  As I am, so you will be. +
+                      Ancient Roman grave inscription +

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#47Magnus Hagander
magnus@hagander.net
In reply to: Bruce Momjian (#46)
Re: Proposal for changes to recovery.conf API

On Thu, Dec 15, 2016 at 1:11 AM, Bruce Momjian <bruce@momjian.us> wrote:

On Wed, Dec 14, 2016 at 02:29:07PM -0800, Josh Berkus wrote:

On 12/14/2016 08:06 AM, Bruce Momjian wrote:

On Fri, Dec 9, 2016 at 09:46:44AM +0900, Michael Paquier wrote:

My own take on it is that the release notes are already a massive
amount of work, and putting duplicative material in a bunch of other
places isn't going to make things better, it'll just increase the
maintenance burden.

This would mean adding literally pages of material to the release

notes.

In the past, folks have been very negative on anything which would

make

the release notes longer. Are you sure?

As that's a per-version information, that seems adapted to me. There
could be as well in the release notes a link to the portion of the
docs holding this manual. Definitely this should be self-contained in
the docs, and not mention the wiki. My 2c.

Yes, that is the usual approach.

So where in the docs should these go, then? We don't (currently) have a
place for this kind of doc. Appendices?

You are saying this is more massive than any other change we have made
in the past? In general, what need to be documented?

I don't necessarily think it's because it's more massive than any chance we
have made before. I think it's more that this is something that we probably
should've had before, and just didn't.

Right now we basically have a bulletpoint list of things that are new, with
a section about things that are incompatible. Having an actual section
with more detailed descriptions of how to handle these changes would
definitely be a win. it shouldn't *just* be for these changes of course, it
should be for any other changes that are large enough to benefit from more
than a oneliner about the fact that they've changed.

--
Magnus Hagander
Me: http://www.hagander.net/
Work: http://www.redpill-linpro.com/

#48Tom Lane
tgl@sss.pgh.pa.us
In reply to: Magnus Hagander (#47)
Re: Proposal for changes to recovery.conf API

Magnus Hagander <magnus@hagander.net> writes:

On Thu, Dec 15, 2016 at 1:11 AM, Bruce Momjian <bruce@momjian.us> wrote:

You are saying this is more massive than any other change we have made
in the past? In general, what need to be documented?

I don't necessarily think it's because it's more massive than any chance we
have made before. I think it's more that this is something that we probably
should've had before, and just didn't.

Right now we basically have a bulletpoint list of things that are new, with
a section about things that are incompatible. Having an actual section
with more detailed descriptions of how to handle these changes would
definitely be a win. it shouldn't *just* be for these changes of course, it
should be for any other changes that are large enough to benefit from more
than a oneliner about the fact that they've changed.

Yeah, it seems to me that where this is ending up is "we may need to
write more in the compatibility entries than we have in the past".
I don't see any problem with that, particularly if someone other than
Bruce or me is volunteering to write it ;-)

regards, tom lane

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#49Josh Berkus
josh@agliodbs.com
In reply to: Magnus Hagander (#36)
Re: Proposal for changes to recovery.conf API

On 12/15/2016 12:54 PM, Tom Lane wrote:

Magnus Hagander <magnus@hagander.net> writes:

On Thu, Dec 15, 2016 at 1:11 AM, Bruce Momjian <bruce@momjian.us> wrote:

You are saying this is more massive than any other change we have made
in the past? In general, what need to be documented?

I don't necessarily think it's because it's more massive than any chance we
have made before. I think it's more that this is something that we probably
should've had before, and just didn't.

Right now we basically have a bulletpoint list of things that are new, with
a section about things that are incompatible. Having an actual section
with more detailed descriptions of how to handle these changes would
definitely be a win. it shouldn't *just* be for these changes of course, it
should be for any other changes that are large enough to benefit from more
than a oneliner about the fact that they've changed.

Yeah, it seems to me that where this is ending up is "we may need to
write more in the compatibility entries than we have in the past".
I don't see any problem with that, particularly if someone other than
Bruce or me is volunteering to write it ;-)

I'm up for writing it (with help from feature owners), provided that I
don't have to spend time arguing that it's not too long, or that I
should put it somewhere different. So can we settle the "where"
question first?

--
--
Josh Berkus
Red Hat OSAS
(any opinions are my own)

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#50Bruce Momjian
bruce@momjian.us
In reply to: Josh Berkus (#49)
Re: Proposal for changes to recovery.conf API

On Thu, Dec 15, 2016 at 04:16:36PM -0800, Josh Berkus wrote:

On 12/15/2016 12:54 PM, Tom Lane wrote:

Magnus Hagander <magnus@hagander.net> writes:

On Thu, Dec 15, 2016 at 1:11 AM, Bruce Momjian <bruce@momjian.us> wrote:

You are saying this is more massive than any other change we have made
in the past? In general, what need to be documented?

I don't necessarily think it's because it's more massive than any chance we
have made before. I think it's more that this is something that we probably
should've had before, and just didn't.

Right now we basically have a bulletpoint list of things that are new, with
a section about things that are incompatible. Having an actual section
with more detailed descriptions of how to handle these changes would
definitely be a win. it shouldn't *just* be for these changes of course, it
should be for any other changes that are large enough to benefit from more
than a oneliner about the fact that they've changed.

Yeah, it seems to me that where this is ending up is "we may need to
write more in the compatibility entries than we have in the past".
I don't see any problem with that, particularly if someone other than
Bruce or me is volunteering to write it ;-)

I'm up for writing it (with help from feature owners), provided that I
don't have to spend time arguing that it's not too long, or that I
should put it somewhere different. So can we settle the "where"
question first?

I am fine with the release note, or the release notes plus a link to a
wiki, like we have done in the past with complex fixes in minor
releases:

https://wiki.postgresql.org/wiki/20110408pg_upgrade_fix

I think what we _don't_ want is the information _only_ in the wiki, nor
do we want to carry around migration instructions in our docs forever.

--
Bruce Momjian <bruce@momjian.us> http://momjian.us
EnterpriseDB http://enterprisedb.com

+ As you are, so once was I.  As I am, so you will be. +
+                      Ancient Roman grave inscription +

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#51Robert Haas
robertmhaas@gmail.com
In reply to: Bruce Momjian (#50)
Re: Proposal for changes to recovery.conf API

On Fri, Dec 16, 2016 at 5:29 PM, Bruce Momjian <bruce@momjian.us> wrote:

I am fine with the release note, or the release notes plus a link to a
wiki, like we have done in the past with complex fixes in minor
releases:

https://wiki.postgresql.org/wiki/20110408pg_upgrade_fix

I think what we _don't_ want is the information _only_ in the wiki, nor
do we want to carry around migration instructions in our docs forever.

I really don't see why we're resisting Josh's idea of putting a more
complex set of migration instructions in the documentation someplace.
Seems useful to me. Sure, we'd have to "carry" it forever, but we
could make a policy of removing migration instructions for releases
that are now EOL. And even if we didn't, it's not like extra
documentation comes with some big cost. I think a lot of users would
benefit from a substantial expansion of our documentation, not just in
this area but in many other areas as well. I bet that we could double
the size of the documentation and users would love it; the hard part
would be finding qualified people to write high-quality documentation
of all the things that aren't well-explained in the current docs.

--
Robert Haas
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#52Bruce Momjian
bruce@momjian.us
In reply to: Robert Haas (#51)
Re: Proposal for changes to recovery.conf API

On Fri, Dec 16, 2016 at 07:19:43PM -0500, Robert Haas wrote:

On Fri, Dec 16, 2016 at 5:29 PM, Bruce Momjian <bruce@momjian.us> wrote:

I am fine with the release note, or the release notes plus a link to a
wiki, like we have done in the past with complex fixes in minor
releases:

https://wiki.postgresql.org/wiki/20110408pg_upgrade_fix

I think what we _don't_ want is the information _only_ in the wiki, nor
do we want to carry around migration instructions in our docs forever.

I really don't see why we're resisting Josh's idea of putting a more
complex set of migration instructions in the documentation someplace.
Seems useful to me. Sure, we'd have to "carry" it forever, but we
could make a policy of removing migration instructions for releases
that are now EOL. And even if we didn't, it's not like extra
documentation comes with some big cost. I think a lot of users would
benefit from a substantial expansion of our documentation, not just in
this area but in many other areas as well. I bet that we could double
the size of the documentation and users would love it; the hard part
would be finding qualified people to write high-quality documentation
of all the things that aren't well-explained in the current docs.

Well, the item has a limited useful lifespan, and we could adjust the
wiki page between minor releases if we found problems/improvements. I
suppose if we create a "Migration" section in the documentation it would
be harmless enough, but it is unclear what would be in there and what
would be in the release notes. What would be the title of the
subsection for this? "Migrating recovery.conf to Postgres 10?" Maybe
make it a subsection of the release notes in the docs?

--
Bruce Momjian <bruce@momjian.us> http://momjian.us
EnterpriseDB http://enterprisedb.com

+ As you are, so once was I.  As I am, so you will be. +
+                      Ancient Roman grave inscription +

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#53Tom Lane
tgl@sss.pgh.pa.us
In reply to: Bruce Momjian (#52)
Re: Proposal for changes to recovery.conf API

Bruce Momjian <bruce@momjian.us> writes:

On Fri, Dec 16, 2016 at 07:19:43PM -0500, Robert Haas wrote:

I really don't see why we're resisting Josh's idea of putting a more
complex set of migration instructions in the documentation someplace.
Seems useful to me. Sure, we'd have to "carry" it forever, but we
could make a policy of removing migration instructions for releases
that are now EOL.

Well, the item has a limited useful lifespan, and we could adjust the
wiki page between minor releases if we found problems/improvements. I
suppose if we create a "Migration" section in the documentation it would
be harmless enough, but it is unclear what would be in there and what
would be in the release notes. What would be the title of the
subsection for this? "Migrating recovery.conf to Postgres 10?" Maybe
make it a subsection of the release notes in the docs?

I've been thinking for awhile that we need to start retiring
ancient-branch release notes from the active documentation. I got
pushback on that when I proposed it to the list (too lazy to look up
the thread right now). But it would certainly be an easier sell
to make the release notes more voluminous if we started cutting off
the long tail of ancient notes.

I'm still not seeing any value in putting this sort of info into
a documentation section that's distinct from the release notes.
We've used links to wiki pages in the past when the information
seemed to be in flux, and that's reasonable. But what's the point
of just linking to somewhere else in the same document?

regards, tom lane

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#54Robert Haas
robertmhaas@gmail.com
In reply to: Tom Lane (#53)
Re: Proposal for changes to recovery.conf API

On Fri, Dec 16, 2016 at 8:07 PM, Tom Lane <tgl@sss.pgh.pa.us> wrote:

I'm still not seeing any value in putting this sort of info into
a documentation section that's distinct from the release notes.
We've used links to wiki pages in the past when the information
seemed to be in flux, and that's reasonable. But what's the point
of just linking to somewhere else in the same document?

If the explanation is just a few sentences long, I see no reason not
to include it in the release notes. But if it's comparable in length
to a moderately-long chapter then why would we not make it its own
chapter? I think your argument boils down to "people probably don't
need very much detail about this". But I think that's the wrong line
of thinking. In my view, we ought to ship just about as much quality
documentation as people are willing to write. Saying that we're going
to reject good-quality documentation because we don't want to have too
much of it is like saying we want to reject good-quality features
because we don't want to have too many of them, or good-quality
regression tests because we don't want to have too much code coverage,
or good-quality bug fixes because we don't want to have too few bugs.
It is of course true that all of these things can be done in a bad way
or to excess: our documentation could be a terabyte! our features
could be so numerous as to befuddle even expert users! our regression
tests could run for a hundred hours on a supercomputer! we could fix
so many basically-trivial bugs that our committers all fall sobbing to
the floor with crippling repetitive strain injuries! But let's not
FUD things that are basically positive. I think it's fantastic that
Josh Berkus wants to write more documentation, and if 10 other people
show up to do similar work, I'll buy them all a beer at the next
conference we're at together. I almost wrote "I'll buy them all a
bear" but that might be over the top.

--
Robert Haas
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#55Bruce Momjian
bruce@momjian.us
In reply to: Robert Haas (#54)
Re: Proposal for changes to recovery.conf API

On Fri, Dec 16, 2016 at 08:52:25PM -0500, Robert Haas wrote:

On Fri, Dec 16, 2016 at 8:07 PM, Tom Lane <tgl@sss.pgh.pa.us> wrote:

I'm still not seeing any value in putting this sort of info into
a documentation section that's distinct from the release notes.
We've used links to wiki pages in the past when the information
seemed to be in flux, and that's reasonable. But what's the point
of just linking to somewhere else in the same document?

If the explanation is just a few sentences long, I see no reason not
to include it in the release notes. But if it's comparable in length
to a moderately-long chapter then why would we not make it its own
chapter? I think your argument boils down to "people probably don't
need very much detail about this". But I think that's the wrong line
of thinking. In my view, we ought to ship just about as much quality
documentation as people are willing to write. Saying that we're going
to reject good-quality documentation because we don't want to have too
much of it is like saying we want to reject good-quality features
because we don't want to have too many of them, or good-quality
regression tests because we don't want to have too much code coverage,

[ I stopped reading after this. ]

The point is that the documentation about the recovery.conf changes in
Postgres are only interesting to people migrating to Postgres 10, i.e.
this is not quality documentation for someone going from Postgres 10 to
Postgres 11.

--
Bruce Momjian <bruce@momjian.us> http://momjian.us
EnterpriseDB http://enterprisedb.com

+ As you are, so once was I.  As I am, so you will be. +
+                      Ancient Roman grave inscription +

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#56Magnus Hagander
magnus@hagander.net
In reply to: Bruce Momjian (#55)
Re: Proposal for changes to recovery.conf API

On Sat, Dec 17, 2016 at 5:42 AM, Bruce Momjian <bruce@momjian.us> wrote:

On Fri, Dec 16, 2016 at 08:52:25PM -0500, Robert Haas wrote:

On Fri, Dec 16, 2016 at 8:07 PM, Tom Lane <tgl@sss.pgh.pa.us> wrote:

I'm still not seeing any value in putting this sort of info into
a documentation section that's distinct from the release notes.
We've used links to wiki pages in the past when the information
seemed to be in flux, and that's reasonable. But what's the point
of just linking to somewhere else in the same document?

If the explanation is just a few sentences long, I see no reason not
to include it in the release notes. But if it's comparable in length
to a moderately-long chapter then why would we not make it its own
chapter? I think your argument boils down to "people probably don't
need very much detail about this". But I think that's the wrong line
of thinking. In my view, we ought to ship just about as much quality
documentation as people are willing to write. Saying that we're going
to reject good-quality documentation because we don't want to have too
much of it is like saying we want to reject good-quality features
because we don't want to have too many of them, or good-quality
regression tests because we don't want to have too much code coverage,

[ I stopped reading after this. ]

The point is that the documentation about the recovery.conf changes in
Postgres are only interesting to people migrating to Postgres 10, i.e.
this is not quality documentation for someone going from Postgres 10 to
Postgres 11.

They will also be interesting to people going from 9.4 to 11, or from 9.3
to 12. Or from 9.5 to 13.

They only become uninteresting when we stop supporting 9.6 which is the
last version that didn't have that functionality.

--
Magnus Hagander
Me: http://www.hagander.net/
Work: http://www.redpill-linpro.com/

#57Robert Haas
robertmhaas@gmail.com
In reply to: Magnus Hagander (#56)
Re: Proposal for changes to recovery.conf API

On Sat, Dec 17, 2016 at 8:52 AM, Magnus Hagander <magnus@hagander.net> wrote:

On Sat, Dec 17, 2016 at 5:42 AM, Bruce Momjian <bruce@momjian.us> wrote:

On Fri, Dec 16, 2016 at 08:52:25PM -0500, Robert Haas wrote:

On Fri, Dec 16, 2016 at 8:07 PM, Tom Lane <tgl@sss.pgh.pa.us> wrote:

I'm still not seeing any value in putting this sort of info into
a documentation section that's distinct from the release notes.
We've used links to wiki pages in the past when the information
seemed to be in flux, and that's reasonable. But what's the point
of just linking to somewhere else in the same document?

If the explanation is just a few sentences long, I see no reason not
to include it in the release notes. But if it's comparable in length
to a moderately-long chapter then why would we not make it its own
chapter? I think your argument boils down to "people probably don't
need very much detail about this". But I think that's the wrong line
of thinking. In my view, we ought to ship just about as much quality
documentation as people are willing to write. Saying that we're going
to reject good-quality documentation because we don't want to have too
much of it is like saying we want to reject good-quality features
because we don't want to have too many of them, or good-quality
regression tests because we don't want to have too much code coverage,

[ I stopped reading after this. ]

The point is that the documentation about the recovery.conf changes in
Postgres are only interesting to people migrating to Postgres 10, i.e.
this is not quality documentation for someone going from Postgres 10 to
Postgres 11.

They will also be interesting to people going from 9.4 to 11, or from 9.3 to
12. Or from 9.5 to 13.

They only become uninteresting when we stop supporting 9.6 which is the last
version that didn't have that functionality.

Right, exactly. Also, even if it were true that they were only
interesting to people migrating to version 10, that doesn't mean we
shouldn't have them.

--
Robert Haas
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#58Bruce Momjian
bruce@momjian.us
In reply to: Magnus Hagander (#56)
Re: Proposal for changes to recovery.conf API

On Sat, Dec 17, 2016 at 02:52:41PM +0100, Magnus Hagander wrote:

The point is that the documentation about the recovery.conf changes in
Postgres are only interesting to people migrating to Postgres 10, i.e.
this is not quality documentation for someone going from Postgres 10 to
Postgres 11.

They will also be interesting to people going from 9.4 to 11, or from 9.3 to
12. Or from 9.5 to 13.

They only become uninteresting when we stop supporting 9.6 which is the last
version that didn't have that functionality.�

No, they become uninteresting to anyone who has passed Postgres 10. I
would argue they are still required to be around even after we stop
supporting Postgres 9.6 because we know everyone will not upgrade off of
supported releases once we stop supporting them. This means we can
effectively never remove the information.

Right now if you migrate from Postgres 8.0 to Postgres 9.6, all the
information you need is in the 9.6 documentation. If you were to remove
migration details from 8.4 to 9.0 they would have to look at the 9.0
docs to get a full picture of how to migrate.

Again, I am fine putting this as a subsection of the release notes, but
let's not pretend it is some extra section we can remove in five years.

--
Bruce Momjian <bruce@momjian.us> http://momjian.us
EnterpriseDB http://enterprisedb.com

+ As you are, so once was I.  As I am, so you will be. +
+                      Ancient Roman grave inscription +

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#59Magnus Hagander
magnus@hagander.net
In reply to: Bruce Momjian (#58)
Re: Proposal for changes to recovery.conf API

On Sat, Dec 17, 2016 at 10:34 PM, Bruce Momjian <bruce@momjian.us> wrote:

On Sat, Dec 17, 2016 at 02:52:41PM +0100, Magnus Hagander wrote:

The point is that the documentation about the recovery.conf changes

in

Postgres are only interesting to people migrating to Postgres 10,

i.e.

this is not quality documentation for someone going from Postgres 10

to

Postgres 11.

They will also be interesting to people going from 9.4 to 11, or from

9.3 to

12. Or from 9.5 to 13.

They only become uninteresting when we stop supporting 9.6 which is the

last

version that didn't have that functionality.

No, they become uninteresting to anyone who has passed Postgres 10. I
would argue they are still required to be around even after we stop
supporting Postgres 9.6 because we know everyone will not upgrade off of
supported releases once we stop supporting them. This means we can
effectively never remove the information.

This is true, but I think it's also safe to say that it's acceptable that
if you are upgrading from an unsupported version you need to read more than
one set of documentation -- one set to get to a supported one, and one get
on from there.

Right now if you migrate from Postgres 8.0 to Postgres 9.6, all the
information you need is in the 9.6 documentation. If you were to remove
migration details from 8.4 to 9.0 they would have to look at the 9.0
docs to get a full picture of how to migrate.

In fairness, all the information you need is definitely not in the
documentation. You have all the release notes, that is true. But for a lot
of people and in the case of a lot of features, that is not at all enough
information. But it's true in the sense that it's just as much information
as you would've had if you'd done the incremental steps of upgrading,
because we didn't purge anything.

Again, I am fine putting this as a subsection of the release notes, but
let's not pretend it is some extra section we can remove in five years.

Depends on what we decide to do about it, but sure, it could certainly turn
into another section that we keep around (whether as part of the release
notes, or as a separate "upgrade steps" section or something).

//Magnus

#60Bruce Momjian
bruce@momjian.us
In reply to: Magnus Hagander (#59)
Re: Proposal for changes to recovery.conf API

On Sun, Dec 18, 2016 at 12:41:01PM +0100, Magnus Hagander wrote:

No, they become uninteresting to anyone who has passed Postgres 10.� I
would argue they are still required to be around even after we stop
supporting Postgres 9.6 because we know everyone will not upgrade off of
supported releases once we stop supporting them.� This means we can
effectively never remove the information.

This is true, but I think it's also safe to say that it's acceptable that if
you are upgrading from an unsupported version you need to read more than one
set of documentation -- one set to get to a supported one, and one get on from
there.

How do you propose they find that other documentation set if upgrading
from 9.6 to version 16? Do we put the old docs somewhere on our web
site? Do we have them download a tarball and unpack it? How do we
handle old translated doc versions? I realize the wiki isn't
translated, but if we have translators translate it, we should keep it
available.

Right now if you migrate from Postgres 8.0 to Postgres 9.6, all the
information you need is in the 9.6 documentation.� If you were to remove
migration details from 8.4 to 9.0 they would have to look at the 9.0
docs to get a full picture of how to migrate.

In fairness, all the information you need is definitely not in the
documentation. You have all the release notes, that is true. But for a lot of
people and in the case of a lot of features, that is not at all enough
information. But it's true in the sense that it's just as much information as

Uh, what exactly is this additional information you are talking about?
Blogs? Are you saying we have information about upgrades we have
captured but discard?

Again, I am fine putting this as a subsection of the release notes, but
let's not pretend it is some extra section we can remove in five years.

Depends on what we decide to do about it, but sure, it could certainly turn
into another section that we keep around (whether as part of the release notes,
or as a separate "upgrade steps" section or something).

I suggest whatever we do, we place the information in a permanent
location that isn't moved or removed.

--
Bruce Momjian <bruce@momjian.us> http://momjian.us
EnterpriseDB http://enterprisedb.com

+ As you are, so once was I.  As I am, so you will be. +
+                      Ancient Roman grave inscription +

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#61Magnus Hagander
magnus@hagander.net
In reply to: Bruce Momjian (#60)
Re: Proposal for changes to recovery.conf API

On Sun, Dec 18, 2016 at 1:57 PM, Bruce Momjian <bruce@momjian.us> wrote:

On Sun, Dec 18, 2016 at 12:41:01PM +0100, Magnus Hagander wrote:

No, they become uninteresting to anyone who has passed Postgres 10.

I

would argue they are still required to be around even after we stop
supporting Postgres 9.6 because we know everyone will not upgrade

off of

supported releases once we stop supporting them. This means we can
effectively never remove the information.

This is true, but I think it's also safe to say that it's acceptable

that if

you are upgrading from an unsupported version you need to read more than

one

set of documentation -- one set to get to a supported one, and one get

on from

there.

How do you propose they find that other documentation set if upgrading
from 9.6 to version 16? Do we put the old docs somewhere on our web
site? Do we have them download a tarball and unpack it? How do we
handle old translated doc versions? I realize the wiki isn't
translated, but if we have translators translate it, we should keep it
available.

We have all the old documentation up on the website, yes. So far it goes
back to 6.3. There is no reason to stop that.

The wiki is not a good location for documentation that needs to be around
for a long time.

For translated docs, that's really up to each translation team, since we
don't have any central repository for it. We do have that for the
translation of the code, but not docs.

Right now if you migrate from Postgres 8.0 to Postgres 9.6, all the
information you need is in the 9.6 documentation. If you were to

remove

migration details from 8.4 to 9.0 they would have to look at the 9.0
docs to get a full picture of how to migrate.

In fairness, all the information you need is definitely not in the
documentation. You have all the release notes, that is true. But for a

lot of

people and in the case of a lot of features, that is not at all enough
information. But it's true in the sense that it's just as much

information as

Uh, what exactly is this additional information you are talking about?
Blogs? Are you saying we have information about upgrades we have
captured but discard?

Exactly the type of information that Simon is suggested that he writes this
time.

The *how* to migrate.

And no, we don't have this information "officially" today. Some of it is in
blogs, most of it is in the mailinglist archives. The proposal here is,
AIUI, to formalize that so we have it in the formal documentation in the
future.

Again, I am fine putting this as a subsection of the release notes,

but

let's not pretend it is some extra section we can remove in five

years.

Depends on what we decide to do about it, but sure, it could certainly

turn

into another section that we keep around (whether as part of the release

notes,

or as a separate "upgrade steps" section or something).

I suggest whatever we do, we place the information in a permanent
location that isn't moved or removed.

+1. Absolutely. That's a very important point.

--
Magnus Hagander
Me: http://www.hagander.net/
Work: http://www.redpill-linpro.com/

#62Bruce Momjian
bruce@momjian.us
In reply to: Magnus Hagander (#61)
Re: Proposal for changes to recovery.conf API

On Sun, Dec 18, 2016 at 02:02:58PM +0100, Magnus Hagander wrote:

� � �Again, I am fine putting this as a subsection of the release notes,

but

� � �let's not pretend it is some extra section we can remove in five

years.

Depends on what we decide to do about it, but sure, it could certainly

turn

into another section that we keep around (whether as part of the release

notes,

or as a separate "upgrade steps" section or something).

I suggest whatever we do, we place the information in a permanent
location that isn't moved or removed.

+1. Absolutely. That's a very important point.

That is really my only point --- wherever you put it, expect it to live
there forever.

--
Bruce Momjian <bruce@momjian.us> http://momjian.us
EnterpriseDB http://enterprisedb.com

+ As you are, so once was I.  As I am, so you will be. +
+                      Ancient Roman grave inscription +

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#63Magnus Hagander
magnus@hagander.net
In reply to: Bruce Momjian (#62)
Re: Proposal for changes to recovery.conf API

On Sun, Dec 18, 2016 at 2:11 PM, Bruce Momjian <bruce@momjian.us> wrote:

On Sun, Dec 18, 2016 at 02:02:58PM +0100, Magnus Hagander wrote:

Again, I am fine putting this as a subsection of the release

notes,

but

let's not pretend it is some extra section we can remove in

five

years.

Depends on what we decide to do about it, but sure, it could

certainly

turn

into another section that we keep around (whether as part of the

release

notes,

or as a separate "upgrade steps" section or something).

I suggest whatever we do, we place the information in a permanent
location that isn't moved or removed.

+1. Absolutely. That's a very important point.

That is really my only point --- wherever you put it, expect it to live
there forever.

Then in the end, it turns out we're in strong agreement :)

--
Magnus Hagander
Me: http://www.hagander.net/
Work: http://www.redpill-linpro.com/

#64Bruce Momjian
bruce@momjian.us
In reply to: Magnus Hagander (#63)
Re: Proposal for changes to recovery.conf API

On Sun, Dec 18, 2016 at 02:14:06PM +0100, Magnus Hagander wrote:

� � �turn
� � �> into another section that we keep around (whether as part of the

release

� � �notes,
� � �> or as a separate "upgrade steps" section or something).

� � �I suggest whatever we do, we place the information in a permanent
� � �location that isn't moved or removed.

+1. Absolutely. That's a very important point.

That is really my only point --- wherever you put it, expect it to live
there forever.

Then in the end, it turns out we're in strong agreement :)�

Yes, but there was talk of adding it to the docs, then removing it in
later releases --- that's what I think is a bad idea. I think it would
be logical to have a sub-document underneath each major release note
section with more detailed instructions. This could be done for minor
releases as well where necessary.

--
Bruce Momjian <bruce@momjian.us> http://momjian.us
EnterpriseDB http://enterprisedb.com

+ As you are, so once was I.  As I am, so you will be. +
+                      Ancient Roman grave inscription +

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#65Peter Eisentraut
peter.eisentraut@2ndquadrant.com
In reply to: Robert Haas (#54)
Re: Proposal for changes to recovery.conf API

On 12/16/16 8:52 PM, Robert Haas wrote:

If the explanation is just a few sentences long, I see no reason not
to include it in the release notes.

As far as I can tell from the latest posted patch, the upgrade
instructions are

- To trigger recovery, create an empty file recovery.trigger instead of
recovery.conf.

- All parameters formerly in recovery.conf are now regular
postgresql.conf parameters. For backward compatibility, recovery.conf
is read after postgresql.conf, but the parameters can now be put into
postgresql.conf if desired.

Some of that might be subject to patch review, but it's probably not
going to be much longer than that. So I think that will fit well into
the usual release notes section.

--
Peter Eisentraut http://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#66Simon Riggs
simon@2ndquadrant.com
In reply to: Peter Eisentraut (#65)
Re: Proposal for changes to recovery.conf API

On 19 December 2016 at 21:29, Peter Eisentraut
<peter.eisentraut@2ndquadrant.com> wrote:

On 12/16/16 8:52 PM, Robert Haas wrote:

If the explanation is just a few sentences long, I see no reason not
to include it in the release notes.

As far as I can tell from the latest posted patch, the upgrade
instructions are

- To trigger recovery, create an empty file recovery.trigger instead of
recovery.conf.

- All parameters formerly in recovery.conf are now regular
postgresql.conf parameters. For backward compatibility, recovery.conf
is read after postgresql.conf, but the parameters can now be put into
postgresql.conf if desired.

Some of that might be subject to patch review, but it's probably not
going to be much longer than that. So I think that will fit well into
the usual release notes section.

Although that's right, there is more detail.

The current list is this... based upon discussion in Tokyo dev meeting

* Any use of the earlier recovery.conf or any of the old recovery
parameter names causes an ERROR, so we have a clear API break

* To trigger recovery, create an empty recovery.trigger file. Same as
recovery.conf with standby_mode = off

* To trigger standby, create an empty standby.trigger file. Same as
recovery.conf with standby_mode = on

* Trigger files can be placed in a writable directory outside of the
data directory, trigger_directory
(I would also like to rename the concept of "trigger file" to another
word/phrase not already used for something else, c.f. table triggers)

* recovery.conf parameters are all moved to postgresql.conf, with these changes
a) standby_mode no longer exists
b) trigger_file is replaced by promote_trigger_file
equivalent parameters will be added for c) recovery_trigger_file and
d) standby_trigger_file
e) recovery_target is a new parameter with the two part content
mentioned upthread, e.g. 'xid 1234'
f) recovery_target and all trigger_file parameters are now reloadable,
not server start
g) recovery parameters are shown in the postgresql.conf.sample, but
commented out
h) recovery.conf.sample is removed
which leaves many of the parameters with same name and meaning as
before, but enough change to be a clear API break that needs people to
understand the changes

* pg_basebackup -R will generate a parameter file written to data
directory that will allow it to work, even when postgresql.conf is in
a different directory
(this bit was the most awkward part of the list of changes)

* hot_standby parameter would be removed

I think I can work this into the docs without anything worth further
discussion, but let me write that first.

The release notes can warn about the old API generating an ERROR, with
a multiple links to discussion of how it now works.

Merry Christmas everybody, new patch in time for New Year, further
discussion welcome

--
Simon Riggs http://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#67Fujii Masao
masao.fujii@gmail.com
In reply to: Simon Riggs (#66)
Re: Proposal for changes to recovery.conf API

On Tue, Dec 20, 2016 at 7:11 AM, Simon Riggs <simon@2ndquadrant.com> wrote:

On 19 December 2016 at 21:29, Peter Eisentraut
<peter.eisentraut@2ndquadrant.com> wrote:

On 12/16/16 8:52 PM, Robert Haas wrote:

If the explanation is just a few sentences long, I see no reason not
to include it in the release notes.

As far as I can tell from the latest posted patch, the upgrade
instructions are

- To trigger recovery, create an empty file recovery.trigger instead of
recovery.conf.

- All parameters formerly in recovery.conf are now regular
postgresql.conf parameters. For backward compatibility, recovery.conf
is read after postgresql.conf, but the parameters can now be put into
postgresql.conf if desired.

Some of that might be subject to patch review, but it's probably not
going to be much longer than that. So I think that will fit well into
the usual release notes section.

Although that's right, there is more detail.

The current list is this... based upon discussion in Tokyo dev meeting

* Any use of the earlier recovery.conf or any of the old recovery
parameter names causes an ERROR, so we have a clear API break

* To trigger recovery, create an empty recovery.trigger file. Same as
recovery.conf with standby_mode = off

* To trigger standby, create an empty standby.trigger file. Same as
recovery.conf with standby_mode = on

API for crash recovery will never be changed. That is, crash recovery needs
neither recovery.trigger nor standby.trigger. When the server starts a crash
recovery without any trigger file, any recovery parameter settings in
postgresql.conf are ignored. Right?

Regards,

--
Fujii Masao

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#68Simon Riggs
simon@2ndquadrant.com
In reply to: Fujii Masao (#67)
Re: Proposal for changes to recovery.conf API

On 20 December 2016 at 15:03, Fujii Masao <masao.fujii@gmail.com> wrote:

API for crash recovery will never be changed. That is, crash recovery needs
neither recovery.trigger nor standby.trigger. When the server starts a crash
recovery without any trigger file, any recovery parameter settings in
postgresql.conf are ignored. Right?

Yes. There are no conceptual changes, just the API.

The goals are: visibility and reloading of recovery parameters,
removal of special case code.

--
Simon Riggs http://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#69Josh Berkus
josh@agliodbs.com
In reply to: Bruce Momjian (#44)
Re: Migration Docs WAS: Proposal for changes to recovery.conf API

On 12/19/2016 01:29 PM, Peter Eisentraut wrote:

On 12/16/16 8:52 PM, Robert Haas wrote:

If the explanation is just a few sentences long, I see no reason not
to include it in the release notes.

As far as I can tell from the latest posted patch, the upgrade
instructions are

- To trigger recovery, create an empty file recovery.trigger instead of
recovery.conf.

- All parameters formerly in recovery.conf are now regular
postgresql.conf parameters. For backward compatibility, recovery.conf
is read after postgresql.conf, but the parameters can now be put into
postgresql.conf if desired.

Aren't we changing how some of the parameters work?

Some of that might be subject to patch review, but it's probably not
going to be much longer than that. So I think that will fit well into
the usual release notes section.

Changed the subject line of this thread because people are becoming
confused about the new topic.

I'm not talking about *just* the recovery.conf changes. We're making a
lot of changes to 10 which will require user action, and there may be
more before 10 is baked. For example, dealing with the version
numbering change. I started a list of the things we're breaking for 10,
but I don't have it with me at the moment. There's more than 3 things
on it.

And then there's docs for stuff which isn't *required* by upgrading, but
would be a good idea. For example, we'll eventually want a doc on how
to migrate old-style partitioned tables to new-style partitioned tables.

In any case, Peter's response shows *exactly* why I don't want to put
this documentation into the release notes: because people are going to
complain it's too long and want to truncate it. Writing the docs will be
hard enough; if I (or anyone else) has to argue about whether or not
they're too long, I'm just going to drop the patch and walk away.

--
--
Josh Berkus
Red Hat OSAS
(any opinions are my own)

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#70Simon Riggs
simon@2ndquadrant.com
In reply to: Simon Riggs (#68)
2 attachment(s)
Re: Proposal for changes to recovery.conf API

On 20 December 2016 at 15:11, Simon Riggs <simon@2ndquadrant.com> wrote:

On 20 December 2016 at 15:03, Fujii Masao <masao.fujii@gmail.com> wrote:

API for crash recovery will never be changed. That is, crash recovery needs
neither recovery.trigger nor standby.trigger. When the server starts a crash
recovery without any trigger file, any recovery parameter settings in
postgresql.conf are ignored. Right?

Yes. There are no conceptual changes, just the API.

The goals are: visibility and reloading of recovery parameters,
removal of special case code.

OK, so here's the patch, plus doc cleanup patch.

Trying to fit recovery targets into one parameter was really not
feasible, though I tried. Michael's suggested approach of using
recovery_target_type and recovery_target_value has been more
practical, bearing in mind the 3 other relevant parameters.

1. Any use of the earlier recovery.conf or any of the old recovery
parameter names causes an ERROR, so we have a clear API break

2. Signal files can be placed in a writable directory outside of the
data directory, signal_file_directory

3. To signal recovery, create an empty recovery.signal file. Same as
recovery.conf with standby_mode = off

4. To signal standby, create an empty standby.signal file. Same as
recovery.conf with standby_mode = on

5. recovery.conf parameters are all moved to postgresql.conf, with these changes
a) standby_mode no longer exists
b) trigger_file no longer exists… create a file in signal_directory
c)
recovery_target_type
recovery_target_value
are two new parameters that specify the most important type and value of
targeted recovery, though there are other parameters that affect the
outcome also.

e.g.
recovery_target_type = ‘xid’
recovery_target_value = ‘1234’

d) recovery target are now reloadable, not server start only

e) recovery parameters are shown in the postgresql.conf.sample, but
commented out

f) primary_conninfo and primary_slotinfo are renamed to
sender_conninfo and sender_slotinfo to more accurately reflect that these
parameters are no longer primary/master only

g) Recovery Config chapter in docs is retained (for now, at least) and
will later add a link from the main docs to explain this.

I've left the following points for later discussion/cleanup

* recovery.conf.sample will be removed

* pg_basebackup -R will generate a parameter file written to data
directory that will allow it to work, even when postgresql.conf is in
a different directory
(this bit was the most awkward part of the list of changes)

* hot_standby parameter would be removed

Tests have been modified also, but regrettably at this point only 5
out of 8 regression tests pass. This is most likely because I've had
to make more complex changes around timeline handling. I'll post again
tomorrow when I fix them.

Next steps I plan for this release

* Relocate some more code out of xlog.c and guc.c

* Full rewrite of main text docs

* pg_ctl demote
Allows us to avoid incrementing the timeline when performing a switchover

--
Simon Riggs http://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services

Attachments:

newRecoveryAPI_doc1.v101.patchapplication/octet-stream; name=newRecoveryAPI_doc1.v101.patchDownload
diff --git a/contrib/pg_standby/pg_standby.c b/contrib/pg_standby/pg_standby.c
index e4136f9..8fcb85c 100644
--- a/contrib/pg_standby/pg_standby.c
+++ b/contrib/pg_standby/pg_standby.c
@@ -520,7 +520,7 @@ usage(void)
 	printf("  -w MAXWAITTIME     max seconds to wait for a file (0=no limit) (default=0)\n");
 	printf("  -?, --help         show this help, then exit\n");
 	printf("\n"
-		   "Main intended use as restore_command in recovery.conf:\n"
+		   "Main intended use as restore_command in postgresql.conf:\n"
 		   "  restore_command = 'pg_standby [OPTION]... ARCHIVELOCATION %%f %%p %%r'\n"
 		   "e.g.\n"
 	"  restore_command = 'pg_standby /mnt/server/archiverdir %%f %%p %%r'\n");
diff --git a/doc/src/sgml/backup.sgml b/doc/src/sgml/backup.sgml
index 6eaed1e..2df0dc6 100644
--- a/doc/src/sgml/backup.sgml
+++ b/doc/src/sgml/backup.sgml
@@ -1190,8 +1190,15 @@ SELECT pg_stop_backup();
    </listitem>
    <listitem>
     <para>
-     Create a recovery command file <filename>recovery.conf</> in the cluster
-     data directory (see <xref linkend="recovery-config">). You might
+     Set up recovery parameters in <filename>postgresql.conf</> (see
+     <xref linkend="runtime-config-wal-archive-recovery"> and
+     <xref linkend="runtime-config-wal-recovery-target">).
+    </para>
+   </listitem>
+   <listitem>
+    <para>
+     Create a recovery trigger file <filename>recovery.trigger</>
+     in the cluster data directory. You might
      also want to temporarily modify <filename>pg_hba.conf</> to prevent
      ordinary users from connecting until you are sure the recovery was successful.
     </para>
@@ -1203,7 +1210,7 @@ SELECT pg_stop_backup();
      recovery be terminated because of an external error, the server can
      simply be restarted and it will continue recovery.  Upon completion
      of the recovery process, the server will rename
-     <filename>recovery.conf</> to <filename>recovery.done</> (to prevent
+     <filename>recovery.trigger</> to <filename>recovery.done</> (to prevent
      accidentally re-entering recovery mode later) and then
      commence normal database operations.
     </para>
@@ -1219,12 +1226,11 @@ SELECT pg_stop_backup();
    </para>
 
    <para>
-    The key part of all this is to set up a recovery configuration file that
-    describes how you want to recover and how far the recovery should
-    run.  You can use <filename>recovery.conf.sample</> (normally
-    located in the installation's <filename>share/</> directory) as a
-    prototype.  The one thing that you absolutely must specify in
-    <filename>recovery.conf</> is the <varname>restore_command</>,
+    The key part of all this is to set up recovery parameters that
+    specify how you want to recover and how far the recovery should
+    run. The one thing that you absolutely must specify in
+    <filename>postgresql.conf</> to recover from the backup is
+    the <varname>restore_command</>,
     which tells <productname>PostgreSQL</> how to retrieve archived
     WAL file segments.  Like the <varname>archive_command</>, this is
     a shell command string.  It can contain <literal>%f</>, which is
@@ -1286,7 +1292,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 <link linkend="recovery-target-settings">stopping point</link> in <filename>recovery.conf</>.  You can specify
+    required <link linkend="recovery-target-settings">stopping point</link> in <filename>postgresql.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
@@ -1383,8 +1389,9 @@ restore_command = 'cp /mnt/server/archivedir/%f %p'
     The default behavior of recovery is to recover along the same timeline
     that was current when the base backup was taken.  If you wish to recover
     into some child timeline (that is, you want to return to some state that
-    was itself generated after a recovery attempt), you need to specify the
-    target timeline ID in <filename>recovery.conf</>.  You cannot recover into
+    was itself generated after a recovery attempt), you need to set
+    <xref linkend="guc-recovery-target-timeline"> to the
+    target timeline ID in <filename>postgresql.conf</>.  You cannot recover into
     timelines that branched off earlier than the base backup.
    </para>
   </sect2>
diff --git a/doc/src/sgml/func.sgml b/doc/src/sgml/func.sgml
index 10e3186..d3abac9 100644
--- a/doc/src/sgml/func.sgml
+++ b/doc/src/sgml/func.sgml
@@ -18131,7 +18131,7 @@ postgres=# select pg_start_backup('label_goes_here');
     <function>pg_create_restore_point</> creates a named transaction log
     record that can be used as recovery target, and returns the corresponding
     transaction log location.  The given name can then be used with
-    <xref linkend="recovery-target-name"> to specify the point up to which
+    <xref linkend="guc-recovery-target-name"> to specify the point up to which
     recovery will proceed.  Avoid creating multiple restore points with the
     same name, since recovery will stop at the first one whose name matches
     the recovery target.
diff --git a/doc/src/sgml/high-availability.sgml b/doc/src/sgml/high-availability.sgml
index a1a9532..011f4db 100644
--- a/doc/src/sgml/high-availability.sgml
+++ b/doc/src/sgml/high-availability.sgml
@@ -591,7 +591,7 @@ protocol to make nodes agree on a serializable transactional order.
    <para>
     In standby mode, the server continuously applies WAL received from the
     master server. The standby server can read WAL from a WAL archive
-    (see <xref linkend="restore-command">) or directly from the master
+    (see <xref linkend="guc-restore-command">) or directly from the master
     over a TCP connection (streaming replication). The standby server will
     also attempt to restore any WAL found in the standby cluster's
     <filename>pg_wal</> directory. That typically happens after a server
@@ -660,8 +660,8 @@ protocol to make nodes agree on a serializable transactional order.
    <para>
     To set up the standby server, restore the base backup taken from primary
     server (see <xref linkend="backup-pitr-recovery">). Create a recovery
-    command file <filename>recovery.conf</> in the standby's cluster data
-    directory, and turn on <varname>standby_mode</>. Set
+    signal file <filename>recovery.trigger</> in the standby's cluster data
+    directory. Turn on <varname>standby_mode</> and set
     <varname>restore_command</> to a simple command to copy files from
     the WAL archive. If you plan to have multiple standby servers for high
     availability purposes, set <varname>recovery_target_timeline</> to
@@ -697,7 +697,7 @@ protocol to make nodes agree on a serializable transactional order.
 
    <para>
     If you're using a WAL archive, its size can be minimized using the <xref
-    linkend="archive-cleanup-command"> parameter to remove files that are no
+    linkend="guc-archive-cleanup-command"> parameter to remove files that are no
     longer required by the standby server.
     The <application>pg_archivecleanup</> utility is designed specifically to
     be used with <varname>archive_cleanup_command</> in typical single-standby
@@ -708,7 +708,7 @@ protocol to make nodes agree on a serializable transactional order.
    </para>
 
    <para>
-    A simple example of a <filename>recovery.conf</> is:
+    A simple example of standby settings is:
 <programlisting>
 standby_mode = 'on'
 primary_conninfo = 'host=192.168.1.50 port=5432 user=foo password=foopass'
@@ -766,8 +766,8 @@ archive_cleanup_command = 'pg_archivecleanup /path/to/archive %r'
     To use streaming replication, set up a file-based log-shipping standby
     server as described in <xref linkend="warm-standby">. The step that
     turns a file-based log-shipping standby into streaming replication
-    standby is setting <varname>primary_conninfo</> setting in the
-    <filename>recovery.conf</> file to point to the primary server. Set
+    standby is setting <varname>primary_conninfo</> to
+    point to the primary server. Set
     <xref linkend="guc-listen-addresses"> and authentication options
     (see <filename>pg_hba.conf</>) on the primary so that the standby server
     can connect to the <literal>replication</> pseudo-database on the primary
@@ -827,15 +827,14 @@ host    replication     foo             192.168.1.100/32        md5
     </para>
     <para>
      The host name and port number of the primary, connection user name,
-     and password are specified in the <filename>recovery.conf</> file.
+     and password are specified in <varname>primary_conninfo</>.
      The password can also be set in the <filename>~/.pgpass</> file on the
      standby (specify <literal>replication</> in the <replaceable>database</>
      field).
      For example, if the primary is running on host IP <literal>192.168.1.50</>,
      port <literal>5432</literal>, the account name for replication is
      <literal>foo</>, and the password is <literal>foopass</>, the administrator
-     can add the following line to the <filename>recovery.conf</> file on the
-     standby:
+     can set <varname>primary_conninfo</> on the standby like this:
 
 <programlisting>
 # The standby connects to the primary that is running on host 192.168.1.50
@@ -940,7 +939,7 @@ postgres=# SELECT * FROM pg_replication_slots;
 (1 row)
 </programlisting>
      To configure the standby to use this slot, <varname>primary_slot_name</>
-     should be configured in the standby's <filename>recovery.conf</>.
+     should be configured in the standby's <filename>postgresql.conf</>.
      Here is a simple example:
 <programlisting>
 standby_mode = 'on'
@@ -1417,8 +1416,8 @@ synchronous_standby_names = 'FIRST 2 (s1, s2, s3)'
    <para>
     To trigger failover of a log-shipping standby server,
     run <command>pg_ctl promote</> or create a trigger
-    file with the file name and path specified by the <varname>trigger_file</>
-    setting in <filename>recovery.conf</>. If you're planning to use
+    file with the file name and path specified by the <varname>trigger_file</>.
+    If you're planning to use
     <command>pg_ctl promote</> to fail over, <varname>trigger_file</> is
     not required. If you're setting up the reporting servers that are
     only used to offload read-only queries from the primary, not for high
@@ -1463,8 +1462,7 @@ synchronous_standby_names = 'FIRST 2 (s1, s2, s3)'
     The magic that makes the two loosely coupled servers work together is
     simply a <varname>restore_command</> used on the standby that,
     when asked for the next WAL file, waits for it to become available from
-    the primary. The <varname>restore_command</> is specified in the
-    <filename>recovery.conf</> file on the standby server. Normal recovery
+    the primary. Normal recovery
     processing would request a file from the WAL archive, reporting failure
     if the file was unavailable.  For standby processing it is normal for
     the next WAL file to be unavailable, so the standby must wait for
@@ -1551,8 +1549,14 @@ if (!triggered)
      </listitem>
      <listitem>
       <para>
+       Create a recovery trigger file <filename>recovery.signal</>
+       in the standby's cluster data directory.
+      </para>
+     </listitem>
+     <listitem>
+      <para>
        Begin recovery on the standby server from the local WAL
-       archive, using a <filename>recovery.conf</> that specifies a
+       archive, specifying a
        <varname>restore_command</> that waits as described
        previously (see <xref linkend="backup-pitr-recovery">).
       </para>
@@ -2048,9 +2052,9 @@ if (!triggered)
    <title>Administrator's Overview</title>
 
    <para>
-    If <varname>hot_standby</> is turned <literal>on</> in
-    <filename>postgresql.conf</> and there is a <filename>recovery.conf</>
-    file present, the server will run in Hot Standby mode.
+    If <varname>hot_standby</> is turned <literal>on</>
+    and there is a <filename>recovery.signal</> or
+    <filename>standby.signal</> present, the server will run in Hot Standby mode.
     However, it may take some time for Hot Standby connections to be allowed,
     because the server will not accept connections until it has completed
     sufficient recovery to provide a consistent state against which queries
diff --git a/doc/src/sgml/pgstandby.sgml b/doc/src/sgml/pgstandby.sgml
index 80c6f60..c9e6185 100644
--- a/doc/src/sgml/pgstandby.sgml
+++ b/doc/src/sgml/pgstandby.sgml
@@ -46,8 +46,8 @@
 
   <para>
    To configure a standby
-   server to use <application>pg_standby</>, put this into its
-   <filename>recovery.conf</filename> configuration file:
+   server to use <application>pg_standby</>, specify
+   <xref linkend="guc-restore-command"> like this:
 <programlisting>
 restore_command = 'pg_standby <replaceable>archiveDir</> %f %p %r'
 </programlisting>
diff --git a/doc/src/sgml/ref/pgarchivecleanup.sgml b/doc/src/sgml/ref/pgarchivecleanup.sgml
index abe01be..dc29854 100644
--- a/doc/src/sgml/ref/pgarchivecleanup.sgml
+++ b/doc/src/sgml/ref/pgarchivecleanup.sgml
@@ -38,8 +38,8 @@
 
   <para>
    To configure a standby
-   server to use <application>pg_archivecleanup</>, put this into its
-   <filename>recovery.conf</filename> configuration file:
+   server to use <application>pg_archivecleanup</>, specify
+   <xref linkend="guc-archive-cleanup-command"> like this:
 <programlisting>
 archive_cleanup_command = 'pg_archivecleanup <replaceable>archivelocation</> %r'
 </programlisting>
@@ -47,7 +47,7 @@ archive_cleanup_command = 'pg_archivecleanup <replaceable>archivelocation</> %r'
    files should be removed.
   </para>
   <para>
-   When used within <xref linkend="archive-cleanup-command">, all WAL files
+   When used within <varname>archive_cleanup_command</>, all WAL files
    logically preceding the value of the <literal>%r</> argument will be removed
    from <replaceable>archivelocation</>. This minimizes the number of files
    that need to be retained, while preserving crash-restart capability.  Use of
diff --git a/doc/src/sgml/release-9.1.sgml b/doc/src/sgml/release-9.1.sgml
index edacfbf..cfef818 100644
--- a/doc/src/sgml/release-9.1.sgml
+++ b/doc/src/sgml/release-9.1.sgml
@@ -9811,7 +9811,7 @@ Branch: REL9_0_STABLE [9d6af7367] 2015-08-15 11:02:34 -0400
        <para>
         These named restore points can be specified as recovery
         targets using the new <filename>recovery.conf</> setting
-        <link linkend="recovery-target-name"><varname>recovery_target_name</></link>.
+        <link linkend="guc-recovery-target-name"><varname>recovery_target_name</></link>.
        </para>
       </listitem>
 
@@ -9843,8 +9843,7 @@ Branch: REL9_0_STABLE [9d6af7367] 2015-08-15 11:02:34 -0400
 
       <listitem>
        <para>
-        Allow <link
-        linkend="recovery-config"><filename>recovery.conf</></link>
+        Allow <filename>recovery.conf</>
         to use the same quoting behavior as <filename>postgresql.conf</>
         (Dimitri Fontaine)
        </para>
diff --git a/doc/src/sgml/release.sgml b/doc/src/sgml/release.sgml
index 472c1f6..4237a66 100644
--- a/doc/src/sgml/release.sgml
+++ b/doc/src/sgml/release.sgml
@@ -6,7 +6,7 @@ Typical markup:
 &<>                             use & escapes
 PostgreSQL                      <productname>
 postgresql.conf, pg_hba.conf,
-        recovery.conf           <filename>
+        recovery.trigger        <filename>
 [A-Z][A-Z_ ]+[A-Z_]             <command>, <literal>, <envar>, <acronym>
 [A-Za-z_][A-Za-z0-9_]+()        <function>
 -[-A-Za-z_]+                    <option>
diff --git a/src/bin/pg_archivecleanup/pg_archivecleanup.c b/src/bin/pg_archivecleanup/pg_archivecleanup.c
index f1651d4..50e5f11 100644
--- a/src/bin/pg_archivecleanup/pg_archivecleanup.c
+++ b/src/bin/pg_archivecleanup/pg_archivecleanup.c
@@ -270,7 +270,7 @@ usage(void)
 	printf(_("  -x EXT         clean up files if they have this extension\n"));
 	printf(_("  -?, --help     show this help, then exit\n"));
 	printf(_("\n"
-			 "For use as archive_cleanup_command in recovery.conf when standby_mode = on:\n"
+			 "For use as archive_cleanup_command in postgresql.conf when standby_mode = on:\n"
 			 "  archive_cleanup_command = 'pg_archivecleanup [OPTION]... ARCHIVELOCATION %%r'\n"
 			 "e.g.\n"
 			 "  archive_cleanup_command = 'pg_archivecleanup /mnt/server/archiverdir %%r'\n"));
diff --git a/src/backend/access/transam/xlogarchive.c b/src/backend/access/transam/xlogarchive.c
index b919164..29b2fb6 100644
--- a/src/backend/access/transam/xlogarchive.c
+++ b/src/backend/access/transam/xlogarchive.c
@@ -409,7 +409,7 @@ ExecuteRecoveryCommand(char *command, char *commandName, bool failOnSignal)
 
 		ereport((signaled && failOnSignal) ? FATAL : WARNING,
 		/*------
-		   translator: First %s represents a recovery.conf parameter name like
+		   translator: First %s represents a recovery parameter name like
 		  "recovery_end_command", the 2nd is the value of that parameter, the
 		  third an already translated error message. */
 				(errmsg("%s \"%s\": %s", commandName,
newRecoveryAPI.v101y.patchapplication/octet-stream; name=newRecoveryAPI.v101y.patchDownload
diff --git a/doc/src/sgml/recovery-config.sgml b/doc/src/sgml/recovery-config.sgml
index 8c24ae2..3071879 100644
--- a/doc/src/sgml/recovery-config.sgml
+++ b/doc/src/sgml/recovery-config.sgml
@@ -11,30 +11,90 @@
 
    <para>
     This chapter describes the settings available in the
-    <filename>recovery.conf</><indexterm><primary>recovery.conf</></>
-    file. They apply only for the duration of the
+    <filename>postgresql.conf</><indexterm><primary>postgresql.conf</></>
+    file that apply only for the duration of the
     recovery.  They must be reset for any subsequent recovery you wish to
-    perform.  They cannot be changed once recovery has begun.
+    perform.
    </para>
 
    <para>
-     Settings in <filename>recovery.conf</> are specified in the format
-     <literal>name = 'value'</>. One parameter is specified per line.
-     Hash marks (<literal>#</literal>) designate the rest of the
-     line as a comment.  To embed a single quote in a parameter
-     value, write two quotes (<literal>''</>).
+    The database server can also be started "in recovery" a term that covers
+    using the server as a standby or for executing a targeted recovery. Typically
+    standby mode would be used to provide high availability and/or read
+    scalability, whereas a targeted recovery is used to recover from data loss.
    </para>
 
    <para>
-    A sample file, <filename>share/recovery.conf.sample</>,
-    is provided in the installation's <filename>share/</> directory.
+    To start the server in standby mode create a zero-length file
+    called <filename>standby.signal</><indexterm><primary>standby.signal</></>
+    in the <varname>signal_file_directory</>. The server will enter recovery and
+    will not stop recovery when the end of archived WAL is reached, but
+    will keep trying to continue recovery by connecting to the sending server as
+    specified by the <varname>sender_conninfo</> setting and/or by
+    fetching new WAL segments using <varname>restore_command</>
+    In this mode you may use parameters
+    in both <xref linkend="archive-recovery-settings"> and
+    <xref linkend="standby-settings"> sections. Parameters from
+    <xref linkend="recovery-target-settings"> will not be used.
    </para>
 
+   <para>
+    To start the server in targeted recovery create a zero-length file called
+    <filename>recovery.signal</><indexterm><primary>recovery.signal</></>
+    in the <varname>signal_file_directory</>.
+    If both <filename>standby.signal</> and <filename>recovery.signal</> files are
+    created, standby mode takes precedence. Targeted recovery mode will end when
+    end of archived WAL is reached, or when <varname>recovery_target</> is reached.
+    In this mode you may use parameters from both
+    <xref linkend="archive-recovery-settings"> and
+    <xref linkend="recovery-target-settings"> sections. Parameters from
+    <xref linkend="standby-settings"> will not be used.
+   </para>
+
+  <sect1 id="recovery-signal-settings">
+
+    <title>Recovery Signal Settings</title>
+     <variablelist>
+
+       <varlistentry id="signal_file_directory" xreflabel="signal_file_directory">
+        <term><varname>signal_file_directory</varname> (<type>string</type>)
+        <indexterm>
+          <primary><varname>signal_file_directory</> recovery parameter</primary>
+          <primary><varname>trigger_file</> a now-deprecated recovery parameter used to specify name of a promotion signal file</primary>
+        </indexterm>
+        </term>
+        <listitem>
+         <para>
+          Specifies a directory where files can be created to signal state changes
+          to the server, also sometimes described as a trigger file though that term
+          is not normally used. The default setting is <varname>data_directory</>.
+         </para>
+         <para>
+          The server will look for <filename>standby.signal</>, <filename>recovery.signal</>
+          and <filename>promote.signal</> files in this directory.
+         </para>
+         <para>
+          The parameter may only be changed at server start.
+         </para>
+        </listitem>
+       </varlistentry>
+
+    </variablelist>
+
+  </sect1>
+
   <sect1 id="archive-recovery-settings">
 
     <title>Archive Recovery Settings</title>
      <variablelist>
 
+     <para>
+      Parameter settings may be changed in <filename>postgresql.conf</filename> or
+      by executing the <command>ALTER SYSTEM</command>. Changes will take some
+      time to take effect, so changes made while not in paused state may not
+      have the desired effect in all cases.
+     </para>
+
      <varlistentry id="restore-command" xreflabel="restore_command">
       <term><varname>restore_command</varname> (<type>string</type>)
       <indexterm>
@@ -45,7 +105,7 @@
        <para>
         The local shell command to execute to retrieve an archived segment of
         the WAL file series. This parameter is required for archive recovery,
-        but optional for streaming replication.
+        but optional for standby mode.
         Any <literal>%f</> in the string is
         replaced by the name of the file to retrieve from the archive,
         and any <literal>%p</> is replaced by the copy destination path name
@@ -154,99 +214,91 @@ restore_command = 'copy "C:\\server\\archivedir\\%f" "%p"'  # Windows
     <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_lsn</>, <varname>recovery_target_name</>,
-      <varname>recovery_target_time</>, or <varname>recovery_target_xid</>
-      can be used; if more than one of these is specified in the configuration
-      file, the last entry will be used.
+      By default, recovery will recover to the end of the WAL log. An earlier
+      stopping point may be specified using <varname>recovery_target_type</>
+      and in most cases also <varname>recovery_target_value</>, plus the optional
+      parameters <varname>recovery_target_inclusive</>,
+      <varname>recovery_targeti_timeline</> and <varname>recovery_target_action</>.
+     </para>
+
+     <para>
+      Parameter settings may be changed in <filename>postgresql.conf</filename> or
+      by executing the <command>ALTER SYSTEM</command>. Changes will take some
+      time to take effect, so changes made while not in paused state may not
+      have the desired effect in all cases.
      </para>
 
      <variablelist>
-     <varlistentry id="recovery-target" xreflabel="recovery_target">
-      <term><varname>recovery_target</varname><literal> = 'immediate'</literal>
+     <varlistentry id="recovery-target-type" xreflabel="recovery_target_type">
+      <term><varname>recovery_target_type</varname> (<type>enum</type>)
       <indexterm>
-        <primary><varname>recovery_target</> recovery parameter</primary>
+        <primary><varname>recovery_target_type</> targeted recovery parameter</primary>
       </indexterm>
       </term>
       <listitem>
        <para>
-        This parameter specifies that recovery should end as soon as a
-        consistent state is reached, i.e. as early as possible. When restoring
-        from an online backup, this means the point where taking the backup
-        ended.
+        <varname>recovery_target_type</varname> specifies the search criteria used for a
+        a targeted recovery. The default value is <literal>none</literal>. Valid
+        values are <literal>none</literal>, <literal>immediate</literal>,
+        <literal>name</literal>, <literal>timestamp</literal>,
+        <literal>xid</literal> and <literal>lsn</literal>.
        </para>
+
        <para>
-        Technically, this is a string parameter, but <literal>'immediate'</>
-        is currently the only allowed value.
+        Target-type <literal>none</literal> specifies that recovery will not stop
+        until it runs out of WAL, which is the default setting. When not in targeted
+        recovery this is the only meaningful setting.
        </para>
-      </listitem>
-     </varlistentry>
 
-     <varlistentry id="recovery-target-name" xreflabel="recovery_target_name">
-      <term><varname>recovery_target_name</varname> (<type>string</type>)
-      <indexterm>
-        <primary><varname>recovery_target_name</> recovery parameter</primary>
-      </indexterm>
-      </term>
-      <listitem>
        <para>
-        This parameter specifies the named restore point (created with
-        <function>pg_create_restore_point()</>) to which recovery will proceed.
+        Target-type <literal>immediate</literal> specifies that recovery should end as
+        soon as a consistent state is reached, i.e. as early as possible. When restoring
+        from an online backup, this means the point where taking the backup ended.
+        For this target-type, no additional target-specifiers influence the stopping
+        point.
        </para>
-      </listitem>
-     </varlistentry>
 
-     <varlistentry id="recovery-target-time" xreflabel="recovery_target_time">
-      <term><varname>recovery_target_time</varname> (<type>timestamp</type>)
-      <indexterm>
-        <primary><varname>recovery_target_time</> recovery parameter</primary>
-      </indexterm>
-      </term>
-      <listitem>
        <para>
-        This parameter specifies the time stamp up to which recovery
-        will proceed.
+        Target-type <literal>name</literal> specifies that recovery will
+        proceed to the point specified by <varname>recovery_target_value</varname>
+        when interpreted as the name of a restore point created with
+        <function>pg_create_restore_point()</>.
         The precise stopping point is also influenced by
-        <xref linkend="recovery-target-inclusive">.
+        <xref linkend="recovery-target-inclusive"> and
+        <xref linkend="recovery-target-timeline">.
        </para>
-      </listitem>
-     </varlistentry>
 
-     <varlistentry id="recovery-target-xid" xreflabel="recovery_target_xid">
-      <term><varname>recovery_target_xid</varname> (<type>string</type>)
-      <indexterm>
-        <primary><varname>recovery_target_xid</> recovery parameter</primary>
-      </indexterm>
-      </term>
-      <listitem>
        <para>
-        This parameter specifies the transaction ID up to which recovery
-        will proceed. Keep in mind
+        Target-type <literal>timestamp</literal> specifies that recovery will
+        proceed to the point specified by <varname>recovery_target_value</varname>
+        when interpreted as the timestamp of a transaction commit or abort.
+        The precise stopping point is also influenced by
+        <xref linkend="recovery-target-inclusive"> and
+        <xref linkend="recovery-target-timeline">.
+       </para>
+
+       <para>
+        Target-type <literal>xid</literal> specifies that recovery will
+        proceed to the point specified by <varname>recovery_target_value</varname>
+        when interpreted as the transaction ID of a transaction commit or abort.
+        Keep in mind
         that while transaction IDs are assigned sequentially at transaction
         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.
         The precise stopping point is also influenced by
-        <xref linkend="recovery-target-inclusive">.
+        <xref linkend="recovery-target-inclusive"> and
+        <xref linkend="recovery-target-timeline">.
        </para>
-      </listitem>
-     </varlistentry>
 
-     <varlistentry id="recovery-target-lsn" xreflabel="recovery_target_lsn">
-      <term><varname>recovery_target_lsn</varname> (<type>pg_lsn</type>)
-      <indexterm>
-        <primary><varname>recovery_target_lsn</> recovery parameter</primary>
-      </indexterm>
-      </term>
-      <listitem>
        <para>
-        This parameter specifies the LSN of the transaction log location up
-        to which recovery will proceed. The precise stopping point is also
-        influenced by <xref linkend="recovery-target-inclusive">. This
-        parameter is parsed using the system data type
-        <link linkend="datatype-pg-lsn"><type>pg_lsn</></link>.
+        Target-type <literal>lsn</literal> specifies that recovery will
+        proceed to the point specified by <varname>recovery_target_value</varname>
+        when interpreted as the LSN of any WAL record, parsed using the system
+        data type <link linkend="datatype-pg-lsn"><type>pg_lsn</></link>.
+        The precise stopping point is also influenced by
+        <xref linkend="recovery-target-inclusive"> and
+        <xref linkend="recovery-target-timeline">.
        </para>
       </listitem>
      </varlistentry>
@@ -258,6 +310,23 @@ restore_command = 'copy "C:\\server\\archivedir\\%f" "%p"'  # Windows
      </para>
 
      <variablelist>
+     <varlistentry id="recovery-target-value"
+                   xreflabel="recovery_target_value">
+      <term><varname>recovery_target_value</varname> (<type>string</type>)
+      <indexterm>
+        <primary><varname>recovery_target_value</> targeted recovery search parameter</primary>
+      </indexterm>
+      </term>
+      <listitem>
+       <para>
+        Specifies the stopping point for targeted recovery. The string value
+        is interpreted according to strict rules according to the value of
+        <varname>recovery_target_type</varname>. An empty string may be an
+        invalid value in some cases.
+       </para>
+      </listitem>
+     <variablelist>
+
      <varlistentry id="recovery-target-inclusive"
                    xreflabel="recovery_target_inclusive">
       <term><varname>recovery_target_inclusive</varname> (<type>boolean</type>)
@@ -357,34 +426,21 @@ restore_command = 'copy "C:\\server\\archivedir\\%f" "%p"'  # Windows
     <title>Standby Server Settings</title>
      <variablelist>
 
-       <varlistentry id="standby-mode" xreflabel="standby_mode">
-        <term><varname>standby_mode</varname> (<type>boolean</type>)
-        <indexterm>
-          <primary><varname>standby_mode</> recovery parameter</primary>
-        </indexterm>
-        </term>
-        <listitem>
-         <para>
-          Specifies whether to start the <productname>PostgreSQL</> server as
-          a standby. If this parameter is <literal>on</>, the server will
-          not stop recovery when the end of archived WAL is reached, but
-          will keep trying to continue recovery by fetching new WAL segments
-          using <varname>restore_command</>
-          and/or by connecting to the primary server as specified by the
-          <varname>primary_conninfo</> setting.
-         </para>
-        </listitem>
-       </varlistentry>
-       <varlistentry id="primary-conninfo" xreflabel="primary_conninfo">
-        <term><varname>primary_conninfo</varname> (<type>string</type>)
+     <para>
+      Parameter settings may be changed only at server start, though later
+      patches may add this capability.
+     </para>
+
+       <varlistentry id="sender-conninfo" xreflabel="sender_conninfo">
+        <term><varname>sender_conninfo</varname> (<type>string</type>)
         <indexterm>
-          <primary><varname>primary_conninfo</> recovery parameter</primary>
+          <primary><varname>sender_conninfo</> recovery parameter</primary>
         </indexterm>
         </term>
         <listitem>
          <para>
           Specifies a connection string to be used for the standby server
-          to connect with the primary. This string is in the format
+          to connect with a sending server. This string is in the format
           described in <xref linkend="libpq-connstring">. If any option is
           unspecified in this string, then the corresponding environment
           variable (see <xref linkend="libpq-envars">) is checked. If the
@@ -393,56 +449,42 @@ restore_command = 'copy "C:\\server\\archivedir\\%f" "%p"'  # Windows
          </para>
          <para>
           The connection string should specify the host name (or address)
-          of the primary server, as well as the port number if it is not
+          of the sending server, as well as the port number if it is not
           the same as the standby server's default.
           Also specify a user name corresponding to a suitably-privileged role
-          on the primary (see
+          on the sending server (see
           <xref linkend="streaming-replication-authentication">).
-          A password needs to be provided too, if the primary demands password
+          A password needs to be provided too, if the sender demands password
           authentication.  It can be provided in the
-          <varname>primary_conninfo</varname> string, or in a separate
+          <varname>sender_conninfo</varname> string, or in a separate
           <filename>~/.pgpass</> file on the standby server (use
           <literal>replication</> as the database name).
           Do not specify a database name in the
-          <varname>primary_conninfo</varname> string.
+          <varname>sender_conninfo</varname> string.
          </para>
          <para>
           This setting has no effect if <varname>standby_mode</> is <literal>off</>.
          </para>
         </listitem>
        </varlistentry>
-       <varlistentry id="primary-slot-name" xreflabel="primary_slot_name">
-        <term><varname>primary_slot_name</varname> (<type>string</type>)
+
+       <varlistentry id="sender-slot-name" xreflabel="sender_slot_name">
+        <term><varname>sender_slot_name</varname> (<type>string</type>)
         <indexterm>
-          <primary><varname>primary_slot_name</> recovery parameter</primary>
+          <primary><varname>sender_slot_name</> recovery parameter</primary>
         </indexterm>
         </term>
         <listitem>
          <para>
           Optionally specifies an existing replication slot to be used when
-          connecting to the primary via streaming replication to control
+          connecting to the sending server via streaming replication to control
           resource removal on the upstream node
           (see <xref linkend="streaming-replication-slots">).
-          This setting has no effect if <varname>primary_conninfo</> is not
+          This setting has no effect if <varname>sender_conninfo</> is not
           set.
          </para>
         </listitem>
        </varlistentry>
-       <varlistentry id="trigger-file" xreflabel="trigger_file">
-        <term><varname>trigger_file</varname> (<type>string</type>)
-        <indexterm>
-          <primary><varname>trigger_file</> recovery parameter</primary>
-        </indexterm>
-        </term>
-        <listitem>
-         <para>
-          Specifies a trigger file whose presence ends recovery in the
-          standby.  Even if this value is not set, you can still promote
-          the standby using <command>pg_ctl promote</>.
-          This setting has no effect if <varname>standby_mode</> is <literal>off</>.
-         </para>
-        </listitem>
-       </varlistentry>
 
      <varlistentry id="recovery-min-apply-delay" xreflabel="recovery_min_apply_delay">
       <term><varname>recovery_min_apply_delay</varname> (<type>integer</type>)
@@ -453,7 +495,7 @@ restore_command = 'copy "C:\\server\\archivedir\\%f" "%p"'  # Windows
       <listitem>
        <para>
         By default, a standby server restores WAL records from the
-        primary as soon as possible. It may be useful to have a time-delayed
+        sending server as soon as possible. It may be useful to have a time-delayed
         copy of the data, offering opportunities to correct data loss errors.
         This parameter allows you to delay recovery by a fixed period of time,
         measured in milliseconds if no unit is specified.  For example, if
diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c
index f8ffa5c..e4168c2 100644
--- a/src/backend/access/transam/xlog.c
+++ b/src/backend/access/transam/xlog.c
@@ -76,11 +76,13 @@
 
 extern uint32 bootstrap_data_checksum_version;
 
-/* File path names (all relative to $PGDATA) */
+/* File path names */
 #define RECOVERY_COMMAND_FILE	"recovery.conf"
 #define RECOVERY_COMMAND_DONE	"recovery.done"
-#define PROMOTE_SIGNAL_FILE		"promote"
-#define FALLBACK_PROMOTE_SIGNAL_FILE "fallback_promote"
+#define PROMOTE_SIGNAL_FILE	"promote.signal"
+#define FALLBACK_PROMOTE_SIGNAL_FILE "fallback_promote.signal"
+#define RECOVERY_SIGNAL_FILE	"recovery.signal"
+#define STANDBY_SIGNAL_FILE	"standby.signal"
 
 
 /* User-settable parameters */
@@ -241,29 +243,35 @@ static int	LocalXLogInsertAllowed = -1;
 */
 bool		ArchiveRecoveryRequested = false;
 bool		InArchiveRecovery = false;
+static bool	standby_signal_file_found = false;
+static bool	recovery_signal_file_found = false;
 
 /* Was the last xlog file restored from archive, or local? */
 static bool restoredFromArchive = false;
 
 /* options taken from recovery.conf for archive recovery */
-char	   *recoveryRestoreCommand = NULL;
-static char *recoveryEndCommand = NULL;
-static char *archiveCleanupCommand = NULL;
-static RecoveryTargetType recoveryTarget = RECOVERY_TARGET_UNSET;
-static bool recoveryTargetInclusive = true;
-static RecoveryTargetAction recoveryTargetAction = RECOVERY_TARGET_ACTION_PAUSE;
-static TransactionId recoveryTargetXid;
-static TimestampTz recoveryTargetTime;
-static char *recoveryTargetName;
-static XLogRecPtr recoveryTargetLSN;
-static int	recovery_min_apply_delay = 0;
-static TimestampTz recoveryDelayUntilTime;
+char *recoveryRestoreCommand = NULL;
+char *recoveryEndCommand = NULL;
+char *archiveCleanupCommand = NULL;
+char *recoveryTargetTypeString;
+RecoveryTargetType recoveryTarget = RECOVERY_TARGET_UNSET;
+char *recoveryTargetValue = NULL;
+bool recoveryTargetInclusive = true;
+RecoveryTargetAction recoveryTargetAction = RECOVERY_TARGET_ACTION_PAUSE;
+TransactionId recoveryTargetXid;
+TimestampTz recoveryTargetTime;
+char *recoveryTargetName;
+XLogRecPtr recoveryTargetLSN;
+int	recovery_min_apply_delay = 0;
+TimestampTz recoveryDelayUntilTime;
 
 /* options taken from recovery.conf for XLOG streaming */
-static bool StandbyModeRequested = false;
-static char *PrimaryConnInfo = NULL;
-static char *PrimarySlotName = NULL;
-static char *TriggerFile = NULL;
+bool StandbyModeRequested = false;
+
+char *signal_file_directory = NULL;
+char PromoteSignalFile[MAXPGPATH];
+char RecoverySignalFile[MAXPGPATH];
+char StandbySignalFile[MAXPGPATH];
 
 /* are we currently in standby mode? */
 bool		StandbyMode = false;
@@ -289,7 +297,11 @@ static bool recoveryStopAfter;
  * the currently-scanned WAL record was generated).  We also need these
  * timeline values:
  *
- * recoveryTargetTLI: the desired timeline that we want to end in.
+ * recoveryTargetTimeLineGoal: what the user requested, if any
+ *
+ * recoveryTargetTLIRequested: numeric value of requested timeline, if constant
+ *
+ * recoveryTargetTLI: the currently understood target timeline; changes
  *
  * recoveryTargetIsLatest: was the requested target timeline 'latest'?
  *
@@ -305,7 +317,10 @@ static bool recoveryStopAfter;
  * file was created.)  During a sequential scan we do not allow this value
  * to decrease.
  */
-static TimeLineID recoveryTargetTLI;
+char *recoveryTargetTLIString = NULL;
+RecoveryTargetTimeLineGoal recoveryTargetTimeLineGoal = RECOVERY_TARGET_TIMELINE_CONTROLFILE;
+TimeLineID recoveryTargetTLIRequested = 0;
+TimeLineID recoveryTargetTLI = 0;
 static bool recoveryTargetIsLatest = false;
 static List *expectedTLEs;
 static TimeLineID curFileTLI;
@@ -801,7 +816,8 @@ static bool holdingAllLocks = false;
 static MemoryContext walDebugCxt = NULL;
 #endif
 
-static void readRecoveryCommandFile(void);
+static void readRecoverySignalFile(void);
+static void logRecoveryParameters(void);
 static void exitArchiveRecovery(TimeLineID endTLI, XLogRecPtr endOfLog);
 static bool recoveryStopsBefore(XLogReaderState *record);
 static bool recoveryStopsAfter(XLogReaderState *record);
@@ -4963,261 +4979,170 @@ str_time(pg_time_t tnow)
 
 /*
  * See if there is a recovery command file (recovery.conf), and if so
- * read in parameters for archive recovery and XLOG streaming.
+ * throw an ERROR.
  *
- * The file is parsed using the main configuration parser.
+ * See if there are any recovery signal files and if so, set state for
+ * recovery.
  */
 static void
-readRecoveryCommandFile(void)
+readRecoverySignalFile(void)
 {
-	FILE	   *fd;
-	TimeLineID	rtli = 0;
-	bool		rtliGiven = false;
-	ConfigVariable *item,
-			   *head = NULL,
-			   *tail = NULL;
-	bool		recoveryTargetActionSet = false;
+	struct stat stat_buf;
 
+	if (IsBootstrapProcessingMode())
+		return;
 
-	fd = AllocateFile(RECOVERY_COMMAND_FILE, "r");
-	if (fd == NULL)
+	/*
+	 * Set paths for named signal files
+	 */
+	if (signal_file_directory)
 	{
-		if (errno == ENOENT)
-			return;				/* not there, so no archive recovery */
+		snprintf(StandbySignalFile, MAXPGPATH, "%s/%s", signal_file_directory, STANDBY_SIGNAL_FILE);
+		snprintf(RecoverySignalFile, MAXPGPATH, "%s/%s", signal_file_directory, RECOVERY_SIGNAL_FILE);
+		snprintf(PromoteSignalFile, MAXPGPATH, "%s/%s", signal_file_directory, PROMOTE_SIGNAL_FILE);
+	}
+	else
+	{
+		snprintf(StandbySignalFile, MAXPGPATH, "%s", STANDBY_SIGNAL_FILE);
+		snprintf(RecoverySignalFile, MAXPGPATH, "%s", RECOVERY_SIGNAL_FILE);
+		snprintf(PromoteSignalFile, MAXPGPATH, "%s", PROMOTE_SIGNAL_FILE);
+	}
+
+	/*
+	 * Check for old recovery API file: recovery.conf
+	 */
+	if (stat(RECOVERY_COMMAND_FILE, &stat_buf) == 0)
 		ereport(FATAL,
-				(errcode_for_file_access(),
-				 errmsg("could not open recovery command file \"%s\": %m",
+			(errcode_for_file_access(),
+			 errmsg("deprecated API using recovery command file \"%s\"",
 						RECOVERY_COMMAND_FILE)));
+	/*
+	 * Remove unused .done file, if present. Ignore if absent.
+	 */
+	unlink(RECOVERY_COMMAND_DONE);
+
+	/*
+	 * Check for recovery signal files.
+	 *
+	 * If present, standby signal file takes precedence.
+	 * If neither is present then we won't enter archive recovery.
+	 */
+	if (stat(StandbySignalFile, &stat_buf) == 0)
+	{
+		standby_signal_file_found = true;
+		StandbyModeRequested = true;
+		ArchiveRecoveryRequested = true;
+	}
+	else if (stat(RecoverySignalFile, &stat_buf) == 0)
+	{
+		recovery_signal_file_found = true;
+		StandbyModeRequested = false;
+		ArchiveRecoveryRequested = true;
 	}
+	else
+		return;
 
 	/*
-	 * Since we're asking ParseConfigFp() to report errors as FATAL, there's
-	 * no need to check the return value.
+	 * We don't support standby_mode in standalone backends; that requires
+	 * other processes such as the WAL receiver to be alive.
 	 */
-	(void) ParseConfigFp(fd, RECOVERY_COMMAND_FILE, 0, FATAL, &head, &tail);
+	if (StandbyModeRequested && !IsUnderPostmaster)
+		ereport(FATAL,
+				(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+			errmsg("standby mode is not supported by single-user servers")));
 
-	FreeFile(fd);
+	logRecoveryParameters();
+	validateRecoveryParameters();
+}
 
-	for (item = head; item; item = item->next)
-	{
-		if (strcmp(item->name, "restore_command") == 0)
-		{
-			recoveryRestoreCommand = pstrdup(item->value);
-			ereport(DEBUG2,
-					(errmsg_internal("restore_command = '%s'",
-									 recoveryRestoreCommand)));
-		}
-		else if (strcmp(item->name, "recovery_end_command") == 0)
-		{
-			recoveryEndCommand = pstrdup(item->value);
-			ereport(DEBUG2,
-					(errmsg_internal("recovery_end_command = '%s'",
-									 recoveryEndCommand)));
-		}
-		else if (strcmp(item->name, "archive_cleanup_command") == 0)
-		{
-			archiveCleanupCommand = pstrdup(item->value);
-			ereport(DEBUG2,
-					(errmsg_internal("archive_cleanup_command = '%s'",
-									 archiveCleanupCommand)));
-		}
-		else if (strcmp(item->name, "recovery_target_action") == 0)
-		{
-			if (strcmp(item->value, "pause") == 0)
-				recoveryTargetAction = RECOVERY_TARGET_ACTION_PAUSE;
-			else if (strcmp(item->value, "promote") == 0)
-				recoveryTargetAction = RECOVERY_TARGET_ACTION_PROMOTE;
-			else if (strcmp(item->value, "shutdown") == 0)
-				recoveryTargetAction = RECOVERY_TARGET_ACTION_SHUTDOWN;
-			else
-				ereport(ERROR,
-						(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
-				errmsg("invalid value for recovery parameter \"%s\": \"%s\"",
-					   "recovery_target_action",
-					   item->value),
-						 errhint("Valid values are \"pause\", \"promote\", and \"shutdown\".")));
+static void
+logRecoveryParameters(void)
+{
+	int	normal_log_level = LOG; //DEBUG2;
 
-			ereport(DEBUG2,
-					(errmsg_internal("recovery_target_action = '%s'",
-									 item->value)));
+	/*
+	 * Log messages for recovery parameters at server start
+	 */
+	ereport(normal_log_level,
+		(errmsg_internal("standby_mode = '%s'", (StandbyModeRequested ? "on " : "off"))));
 
-			recoveryTargetActionSet = true;
-		}
-		else if (strcmp(item->name, "recovery_target_timeline") == 0)
-		{
-			rtliGiven = true;
-			if (strcmp(item->value, "latest") == 0)
-				rtli = 0;
-			else
-			{
-				errno = 0;
-				rtli = (TimeLineID) strtoul(item->value, NULL, 0);
-				if (errno == EINVAL || errno == ERANGE)
-					ereport(FATAL,
-							(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
-							 errmsg("recovery_target_timeline is not a valid number: \"%s\"",
-									item->value)));
-			}
-			if (rtli)
-				ereport(DEBUG2,
-				   (errmsg_internal("recovery_target_timeline = %u", rtli)));
-			else
-				ereport(DEBUG2,
-					 (errmsg_internal("recovery_target_timeline = latest")));
-		}
-		else if (strcmp(item->name, "recovery_target_xid") == 0)
-		{
-			errno = 0;
-			recoveryTargetXid = (TransactionId) strtoul(item->value, NULL, 0);
-			if (errno == EINVAL || errno == ERANGE)
-				ereport(FATAL,
-						(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
-				  errmsg("recovery_target_xid is not a valid number: \"%s\"",
-						 item->value)));
-			ereport(DEBUG2,
-					(errmsg_internal("recovery_target_xid = %u",
-									 recoveryTargetXid)));
-			recoveryTarget = RECOVERY_TARGET_XID;
-		}
-		else if (strcmp(item->name, "recovery_target_time") == 0)
-		{
-			recoveryTarget = RECOVERY_TARGET_TIME;
+	if (recoveryRestoreCommand != NULL)
+		ereport(normal_log_level,
+			(errmsg_internal("restore_command = '%s'", recoveryRestoreCommand)));
 
-			/*
-			 * Convert the time string given by the user to TimestampTz form.
-			 */
-			recoveryTargetTime =
-				DatumGetTimestampTz(DirectFunctionCall3(timestamptz_in,
-												CStringGetDatum(item->value),
-												ObjectIdGetDatum(InvalidOid),
-														Int32GetDatum(-1)));
-			ereport(DEBUG2,
-					(errmsg_internal("recovery_target_time = '%s'",
-								   timestamptz_to_str(recoveryTargetTime))));
-		}
-		else if (strcmp(item->name, "recovery_target_name") == 0)
-		{
-			recoveryTarget = RECOVERY_TARGET_NAME;
+	if (recoveryEndCommand != NULL)
+		ereport(normal_log_level,
+			(errmsg_internal("recovery_end_command = '%s'", recoveryRestoreCommand)));
 
-			recoveryTargetName = pstrdup(item->value);
-			if (strlen(recoveryTargetName) >= MAXFNAMELEN)
-				ereport(FATAL,
-						(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
-						 errmsg("recovery_target_name is too long (maximum %d characters)",
-								MAXFNAMELEN - 1)));
+	if (archiveCleanupCommand != NULL)
+		ereport(normal_log_level,
+			(errmsg_internal("archive_cleanup_command = '%s'", archiveCleanupCommand)));
 
-			ereport(DEBUG2,
-					(errmsg_internal("recovery_target_name = '%s'",
-									 recoveryTargetName)));
-		}
-		else if (strcmp(item->name, "recovery_target_lsn") == 0)
-		{
-			recoveryTarget = RECOVERY_TARGET_LSN;
+	if (SenderConnInfo != NULL)
+		ereport(normal_log_level,
+			(errmsg_internal("sender_conninfo = '%s'", SenderConnInfo)));
 
-			/*
-			 * Convert the LSN string given by the user to XLogRecPtr form.
-			 */
-			recoveryTargetLSN =
-				DatumGetLSN(DirectFunctionCall3(pg_lsn_in,
-												CStringGetDatum(item->value),
-												ObjectIdGetDatum(InvalidOid),
-														Int32GetDatum(-1)));
-			ereport(DEBUG2,
-					(errmsg_internal("recovery_target_lsn = '%X/%X'",
-									 (uint32) (recoveryTargetLSN >> 32),
-									 (uint32) recoveryTargetLSN)));
-		}
-		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 value for recovery parameter \"%s\": \"%s\"",
-					   "recovery_target",
-					   item->value),
-					   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)
-		{
-			/*
-			 * does nothing if a recovery_target is not also set
-			 */
-			if (!parse_bool(item->value, &recoveryTargetInclusive))
-				ereport(ERROR,
-						(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
-						 errmsg("parameter \"%s\" requires a Boolean value",
-								"recovery_target_inclusive")));
-			ereport(DEBUG2,
-					(errmsg_internal("recovery_target_inclusive = %s",
-									 item->value)));
-		}
-		else if (strcmp(item->name, "standby_mode") == 0)
-		{
-			if (!parse_bool(item->value, &StandbyModeRequested))
-				ereport(ERROR,
-						(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
-						 errmsg("parameter \"%s\" requires a Boolean value",
-								"standby_mode")));
-			ereport(DEBUG2,
-					(errmsg_internal("standby_mode = '%s'", item->value)));
-		}
-		else if (strcmp(item->name, "primary_conninfo") == 0)
-		{
-			PrimaryConnInfo = pstrdup(item->value);
-			ereport(DEBUG2,
-					(errmsg_internal("primary_conninfo = '%s'",
-									 PrimaryConnInfo)));
-		}
-		else if (strcmp(item->name, "primary_slot_name") == 0)
-		{
-			ReplicationSlotValidateName(item->value, ERROR);
-			PrimarySlotName = pstrdup(item->value);
-			ereport(DEBUG2,
-					(errmsg_internal("primary_slot_name = '%s'",
-									 PrimarySlotName)));
-		}
-		else if (strcmp(item->name, "trigger_file") == 0)
+	if (SenderSlotName != NULL)
+		ereport(normal_log_level,
+			(errmsg_internal("sender_slot_name = '%s'", SenderSlotName)));
+
+	if (recovery_min_apply_delay > 0)
+		ereport(normal_log_level,
+			(errmsg_internal("recovery_min_apply_delay = '%u'", recovery_min_apply_delay)));
+
+	/*
+	 * Check details for recovery target, if any
+	 */
+	if (recoveryTarget > RECOVERY_TARGET_UNSET)
+	{
+		ereport(normal_log_level,
+			(errmsg_internal("recovery_target_type = '%s'", RecoveryTargetText(recoveryTarget))));
+		if (recoveryTargetValue != NULL)
+			ereport(normal_log_level,
+				(errmsg_internal("recovery_target_value = '%s'", recoveryTargetValue)));
+		ereport(normal_log_level,
+			(errmsg_internal("recovery_target_inclusive = '%s'", (recoveryTargetInclusive ? "on " : "off"))));
+
+		if (recoveryTargetTimeLineGoal == RECOVERY_TARGET_TIMELINE_CONTROLFILE)
+			ereport(normal_log_level,
+				(errmsg_internal("recovery_target_timeline = '%u' (from controlfile)",
+					recoveryTargetTLI)));
+		else if (recoveryTargetTimeLineGoal == RECOVERY_TARGET_TIMELINE_LATEST)
+			ereport(normal_log_level,
+				(errmsg_internal("recovery_target_timeline = 'latest'")));
+		else
 		{
-			TriggerFile = pstrdup(item->value);
-			ereport(DEBUG2,
-					(errmsg_internal("trigger_file = '%s'",
-									 TriggerFile)));
+			Assert(recoveryTargetTimeLineGoal == RECOVERY_TARGET_TIMELINE_NUMERIC);
+			ereport(normal_log_level,
+				(errmsg_internal("recovery_target_timeline = '%u'",
+					recoveryTargetTLIRequested)));
 		}
-		else if (strcmp(item->name, "recovery_min_apply_delay") == 0)
-		{
-			const char *hintmsg;
+	}
 
-			if (!parse_int(item->value, &recovery_min_apply_delay, GUC_UNIT_MS,
-						   &hintmsg))
-				ereport(ERROR,
-						(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
-						 errmsg("parameter \"%s\" requires a temporal value",
-								"recovery_min_apply_delay"),
-						 hintmsg ? errhint("%s", _(hintmsg)) : 0));
-			ereport(DEBUG2,
-					(errmsg_internal("recovery_min_apply_delay = '%s'", item->value)));
-		}
-		else
+	ereport(normal_log_level,
+		(errmsg_internal("recovery_target_action = '%s'", RecoveryTargetActionText(recoveryTargetAction))));
+}
+
+void
+validateRecoveryParameters(void)
+{
+	if (!ArchiveRecoveryRequested)
+		return;
+
+	if (recoveryTarget > RECOVERY_TARGET_UNSET &&
+		recoveryTargetValue == NULL)
 			ereport(FATAL,
 					(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
-					 errmsg("unrecognized recovery parameter \"%s\"",
-							item->name)));
-	}
+					 errmsg("must specify recovery_target_value when recovery_target_type is set")));
 
 	/*
 	 * Check for compulsory parameters
 	 */
 	if (StandbyModeRequested)
 	{
-		if (PrimaryConnInfo == NULL && recoveryRestoreCommand == NULL)
+		if (SenderConnInfo == NULL && recoveryRestoreCommand == NULL)
 			ereport(WARNING,
-					(errmsg("recovery command file \"%s\" specified neither primary_conninfo nor restore_command",
-							RECOVERY_COMMAND_FILE),
+					(errmsg("specified neither primary_conninfo nor restore_command"),
 					 errhint("The database server will regularly poll the pg_wal subdirectory to check for files placed there.")));
 	}
 	else
@@ -5225,8 +5150,7 @@ readRecoveryCommandFile(void)
 		if (recoveryRestoreCommand == NULL)
 			ereport(FATAL,
 					(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
-					 errmsg("recovery command file \"%s\" must specify restore_command when standby mode is not enabled",
-							RECOVERY_COMMAND_FILE)));
+					 errmsg("must specify restore_command when standby mode is not enabled")));
 	}
 
 	/*
@@ -5235,50 +5159,46 @@ readRecoveryCommandFile(void)
 	 * hot_standby = off, which was surprising behaviour.
 	 */
 	if (recoveryTargetAction == RECOVERY_TARGET_ACTION_PAUSE &&
-		recoveryTargetActionSet &&
 		!EnableHotStandby)
 		recoveryTargetAction = RECOVERY_TARGET_ACTION_SHUTDOWN;
 
 	/*
-	 * We don't support standby_mode in standalone backends; that requires
-	 * other processes such as the WAL receiver to be alive.
-	 */
-	if (StandbyModeRequested && !IsUnderPostmaster)
-		ereport(FATAL,
-				(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
-			errmsg("standby mode is not supported by single-user servers")));
-
-	/* Enable fetching from archive recovery area */
-	ArchiveRecoveryRequested = true;
-
-	/*
 	 * If user specified recovery_target_timeline, validate it or compute the
 	 * "latest" value.  We can't do this until after we've gotten the restore
 	 * command and set InArchiveRecovery, because we need to fetch timeline
 	 * history files from the archive.
 	 */
-	if (rtliGiven)
+	if (recoveryTargetTimeLineGoal == RECOVERY_TARGET_TIMELINE_NUMERIC)
 	{
-		if (rtli)
-		{
-			/* Timeline 1 does not have a history file, all else should */
-			if (rtli != 1 && !existsTimeLineHistory(rtli))
-				ereport(FATAL,
-						(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
-						 errmsg("recovery target timeline %u does not exist",
-								rtli)));
-			recoveryTargetTLI = rtli;
-			recoveryTargetIsLatest = false;
-		}
-		else
-		{
-			/* We start the "latest" search from pg_control's timeline */
-			recoveryTargetTLI = findNewestTimeLine(recoveryTargetTLI);
-			recoveryTargetIsLatest = true;
-		}
-	}
+		TimeLineID rtli = recoveryTargetTLIRequested;
 
-	FreeConfigVariables(head);
+		/* Timeline 1 does not have a history file, all else should */
+		if (rtli != 1 && !existsTimeLineHistory(rtli))
+			ereport(FATAL,
+					(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+					 errmsg("recovery target timeline %u does not exist",
+							rtli)));
+		recoveryTargetTLI = rtli;
+
+		/*
+		 * The user has requested a specific tli. This might be the latest
+		 * timeline but we don't know that; the point here is that we do not
+		 * allow the recoveryTargetTLI to follow any changes.
+		 */
+		recoveryTargetIsLatest = false;
+	}
+	else if (recoveryTargetTimeLineGoal == RECOVERY_TARGET_TIMELINE_LATEST)
+	{
+		/* We start the "latest" search from pg_control's timeline */
+		recoveryTargetTLI = findNewestTimeLine(recoveryTargetTLI);
+		recoveryTargetIsLatest = true;
+	}
+	else
+	{
+		/* else we just use the recoveryTargetTLI as already read from ControlFile */
+		Assert(recoveryTargetTimeLineGoal == RECOVERY_TARGET_TIMELINE_CONTROLFILE);
+		recoveryTargetIsLatest = false;
+	}
 }
 
 /*
@@ -5379,11 +5299,19 @@ exitArchiveRecovery(TimeLineID endTLI, XLogRecPtr endOfLog)
 	unlink(recoveryPath);		/* ignore any error */
 
 	/*
-	 * Rename the config file out of the way, so that we don't accidentally
+	 * Rename the signal files out of the way, so that we don't accidentally
 	 * re-enter archive recovery mode in a subsequent crash.
 	 */
-	unlink(RECOVERY_COMMAND_DONE);
-	durable_rename(RECOVERY_COMMAND_FILE, RECOVERY_COMMAND_DONE, FATAL);
+	if (unlink(StandbySignalFile) != 0 && standby_signal_file_found)
+		ereport(ERROR,
+			(errcode_for_file_access(),
+			 errmsg("could not remove file \"%s\": %m",
+					StandbySignalFile)));
+	if (unlink(RecoverySignalFile) != 0 && recovery_signal_file_found)
+		ereport(ERROR,
+			(errcode_for_file_access(),
+			 errmsg("could not remove file \"%s\": %m",
+					RecoverySignalFile)));
 
 	ereport(LOG,
 			(errmsg("archive recovery complete")));
@@ -6114,10 +6042,9 @@ StartupXLOG(void)
 		recoveryTargetTLI = ControlFile->checkPointCopy.ThisTimeLineID;
 
 	/*
-	 * Check for recovery control file, and if so set up state for offline
-	 * recovery
+	 * Check for signal files, and if so set up state for offline recovery
 	 */
-	readRecoveryCommandFile();
+	readRecoverySignalFile();
 
 	/*
 	 * Save archive_cleanup_command in shared memory so that other processes
@@ -11383,7 +11310,7 @@ WaitForWALToBecomeAvailable(XLogRecPtr RecPtr, bool randAccess,
 					 * that when we later jump backwards to start redo at
 					 * RedoStartLSN, we will have the logs streamed already.
 					 */
-					if (PrimaryConnInfo)
+					if (SenderConnInfo)
 					{
 						XLogRecPtr	ptr;
 						TimeLineID	tli;
@@ -11404,8 +11331,8 @@ WaitForWALToBecomeAvailable(XLogRecPtr RecPtr, bool randAccess,
 									 tli, curFileTLI);
 						}
 						curFileTLI = tli;
-						RequestXLogStreaming(tli, ptr, PrimaryConnInfo,
-											 PrimarySlotName);
+						RequestXLogStreaming(tli, ptr, SenderConnInfo,
+										SenderSlotName);
 						receivedUpto = 0;
 					}
 
@@ -11733,14 +11660,15 @@ CheckForStandbyTrigger(void)
 		return true;
 	}
 
-	if (TriggerFile == NULL)
-		return false;
-
-	if (stat(TriggerFile, &stat_buf) == 0)
+	/*
+	 * Check for PromoteSignalFile... this is now a constant filename, with only the
+	 * filepath varying, so a user can still specify what they need.
+	 */
+	if (stat(PromoteSignalFile, &stat_buf) == 0)
 	{
 		ereport(LOG,
-				(errmsg("trigger file found: %s", TriggerFile)));
-		unlink(TriggerFile);
+				(errmsg("promote signal file found: %s", PromoteSignalFile)));
+		unlink(PromoteSignalFile);
 		triggered = true;
 		fast_promote = true;
 		return true;
@@ -11748,8 +11676,8 @@ CheckForStandbyTrigger(void)
 	else if (errno != ENOENT)
 		ereport(ERROR,
 				(errcode_for_file_access(),
-				 errmsg("could not stat trigger file \"%s\": %m",
-						TriggerFile)));
+				 errmsg("could not stat promote signal file \"%s\": %m",
+						PromoteSignalFile)));
 
 	return false;
 }
diff --git a/src/backend/access/transam/xlogfuncs.c b/src/backend/access/transam/xlogfuncs.c
index bc7253f..f23e131 100644
--- a/src/backend/access/transam/xlogfuncs.c
+++ b/src/backend/access/transam/xlogfuncs.c
@@ -328,10 +328,11 @@ pg_create_restore_point(PG_FUNCTION_ARGS)
 
 	restore_name_str = text_to_cstring(restore_name);
 
-	if (strlen(restore_name_str) >= MAXFNAMELEN)
+	if (strlen(restore_name_str) >= MAXRESTOREPOINTNAMELEN)
 		ereport(ERROR,
 				(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
-				 errmsg("value too long for restore point (maximum %d characters)", MAXFNAMELEN - 1)));
+				 errmsg("value too long for restore point (maximum %d characters)",
+							MAXRESTOREPOINTNAMELEN - 1)));
 
 	restorepoint = XLogRestorePoint(restore_name_str);
 
diff --git a/src/backend/postmaster/startup.c b/src/backend/postmaster/startup.c
index a7ae7e3..74b72c9 100644
--- a/src/backend/postmaster/startup.c
+++ b/src/backend/postmaster/startup.c
@@ -153,6 +153,7 @@ HandleStartupProcInterrupts(void)
 	{
 		got_SIGHUP = false;
 		ProcessConfigFile(PGC_SIGHUP);
+		validateRecoveryParameters();
 	}
 
 	/*
diff --git a/src/backend/replication/walreceiver.c b/src/backend/replication/walreceiver.c
index cc3cf7d..01f62b5 100644
--- a/src/backend/replication/walreceiver.c
+++ b/src/backend/replication/walreceiver.c
@@ -70,9 +70,11 @@
 
 
 /* GUC variables */
-int			wal_receiver_status_interval;
-int			wal_receiver_timeout;
-bool		hot_standby_feedback;
+int wal_receiver_status_interval;
+int wal_receiver_timeout;
+bool hot_standby_feedback;
+char *SenderConnInfo;
+char *SenderSlotName;
 
 /* libpqwalreceiver connection */
 static WalReceiverConn *wrconn = NULL;
@@ -415,9 +417,33 @@ WalReceiverMain(void)
 
 				if (got_SIGHUP)
 				{
+					char	*conninfo = pstrdup(SenderConnInfo);
+					char	*slotname = pstrdup(SenderSlotName);
+
 					got_SIGHUP = false;
 					ProcessConfigFile(PGC_SIGHUP);
 					XLogWalRcvSendHSFeedback(true);
+
+					/*
+					 * If sender_conninfo has been changed while walreceiver is running,
+					 * shut down walreceiver so that a new walreceiver is started and
+					 * initiates replication with the new connection information.
+					 */
+					if (strcmp(conninfo, SenderConnInfo) != 0)
+						ereport(FATAL,
+								(errcode(ERRCODE_ADMIN_SHUTDOWN),
+								 errmsg("closing replication connection because sender_conninfo was changed")));
+
+					/*
+					 * And the same for sender_slot_name.
+					 */
+					if (strcmp(slotname, SenderSlotName) != 0)
+						ereport(FATAL,
+								(errcode(ERRCODE_ADMIN_SHUTDOWN),
+								 errmsg("closing replication connection because sender_slot_name was changed")));
+
+					pfree(conninfo);
+					pfree(slotname);
 				}
 
 				/* See if we can read data immediately */
diff --git a/src/backend/utils/misc/guc.c b/src/backend/utils/misc/guc.c
index 946ba9e..b560e48 100644
--- a/src/backend/utils/misc/guc.c
+++ b/src/backend/utils/misc/guc.c
@@ -31,6 +31,7 @@
 #include "access/transam.h"
 #include "access/twophase.h"
 #include "access/xact.h"
+#include "access/xlog.h"
 #include "catalog/namespace.h"
 #include "commands/async.h"
 #include "commands/prepare.h"
@@ -77,6 +78,7 @@
 #include "utils/guc_tables.h"
 #include "utils/memutils.h"
 #include "utils/pg_locale.h"
+#include "utils/pg_lsn.h"
 #include "utils/plancache.h"
 #include "utils/portal.h"
 #include "utils/ps_status.h"
@@ -182,6 +184,15 @@ static void assign_application_name(const char *newval, void *extra);
 static bool check_cluster_name(char **newval, void **extra, GucSource source);
 static const char *show_unix_socket_permissions(void);
 static const char *show_log_file_mode(void);
+static bool check_recovery_target_type(char **newval, void **extra, GucSource source);
+static void assign_recovery_target_type(const char *newval, void *extra);
+static bool check_recovery_target_value(char **newval, void **extra, GucSource source);
+static void assign_recovery_target_value(const char *newval, void *extra);
+static bool check_recovery_target_timeline(char **newval, void **extra, GucSource source);
+static void assign_recovery_target_timeline(const char *newval, void *extra);
+static bool check_recovery_target_action(char **newval, void **extra, GucSource source);
+static void assign_recovery_target_action(const char *newval, void *extra);
+static bool check_sender_slot_name(char **newval, void **extra, GucSource source);
 
 /* Private functions in guc-file.l that need to be called from guc.c */
 static ConfigVariable *ProcessConfigFileInternal(GucContext context,
@@ -459,6 +470,9 @@ char	   *IdentFileName;
 char	   *external_pid_file;
 
 char	   *pgstat_temp_directory;
+char	   *signal_file_directory;
+char	*recovery_target_timeline_string;
+char	*recovery_target_action_string;
 
 char	   *application_name;
 
@@ -592,6 +606,10 @@ const char *const config_group_names[] =
 	gettext_noop("Write-Ahead Log / Checkpoints"),
 	/* WAL_ARCHIVING */
 	gettext_noop("Write-Ahead Log / Archiving"),
+	/* WAL_ARCHIVE_RECOVERY */
+	gettext_noop("Write-Ahead Log / Archive Recovery"),
+	/* WAL_RECOVERY_TARGET */
+	gettext_noop("Write-Ahead Log / Recovery Target"),
 	/* REPLICATION */
 	gettext_noop("Replication"),
 	/* REPLICATION_SENDING */
@@ -1549,6 +1567,16 @@ static struct config_bool ConfigureNamesBool[] =
 	},
 
 	{
+		{"recovery_target_inclusive", PGC_SIGHUP, WAL_RECOVERY_TARGET,
+			gettext_noop("Sets whether to include or exclude transaction with recovery target."),
+			NULL
+		},
+		&recoveryTargetInclusive,
+		true,
+		NULL, NULL, NULL
+	},
+
+	{
 		{"hot_standby", PGC_POSTMASTER, REPLICATION_STANDBY,
 			gettext_noop("Allows connections and queries during recovery."),
 			NULL
@@ -1789,8 +1817,19 @@ static struct config_int ConfigureNamesInt[] =
 	},
 
 	{
+		{"recovery_min_apply_delay", PGC_SIGHUP, REPLICATION_STANDBY,
+			gettext_noop("Sets the minimum delay to apply changes during recovery."),
+			NULL,
+			GUC_UNIT_MS
+		},
+		&recovery_min_apply_delay,
+		0, 0, INT_MAX,
+		NULL, NULL, NULL
+	},
+
+	{
 		{"wal_receiver_status_interval", PGC_SIGHUP, REPLICATION_STANDBY,
-			gettext_noop("Sets the maximum interval between WAL receiver status reports to the primary."),
+			gettext_noop("Sets the maximum interval between WAL receiver status reports to the sending server."),
 			NULL,
 			GUC_UNIT_S
 		},
@@ -1801,7 +1840,7 @@ static struct config_int ConfigureNamesInt[] =
 
 	{
 		{"wal_receiver_timeout", PGC_SIGHUP, REPLICATION_STANDBY,
-			gettext_noop("Sets the maximum wait time to receive data from the primary."),
+			gettext_noop("Sets the maximum wait time to receive data from the sending server."),
 			NULL,
 			GUC_UNIT_MS
 		},
@@ -2996,6 +3035,98 @@ static struct config_string ConfigureNamesString[] =
 	},
 
 	{
+		{"restore_command", PGC_SIGHUP, WAL_ARCHIVE_RECOVERY,
+			gettext_noop("Sets the shell command that will retrieve an archived WAL file."),
+			NULL
+		},
+		&recoveryRestoreCommand,
+		"",
+		NULL, NULL, NULL
+	},
+
+	{
+		{"archive_cleanup_command", PGC_SIGHUP, WAL_ARCHIVE_RECOVERY,
+			gettext_noop("Sets the shell command that will be executed at every restartpoint."),
+			NULL
+		},
+		&archiveCleanupCommand,
+		"",
+		NULL, NULL, NULL
+	},
+
+	{
+		{"recovery_end_command", PGC_SIGHUP, WAL_ARCHIVE_RECOVERY,
+			gettext_noop("Sets the shell command that will be executed once only at the end of recovery."),
+			NULL
+		},
+		&recoveryEndCommand,
+		"",
+		NULL, NULL, NULL
+	},
+
+	{
+		{"recovery_target_type", PGC_SIGHUP, WAL_RECOVERY_TARGET,
+			gettext_noop("Sets the type of desired recovery target."),
+			NULL
+		},
+		&recoveryTargetTypeString,
+		"",
+		check_recovery_target_type, assign_recovery_target_type, NULL
+	},
+
+	{
+		{"recovery_target_value", PGC_SIGHUP, WAL_RECOVERY_TARGET,
+			gettext_noop("Sets the value of the recovery taregt up to which recovery will proceed."),
+			NULL
+		},
+		&recoveryTargetValue,
+		"",
+		check_recovery_target_value, assign_recovery_target_value, NULL
+	},
+
+	{
+		{"recovery_target_timeline", PGC_SIGHUP, WAL_RECOVERY_TARGET,
+			gettext_noop("Sets recovering into a particular timeline."),
+			NULL
+		},
+		&recovery_target_timeline_string,
+		"",
+		check_recovery_target_timeline, assign_recovery_target_timeline, NULL
+	},
+
+	{
+		{"recovery_target_action", PGC_SIGHUP, WAL_RECOVERY_TARGET,
+			gettext_noop("Sets the action to perform upon reaching the recovery target."),
+			NULL
+		},
+		&recovery_target_action_string,
+		"",
+		check_recovery_target_action, assign_recovery_target_action, NULL
+	},
+
+	{
+		{"sender_conninfo", PGC_SIGHUP, REPLICATION_STANDBY,
+			gettext_noop("Sets the connection string to be used to connect with the sending server."),
+			NULL,
+			GUC_SUPERUSER_ONLY
+		},
+		&SenderConnInfo,
+		"",
+		NULL, NULL, NULL
+	},
+
+	{
+		{"sender_slot_name", PGC_SIGHUP, REPLICATION_STANDBY,
+			gettext_noop("Sets the name of the replication slot to use on the sending server."),
+			NULL,
+			GUC_SUPERUSER_ONLY
+		},
+		&SenderSlotName,
+		"",
+		check_sender_slot_name, NULL, NULL
+	},
+
+	{
 		{"client_encoding", PGC_USERSET, CLIENT_CONN_LOCALE,
 			gettext_noop("Sets the client's character set encoding."),
 			NULL,
@@ -3475,6 +3606,17 @@ static struct config_string ConfigureNamesString[] =
 	},
 
 	{
+		{"signal_file_directory", PGC_SIGHUP, WAL_ARCHIVE_RECOVERY,
+			gettext_noop("Signal files may be written to the specified directory."),
+			NULL,
+			GUC_SUPERUSER_ONLY
+		},
+		&signal_file_directory,
+		NULL,
+		check_canonical_path, NULL, NULL
+	},
+
+	{
 		{"stats_temp_directory", PGC_SIGHUP, STATS_COLLECTOR,
 			gettext_noop("Writes temporary statistics files to the specified directory."),
 			NULL,
@@ -10335,4 +10477,301 @@ show_log_file_mode(void)
 	return buf;
 }
 
+static bool
+check_recovery_target_type(char **newval, void **extra, GucSource source)
+{
+	RecoveryTargetType *myextra;
+	RecoveryTargetType rt = RECOVERY_TARGET_UNSET;
+
+	if (strcmp(*newval, "xid") == 0)
+		rt = RECOVERY_TARGET_XID;
+	else if (strcmp(*newval, "timestamp") == 0)
+		rt = RECOVERY_TARGET_TIME;
+	else if (strcmp(*newval, "name") == 0)
+		rt = RECOVERY_TARGET_NAME;
+	else if (strcmp(*newval, "lsn") == 0)
+		rt = RECOVERY_TARGET_LSN;
+	else if (strcmp(*newval, "immediate") == 0)
+		rt = RECOVERY_TARGET_IMMEDIATE;
+	else if (strcmp(*newval, "") != 0)
+	{
+		GUC_check_errdetail("recovery_target_type is not valid: \"%s\"", *newval);
+		return false;
+	}
+
+	myextra = (RecoveryTargetType *) guc_malloc(ERROR, sizeof(RecoveryTargetType));
+	*myextra = rt;
+	*extra = (void *) myextra;
+
+	return true;
+}
+
+static void
+assign_recovery_target_type(const char *newval, void *extra)
+{
+	recoveryTarget = *((RecoveryTargetType *) extra);
+}
+
+static bool
+check_recovery_target_value(char **newval, void **extra, GucSource source)
+{
+	bool valid = false;
+
+	/*
+	 * Value must be present in some cases, must not be present in others
+	 */
+	if (strcmp(*newval, "") == 0)
+	{
+		if (recoveryTarget == RECOVERY_TARGET_UNSET ||
+			recoveryTarget == RECOVERY_TARGET_IMMEDIATE)
+			valid = true;
+	}
+	else
+	{
+		if (recoveryTarget == RECOVERY_TARGET_UNSET ||
+			recoveryTarget == RECOVERY_TARGET_IMMEDIATE)
+			valid = false;
+		else
+			valid = true;
+	}
+
+	if (!valid)
+	{
+		GUC_check_errdetail("recovery_target_value is not valid: \"%s\"", *newval);
+		return false;
+	}
+
+	/*
+	 * We assume that recovery_target_type has already been parsed
+	 * since it sorts alphabetically before recovery_target_value.
+	 */
+	switch (recoveryTarget)
+	{
+		case RECOVERY_TARGET_UNSET:
+		case RECOVERY_TARGET_IMMEDIATE:
+			/* No value, so do nothing */
+			break;
+
+		case RECOVERY_TARGET_XID:
+			{
+				TransactionId	xid;
+				TransactionId	*myextra;
+
+				errno = 0;
+				xid = (TransactionId) strtoul(*newval, NULL, 0);
+				if (errno == EINVAL || errno == ERANGE)
+				{
+					GUC_check_errdetail("recovery_target_value is not a valid number: \"%s\"",
+								*newval);
+					return false;
+				}
+
+				myextra = (TransactionId *) guc_malloc(ERROR, sizeof(TransactionId));
+				*myextra = xid;
+				*extra = (void *) myextra;
+			}
+			break;
+
+		case RECOVERY_TARGET_TIME:
+			{
+				TimestampTz     time;
+				TimestampTz     *myextra;
+				MemoryContext oldcontext = CurrentMemoryContext;
+
+				PG_TRY();
+				{
+					time = DatumGetTimestampTz(DirectFunctionCall3(timestamptz_in,
+												CStringGetDatum(*newval),
+												ObjectIdGetDatum(InvalidOid),
+												Int32GetDatum(-1)));
+				}
+				PG_CATCH();
+				{
+					ErrorData  *edata;
+
+					/* Save error info */
+					MemoryContextSwitchTo(oldcontext);
+					edata = CopyErrorData();
+					FlushErrorState();
+
+					/* Pass the error message */
+					GUC_check_errdetail("%s", edata->message);
+					FreeErrorData(edata);
+					return false;
+				}
+				PG_END_TRY();
+
+				myextra = (TimestampTz *) guc_malloc(ERROR, sizeof(TimestampTz));
+				*myextra = time;
+				*extra = (void *) myextra;
+			}
+			break;
+
+		case RECOVERY_TARGET_NAME:
+			/* Use the value of newval directly */
+			if (strlen(*newval) > MAXRESTOREPOINTNAMELEN)
+			{
+				GUC_check_errdetail("recovery_target_value is too long (maximum %d characters)",
+									MAXRESTOREPOINTNAMELEN);
+				return false;
+			}
+			break;
+
+		case RECOVERY_TARGET_LSN:
+			{
+				XLogRecPtr	lsn;
+				XLogRecPtr	*myextra;
+				MemoryContext oldcontext = CurrentMemoryContext;
+
+				/*
+				 * Convert the LSN string given by the user to XLogRecPtr form.
+				 */
+				PG_TRY();
+				{
+					lsn =
+						DatumGetLSN(DirectFunctionCall3(pg_lsn_in,
+													CStringGetDatum(*newval),
+													ObjectIdGetDatum(InvalidOid),
+													Int32GetDatum(-1)));
+				}
+				PG_CATCH();
+				{
+					ErrorData  *edata;
+
+					/* Save error info */
+					MemoryContextSwitchTo(oldcontext);
+					edata = CopyErrorData();
+					FlushErrorState();
+
+					/* Pass the error message */
+					GUC_check_errdetail("%s", edata->message);
+					FreeErrorData(edata);
+					return false;
+				}
+				PG_END_TRY();
+
+				myextra = (XLogRecPtr *) guc_malloc(ERROR, sizeof(XLogRecPtr));
+				*myextra = lsn;
+				*extra = (void *) myextra;
+			}
+			break;
+	}
+
+	return true;
+}
+
+static void
+assign_recovery_target_value(const char *newval, void *extra)
+{
+	switch (recoveryTarget)
+	{
+		case RECOVERY_TARGET_UNSET:
+		case RECOVERY_TARGET_IMMEDIATE:
+			break;
+
+		case RECOVERY_TARGET_XID:
+			recoveryTargetXid = *((TransactionId *) extra);
+			break;
+
+		case RECOVERY_TARGET_TIME:
+			recoveryTargetTime = *((TimestampTz *) extra);
+			break;
+
+		case RECOVERY_TARGET_NAME:
+			if (newval && *newval)
+				recoveryTargetName = (char *) newval;
+			break;
+
+		case RECOVERY_TARGET_LSN:
+			recoveryTargetLSN = *((XLogRecPtr *) extra);
+			break;
+	}
+}
+
+static bool
+check_recovery_target_timeline(char **newval, void **extra, GucSource source)
+{
+	RecoveryTargetTimeLineGoal rttg = RECOVERY_TARGET_TIMELINE_CONTROLFILE;
+	RecoveryTargetTimeLineGoal *myextra;
+
+	if (strcmp(*newval, "latest") == 0)
+		rttg = RECOVERY_TARGET_TIMELINE_LATEST;
+	else if (strcmp(*newval, "controlfile") == 0 || strcmp(*newval, "") == 0)
+		rttg = RECOVERY_TARGET_TIMELINE_CONTROLFILE;
+	else
+	{
+		TimeLineID tli;
+
+		errno = 0;
+		tli = (TimeLineID) strtoul(*newval, NULL, 0);
+		if (errno == EINVAL || errno == ERANGE)
+		{
+			GUC_check_errdetail("recovery_target_timeline is not a valid number: \"%s\"",
+								*newval);
+			return false;
+		}
+		rttg = RECOVERY_TARGET_TIMELINE_NUMERIC;
+	}
+
+	myextra = (TimeLineID *) guc_malloc(ERROR, sizeof(TimeLineID));
+	*myextra = rttg;
+	*extra = (void *) myextra;
+
+	return true;
+}
+
+static void
+assign_recovery_target_timeline(const char *newval, void *extra)
+{
+	recoveryTargetTimeLineGoal = *((TimeLineID *) extra);
+	if (recoveryTargetTimeLineGoal == RECOVERY_TARGET_TIMELINE_NUMERIC)
+		recoveryTargetTLIRequested = (TimeLineID) strtoul(newval, NULL, 0);
+	else
+		recoveryTargetTLIRequested = 0;
+}
+
+static bool
+check_recovery_target_action(char **newval, void **extra, GucSource source)
+{
+	RecoveryTargetAction rta = RECOVERY_TARGET_ACTION_PAUSE;
+	RecoveryTargetAction *myextra;
+
+	if (strcmp(*newval, "pause") == 0)
+		rta = RECOVERY_TARGET_ACTION_PAUSE;
+	else if (strcmp(*newval, "promote") == 0)
+		rta = RECOVERY_TARGET_ACTION_PROMOTE;
+	else if (strcmp(*newval, "shutdown") == 0)
+		rta = RECOVERY_TARGET_ACTION_SHUTDOWN;
+	else if (strcmp(*newval, "") != 0)
+	{
+		GUC_check_errdetail("recovery_target_action is not valid: \"%s\"", *newval);
+		return false;
+	}
+
+	myextra = (RecoveryTargetAction *) guc_malloc(ERROR, sizeof(RecoveryTargetAction));
+	*myextra = rta;
+	*extra = (void *) myextra;
+
+	return true;
+}
+
+static void
+assign_recovery_target_action(const char *newval, void *extra)
+{
+	recoveryTargetAction = *((RecoveryTargetAction *) extra);
+}
+
+static bool
+check_sender_slot_name(char **newval, void **extra, GucSource source)
+{
+	if (strcmp(*newval,"") != 0 &&
+		!ReplicationSlotValidateName(*newval, WARNING))
+	{
+		GUC_check_errdetail("sender_slot_name is not valid: \"%s\"", *newval);
+		return false;
+	}
+
+	return true;
+}
+
 #include "guc-file.c"
diff --git a/src/backend/utils/misc/postgresql.conf.sample b/src/backend/utils/misc/postgresql.conf.sample
index ee8232f..2ee3bca 100644
--- a/src/backend/utils/misc/postgresql.conf.sample
+++ b/src/backend/utils/misc/postgresql.conf.sample
@@ -45,6 +45,9 @@
 #ident_file = 'ConfigDir/pg_ident.conf'	# ident configuration file
 					# (change requires restart)
 
+#signal_file_directory = 'ConfigDir' # signal files for recovery/replication
+					# (change requires restart)
+
 # If external_pid_file is not explicitly set, no extra PID file is written.
 #external_pid_file = ''			# write an extra PID file
 					# (change requires restart)
@@ -221,6 +224,26 @@
 #archive_timeout = 0		# force a logfile segment switch after this
 				# number of seconds; 0 disables
 
+# - Archive Recovery -
+#restore_command = ''		# command to use to restore an archived logfile segment
+				# placeholders: %p = path of file to restore
+				#               %f = file name only
+				# e.g. 'cp /mnt/server/archivedir/%f %p'
+#archive_cleanup_command = ''	# command to execute at every restartpoint
+#recovery_end_command = ''	# command to execute at completion of recovery
+
+# - Recovery Target -
+
+# Set these only when performing a targeted recovery
+
+#recovery_target_type=''	# 'xid', 'time', 'name', 'lsn',
+				# or 'immediate'
+#recovery_target_value=''	# value interpreted according to type
+#recovery_target_inclusive = on
+#recovery_target_timeline = ''	# unset means read from controlfile (default),
+				# or set to 'latest' or timeline ID
+#recovery_target_action = '' 	# 'pause', 'promote', 'shutdown'
+
 
 #------------------------------------------------------------------------------
 # REPLICATION
@@ -254,6 +277,8 @@
 
 # These settings are ignored on a master server.
 
+#sender_conninfo = ''			# connection string on sending server
+#sender_slot_name = ''			# connection slot on sending server
 #hot_standby = off			# "on" allows queries during recovery
 					# (change requires restart)
 #max_standby_archive_delay = 30s	# max delay before canceling queries
diff --git a/src/include/access/xlog.h b/src/include/access/xlog.h
index 7d21408..8b6dfca 100644
--- a/src/include/access/xlog.h
+++ b/src/include/access/xlog.h
@@ -87,6 +87,41 @@ typedef enum
 	RECOVERY_TARGET_IMMEDIATE
 } RecoveryTargetType;
 
+#define RecoveryTargetText(t) ( \
+	t == RECOVERY_TARGET_UNSET ? "unset" : ( \
+	t == RECOVERY_TARGET_XID   ? "xid" : ( \
+	t == RECOVERY_TARGET_TIME  ? "timestamp" : ( \
+	t == RECOVERY_TARGET_NAME  ? "name" : ( \
+	t == RECOVERY_TARGET_LSN   ? "lsn" : \
+					"immediate" )))))
+
+/*
+ * Recovery target action.
+ */
+typedef enum
+{
+	RECOVERY_TARGET_ACTION_PAUSE,
+	RECOVERY_TARGET_ACTION_PROMOTE,
+	RECOVERY_TARGET_ACTION_SHUTDOWN
+} RecoveryTargetAction;
+
+#define RecoveryTargetActionText(t) ( \
+	t == RECOVERY_TARGET_ACTION_PAUSE   ? "pause" : ( \
+	t == RECOVERY_TARGET_ACTION_PROMOTE ? "promote" : ( \
+						"shutdown" )))
+/*
+ * Recovery target TimeLine goal
+ */
+typedef enum
+{
+	RECOVERY_TARGET_TIMELINE_CONTROLFILE,
+	RECOVERY_TARGET_TIMELINE_LATEST,
+	RECOVERY_TARGET_TIMELINE_NUMERIC
+} RecoveryTargetTimeLineGoal;
+
+/* Max length of named restore points */
+#define MAXRESTOREPOINTNAMELEN 64
+
 extern XLogRecPtr ProcLastRecPtr;
 extern XLogRecPtr XactLastRecEnd;
 extern PGDLLIMPORT XLogRecPtr XactLastCommitEnd;
@@ -109,6 +144,36 @@ extern bool log_checkpoints;
 
 extern int	CheckPointSegments;
 
+/* options previously taken from recovery.conf for archive recovery */
+extern char *recoveryRestoreCommand;
+extern char *recoveryEndCommand;
+extern char *archiveCleanupCommand;
+extern char *recoveryTargetTypeString;
+extern RecoveryTargetType recoveryTarget;
+extern char *recoveryTargetValue;
+extern bool recoveryTargetInclusive;
+extern RecoveryTargetAction recoveryTargetAction;
+extern TransactionId recoveryTargetXid;
+extern TimestampTz recoveryTargetTime;
+extern char *recoveryTargetName;
+extern XLogRecPtr recoveryTargetLSN;
+extern int     recovery_min_apply_delay;
+
+/* option set locally in Startup process only when signal files exist */
+extern bool StandbyModeRequested;
+extern bool StandbyMode;
+
+/* options for WALreceiver.c */
+extern char *SenderConnInfo;
+extern char *SenderSlotName;
+
+extern char *signal_file_directory;
+
+extern char *recoveryTargetTLIString;
+extern RecoveryTargetTimeLineGoal recoveryTargetTimeLineGoal;
+extern TimeLineID recoveryTargetTLIRequested;
+extern TimeLineID recoveryTargetTLI;
+
 /* Archive modes */
 typedef enum ArchiveMode
 {
@@ -238,6 +303,7 @@ extern const char *xlog_identify(uint8 info);
 
 extern void issue_xlog_fsync(int fd, XLogSegNo segno);
 
+extern void validateRecoveryParameters(void);
 extern bool RecoveryInProgress(void);
 extern bool HotStandbyActive(void);
 extern bool HotStandbyActiveInReplay(void);
diff --git a/src/include/access/xlog_internal.h b/src/include/access/xlog_internal.h
index 05f996b..1a80377 100644
--- a/src/include/access/xlog_internal.h
+++ b/src/include/access/xlog_internal.h
@@ -246,16 +246,6 @@ typedef struct XLogRecData
 } XLogRecData;
 
 /*
- * Recovery target action.
- */
-typedef enum
-{
-	RECOVERY_TARGET_ACTION_PAUSE,
-	RECOVERY_TARGET_ACTION_PROMOTE,
-	RECOVERY_TARGET_ACTION_SHUTDOWN
-} RecoveryTargetAction;
-
-/*
  * Method table for resource managers.
  *
  * This struct must be kept in sync with the PG_RMGR definition in
diff --git a/src/include/utils/guc_tables.h b/src/include/utils/guc_tables.h
index b695f61..7f4389c 100644
--- a/src/include/utils/guc_tables.h
+++ b/src/include/utils/guc_tables.h
@@ -68,6 +68,8 @@ enum config_group
 	WAL_SETTINGS,
 	WAL_CHECKPOINTS,
 	WAL_ARCHIVING,
+	WAL_ARCHIVE_RECOVERY,
+	WAL_RECOVERY_TARGET,
 	REPLICATION,
 	REPLICATION_SENDING,
 	REPLICATION_MASTER,
diff --git a/src/test/perl/PostgresNode.pm b/src/test/perl/PostgresNode.pm
index c1b16ca..60ad78e 100644
--- a/src/test/perl/PostgresNode.pm
+++ b/src/test/perl/PostgresNode.pm
@@ -574,8 +574,6 @@ of a backup previously created on that node with $node->backup.
 
 Does not start the node after initializing it.
 
-A recovery.conf is not created.
-
 pg_hba.conf is configured to allow replication connections. Pass the keyword
 parameter hba_permit_replication => 0 to disable this.
 
@@ -745,13 +743,16 @@ sub enable_streaming
 	my ($self, $root_node) = @_;
 	my $root_connstr = $root_node->connstr;
 	my $name         = $self->name;
+	my $pgdata  = $self->data_dir;
 
 	print "### Enabling streaming replication for node \"$name\"\n";
 	$self->append_conf(
-		'recovery.conf', qq(
-primary_conninfo='$root_connstr application_name=$name'
-standby_mode=on
+		'postgresql.conf', qq(
+sender_conninfo='$root_connstr application_name=$name'
 ));
+	open my $standbysignal, ">>$pgdata/standby.signal";
+	print $standbysignal "\n# Allow replication (set up by PostgresNode.pm)\n";
+	close $standbysignal;
 }
 
 # Internal routine to enable archive recovery command on a standby node
@@ -760,6 +761,7 @@ sub enable_restoring
 	my ($self, $root_node) = @_;
 	my $path = $root_node->archive_dir;
 	my $name = $self->name;
+	my $pgdata  = $self->data_dir;
 
 	print "### Enabling WAL restore for node \"$name\"\n";
 
@@ -776,10 +778,12 @@ sub enable_restoring
 	  : qq{cp "$path/%f" "%p"};
 
 	$self->append_conf(
-		'recovery.conf', qq(
+		'postgresql.conf', qq(
 restore_command = '$copy_command'
-standby_mode = on
 ));
+	open my $recoverysignal, ">>$pgdata/recovery.signal";
+	print $recoverysignal "\n# Allow recovery (set up by PostgresNode.pm)\n";
+	close $recoverysignal;
 }
 
 # Internal routine to enable archiving
diff --git a/src/test/recovery/t/003_recovery_targets.pl b/src/test/recovery/t/003_recovery_targets.pl
index a82545b..236e651 100644
--- a/src/test/recovery/t/003_recovery_targets.pl
+++ b/src/test/recovery/t/003_recovery_targets.pl
@@ -3,7 +3,7 @@ use strict;
 use warnings;
 use PostgresNode;
 use TestLib;
-use Test::More tests => 9;
+use Test::More tests => 5;
 
 # Create and test a standby from given backup, with a certain
 # recovery target.
@@ -23,7 +23,7 @@ sub test_recovery_standby
 	foreach my $param_item (@$recovery_params)
 	{
 		$node_standby->append_conf(
-			'recovery.conf',
+			'postgresql.conf',
 			qq($param_item
 ));
 	}
@@ -100,47 +100,20 @@ $node_master->safe_psql('postgres',
 $node_master->safe_psql('postgres', "SELECT pg_switch_xlog()");
 
 # Test recovery targets
-my @recovery_params = ("recovery_target = 'immediate'");
+my @recovery_params = ("recovery_target_type = 'immediate'");
 test_recovery_standby('immediate target',
 	'standby_1', $node_master, \@recovery_params, "1000", $lsn1);
-@recovery_params = ("recovery_target_xid = '$recovery_txid'");
+@recovery_params = ("recovery_target_type = 'xid'", "recovery_target_value = '$recovery_txid'");
 test_recovery_standby('XID', 'standby_2', $node_master, \@recovery_params,
 	"2000", $lsn2);
-@recovery_params = ("recovery_target_time = '$recovery_time'");
+@recovery_params = ("recovery_target_type = 'timestamp'", "recovery_target_value = '$recovery_time'");
 test_recovery_standby('time', 'standby_3', $node_master, \@recovery_params,
 	"3000", $lsn3);
-@recovery_params = ("recovery_target_name = '$recovery_name'");
+@recovery_params = ("recovery_target_type = 'name'", "recovery_target_value = '$recovery_name'");
 test_recovery_standby('name', 'standby_4', $node_master, \@recovery_params,
 	"4000", $lsn4);
-@recovery_params = ("recovery_target_lsn = '$recovery_lsn'");
+@recovery_params = ("recovery_target_type = 'lsn'", "recovery_target_value = '$recovery_lsn'");
 test_recovery_standby('LSN', 'standby_5', $node_master, \@recovery_params,
 	"5000", $lsn5);
 
-# Multiple targets
-# Last entry has priority (note that an array respects the order of items
-# not hashes).
-@recovery_params = (
-	"recovery_target_name = '$recovery_name'",
-	"recovery_target_xid  = '$recovery_txid'",
-	"recovery_target_time = '$recovery_time'");
-test_recovery_standby('name + XID + time',
-	'standby_6', $node_master, \@recovery_params, "3000", $lsn3);
-@recovery_params = (
-	"recovery_target_time = '$recovery_time'",
-	"recovery_target_name = '$recovery_name'",
-	"recovery_target_xid  = '$recovery_txid'");
-test_recovery_standby('time + name + XID',
-	'standby_7', $node_master, \@recovery_params, "2000", $lsn2);
-@recovery_params = (
-	"recovery_target_xid  = '$recovery_txid'",
-	"recovery_target_time = '$recovery_time'",
-	"recovery_target_name = '$recovery_name'");
-test_recovery_standby('XID + time + name',
-	'standby_8', $node_master, \@recovery_params, "4000", $lsn4);
-@recovery_params = (
-	"recovery_target_xid  = '$recovery_txid'",
-	"recovery_target_time = '$recovery_time'",
-	"recovery_target_name = '$recovery_name'",
-	"recovery_target_lsn = '$recovery_lsn'",);
-test_recovery_standby('XID + time + name + LSN',
-	'standby_9', $node_master, \@recovery_params, "5000", $lsn5);
+# Tests for multiple targets no longer needed from 10.0 onwards
diff --git a/src/test/recovery/t/004_timeline_switch.pl b/src/test/recovery/t/004_timeline_switch.pl
index 5f3b2fe..d73ffa6 100644
--- a/src/test/recovery/t/004_timeline_switch.pl
+++ b/src/test/recovery/t/004_timeline_switch.pl
@@ -46,12 +46,10 @@ $node_master->teardown_node;
 $node_standby_1->promote;
 
 # Switch standby 2 to replay from standby 1
-rmtree($node_standby_2->data_dir . '/recovery.conf');
 my $connstr_1 = $node_standby_1->connstr;
 $node_standby_2->append_conf(
-	'recovery.conf', qq(
-primary_conninfo='$connstr_1'
-standby_mode=on
+	'postgresql.conf', qq(
+sender_conninfo='$connstr_1'
 recovery_target_timeline='latest'
 ));
 $node_standby_2->restart;
diff --git a/src/test/recovery/t/005_replay_delay.pl b/src/test/recovery/t/005_replay_delay.pl
index 640295b..ab87faf 100644
--- a/src/test/recovery/t/005_replay_delay.pl
+++ b/src/test/recovery/t/005_replay_delay.pl
@@ -25,7 +25,7 @@ my $delay        = 3;
 $node_standby->init_from_backup($node_master, $backup_name,
 	has_streaming => 1);
 $node_standby->append_conf(
-	'recovery.conf', qq(
+	'postgresql.conf', qq(
 recovery_min_apply_delay = '${delay}s'
 ));
 $node_standby->start;
#71Robert Haas
robertmhaas@gmail.com
In reply to: Simon Riggs (#70)
Re: Proposal for changes to recovery.conf API

On Sun, Jan 1, 2017 at 4:14 PM, Simon Riggs <simon@2ndquadrant.com> wrote:

Trying to fit recovery targets into one parameter was really not
feasible, though I tried.

What was the problem?

--
Robert Haas
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#72Simon Riggs
simon@2ndquadrant.com
In reply to: Robert Haas (#71)
Re: Proposal for changes to recovery.conf API

On 3 January 2017 at 15:50, Robert Haas <robertmhaas@gmail.com> wrote:

On Sun, Jan 1, 2017 at 4:14 PM, Simon Riggs <simon@2ndquadrant.com> wrote:

Trying to fit recovery targets into one parameter was really not
feasible, though I tried.

What was the problem?

There are 5 different parameters that affect the recovery target, 3 of
which are optional. That is much more complex than the two compulsory
parameters suggested when the proposal was made and in my view tips
the balance over the edge into pointlessness. Michael's suggestion for
2 parameters works well for this case.

--
Simon Riggs http://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#73Robert Haas
robertmhaas@gmail.com
In reply to: Simon Riggs (#72)
Re: Proposal for changes to recovery.conf API

On Tue, Jan 3, 2017 at 11:21 AM, Simon Riggs <simon@2ndquadrant.com> wrote:

On 3 January 2017 at 15:50, Robert Haas <robertmhaas@gmail.com> wrote:

On Sun, Jan 1, 2017 at 4:14 PM, Simon Riggs <simon@2ndquadrant.com> wrote:

Trying to fit recovery targets into one parameter was really not
feasible, though I tried.

What was the problem?

There are 5 different parameters that affect the recovery target, 3 of
which are optional. That is much more complex than the two compulsory
parameters suggested when the proposal was made and in my view tips
the balance over the edge into pointlessness. Michael's suggestion for
2 parameters works well for this case.

I still think merging recovery_target_type and recovery_target_value
into a single parameter could make sense, never mind the other three.
But I don't really want to argue about it any more.

--
Robert Haas
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#74Simon Riggs
simon@2ndquadrant.com
In reply to: Robert Haas (#73)
Re: Proposal for changes to recovery.conf API

On 3 January 2017 at 16:47, Robert Haas <robertmhaas@gmail.com> wrote:

On Tue, Jan 3, 2017 at 11:21 AM, Simon Riggs <simon@2ndquadrant.com> wrote:

On 3 January 2017 at 15:50, Robert Haas <robertmhaas@gmail.com> wrote:

On Sun, Jan 1, 2017 at 4:14 PM, Simon Riggs <simon@2ndquadrant.com> wrote:

Trying to fit recovery targets into one parameter was really not
feasible, though I tried.

What was the problem?

There are 5 different parameters that affect the recovery target, 3 of
which are optional. That is much more complex than the two compulsory
parameters suggested when the proposal was made and in my view tips
the balance over the edge into pointlessness. Michael's suggestion for
2 parameters works well for this case.

I still think merging recovery_target_type and recovery_target_value
into a single parameter could make sense, never mind the other three.
But I don't really want to argue about it any more.

We all agree that reducing number parameters made sense and would
remove weird quirks of multiple settings. When we were discussing the
idea of replacing them with just 1 parameter, that idea made sense to
me after I'd thought about it, but it would actually be 4 parameters.
I couldn't see a way to wave the corner case parameters away.

After more detailed thought I don't see the point of reducing the
number of parameters from to 4, especially if that reduction comes
with increased complexity for one of the parameters. Better to just
have 5 as Michael suggested.

This patch replaces the previous 8 parameters with just 5.

--
Simon Riggs http://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#75Josh Berkus
josh@agliodbs.com
In reply to: Bruce Momjian (#44)
Re: Proposal for changes to recovery.conf API

On 01/03/2017 08:47 AM, Robert Haas wrote:

On Tue, Jan 3, 2017 at 11:21 AM, Simon Riggs <simon@2ndquadrant.com> wrote:

On 3 January 2017 at 15:50, Robert Haas <robertmhaas@gmail.com> wrote:

On Sun, Jan 1, 2017 at 4:14 PM, Simon Riggs <simon@2ndquadrant.com> wrote:

Trying to fit recovery targets into one parameter was really not
feasible, though I tried.

What was the problem?

There are 5 different parameters that affect the recovery target, 3 of
which are optional. That is much more complex than the two compulsory
parameters suggested when the proposal was made and in my view tips
the balance over the edge into pointlessness. Michael's suggestion for
2 parameters works well for this case.

I still think merging recovery_target_type and recovery_target_value
into a single parameter could make sense, never mind the other three.
But I don't really want to argue about it any more.

Either solution works for me.

--
--
Josh Berkus
Red Hat OSAS
(any opinions are my own)

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#76Peter Eisentraut
peter.eisentraut@2ndquadrant.com
In reply to: Simon Riggs (#70)
Re: Proposal for changes to recovery.conf API

On 1/1/17 4:14 PM, Simon Riggs wrote:

OK, so here's the patch, plus doc cleanup patch.

I don't think this patch is likely to succeed if we throw in more ideas
in every round.

I think we have or are approaching agreement on moving recovery.conf
into postgresql.conf, making the settings reloadable, and adding signal
(formerly trigger) files to trigger recovery or standby. I think that
is a useful change, but it's already big enough and needs extensive
reviewing and testing.

All the other stuff, such as regrouping the recovery parameters,
removing the hot_standby setting, renaming the primary_* parameters,
making the directory of the signal files configurable, should be
separate patches that should be discussed separately. I think the
arguments for these latter changes are weaker, and tactically I would
focus on getting the recovery.conf move in before thinking about all the
other ones.

--
Peter Eisentraut http://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#77Fujii Masao
masao.fujii@gmail.com
In reply to: Simon Riggs (#70)
Re: Proposal for changes to recovery.conf API

On Mon, Jan 2, 2017 at 6:14 AM, Simon Riggs <simon@2ndquadrant.com> wrote:

On 20 December 2016 at 15:11, Simon Riggs <simon@2ndquadrant.com> wrote:

On 20 December 2016 at 15:03, Fujii Masao <masao.fujii@gmail.com> wrote:

API for crash recovery will never be changed. That is, crash recovery needs
neither recovery.trigger nor standby.trigger. When the server starts a crash
recovery without any trigger file, any recovery parameter settings in
postgresql.conf are ignored. Right?

Yes. There are no conceptual changes, just the API.

The goals are: visibility and reloading of recovery parameters,
removal of special case code.

OK, so here's the patch, plus doc cleanup patch.

Thanks for the patch!

5. recovery.conf parameters are all moved to postgresql.conf, with these changes

In current design of the patch, when recovery parameters are misconfigured
(e.g., set recovery_target_timeline to invalid timeline id) and
the configuration file is reloaded, the startup process emits FATAL error and
the server goes down. I don't think this is fine. Basically even
misconfiguration of the parameters should not cause the server crash.
If invalid settings are supplied, I think that we just should warn them
and ignore those new settings, like current other GUC is. Thought?

-        if (PrimaryConnInfo == NULL && recoveryRestoreCommand == NULL)
+        if (SenderConnInfo == NULL && recoveryRestoreCommand == NULL)

Seems "SenderConnInfo == NULL" should be changed to "SenderConnInfo[0] == '\0'".
recoveryRestoreCommand, as well.

Regards,

--
Fujii Masao

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#78Fujii Masao
masao.fujii@gmail.com
In reply to: Peter Eisentraut (#76)
Re: Proposal for changes to recovery.conf API

On Tue, Jan 10, 2017 at 4:50 AM, Peter Eisentraut
<peter.eisentraut@2ndquadrant.com> wrote:

On 1/1/17 4:14 PM, Simon Riggs wrote:

OK, so here's the patch, plus doc cleanup patch.

I don't think this patch is likely to succeed if we throw in more ideas
in every round.

I think we have or are approaching agreement on moving recovery.conf
into postgresql.conf, making the settings reloadable, and adding signal
(formerly trigger) files to trigger recovery or standby. I think that
is a useful change, but it's already big enough and needs extensive
reviewing and testing.

Agreed. It's better to focus on the "core" part of the patch firstly.

Regards,

--
Fujii Masao

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#79Simon Riggs
simon@2ndquadrant.com
In reply to: Peter Eisentraut (#76)
Re: Proposal for changes to recovery.conf API

On 9 January 2017 at 19:50, Peter Eisentraut
<peter.eisentraut@2ndquadrant.com> wrote:

On 1/1/17 4:14 PM, Simon Riggs wrote:

OK, so here's the patch, plus doc cleanup patch.

I don't think this patch is likely to succeed if we throw in more ideas
in every round.

I think we have or are approaching agreement on moving recovery.conf
into postgresql.conf, making the settings reloadable, and adding signal
(formerly trigger) files to trigger recovery or standby. I think that
is a useful change, but it's already big enough and needs extensive
reviewing and testing.

All the other stuff, such as regrouping the recovery parameters,
removing the hot_standby setting, renaming the primary_* parameters,
making the directory of the signal files configurable, should be
separate patches that should be discussed separately. I think the
arguments for these latter changes are weaker, and tactically I would
focus on getting the recovery.conf move in before thinking about all the
other ones.

Thanks for the review.

* Removing hot_standby setting is not included in this patch; happy to
keep that separate; very low priority

* Renaming primary_* parameters - Currently we use this config setting
even when connecting to a standby, so the parameter is confusingly
named, so 10.0 is a good chance to name it correctly. Will submit as
separate patch.

* Directory for signal files was in my understanding a primary goal of
the patch. I am happy to remove that into a later submission. That
resolves, for now, the issue with pg_basebackup -R.

The above changes are fairly minor.

The main area of "design doubt" remains the implementation of the
recovery_target parameter set. Are we happy with the user interface
choices in the patch, given the understanding that the situation was
more comple than at first thought?

--
Simon Riggs http://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#80Simon Riggs
simon@2ndquadrant.com
In reply to: Fujii Masao (#77)
Re: Proposal for changes to recovery.conf API

On 11 January 2017 at 09:51, Fujii Masao <masao.fujii@gmail.com> wrote:

5. recovery.conf parameters are all moved to postgresql.conf, with these changes

In current design of the patch, when recovery parameters are misconfigured
(e.g., set recovery_target_timeline to invalid timeline id) and
the configuration file is reloaded, the startup process emits FATAL error and
the server goes down. I don't think this is fine. Basically even
misconfiguration of the parameters should not cause the server crash.
If invalid settings are supplied, I think that we just should warn them
and ignore those new settings, like current other GUC is. Thought?

Thanks for your comments.

The specification of the recovery target parameters should be different, IMHO.

If the user is performing a recovery and the target of the recovery is
incorrectly specified then it is clear that the recovery cannot
continue with an imprecisely specified target. So in my understanding
we would need to either

1) issue a WARNING and pause recovery

2) issue an ERROR (which becomes FATAL in Startup process) and exit recovery

My view would be 2) is the most useful, though I am willing to hear
other points and/or go with majority view

--
Simon Riggs http://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#81Michael Paquier
michael.paquier@gmail.com
In reply to: Simon Riggs (#80)
Re: Proposal for changes to recovery.conf API

On Wed, Jan 11, 2017 at 7:36 PM, Simon Riggs <simon@2ndquadrant.com> wrote:

The specification of the recovery target parameters should be different, IMHO.

If the user is performing a recovery and the target of the recovery is
incorrectly specified then it is clear that the recovery cannot
continue with an imprecisely specified target. So in my understanding
we would need to either

1) issue a WARNING and pause recovery

2) issue an ERROR (which becomes FATAL in Startup process) and exit recovery

My view would be 2) is the most useful, though I am willing to hear
other points and/or go with majority view

I agree with that. 2) is more consistent with what is in core now.
--
Michael

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#82Peter Eisentraut
peter.eisentraut@2ndquadrant.com
In reply to: Simon Riggs (#79)
Re: Proposal for changes to recovery.conf API

On 1/11/17 5:27 AM, Simon Riggs wrote:

* Renaming primary_* parameters - Currently we use this config setting
even when connecting to a standby, so the parameter is confusingly
named, so 10.0 is a good chance to name it correctly. Will submit as
separate patch.

I don't subscribe to the idea that 10.0 is a better chance to change
something than any other time.

I agree that the naming has become inaccurate, but it still matches the
basic use case (one primary, one standby (heck, standby is also
inaccurate now!)), and I don't recall anyone being confused by this.
Also, it is debatable whether "sender" is better. Yes, it's a sender,
but sending what and to whom?

* Directory for signal files was in my understanding a primary goal of
the patch. I am happy to remove that into a later submission. That
resolves, for now, the issue with pg_basebackup -R.

I think the issue was that some people didn't want configuration files
in the data directory. By removing recovery.conf we accomplish that.
Signal/trigger files are not configuration (or at least it's much easier
to argue that), so I think having them in the data directory is fine.

I'm concerned that having signal files somewhere else opens up a bunch
more edge cases that need to be considered. For example, what if
someone puts a signal file into a temporary directory that is cleared
after a server crash and restart. That can mess up a bunch of things.

(I think I like trigger better than signal, btw. A signal is something
asynchronous.)

--
Peter Eisentraut http://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#83Simon Riggs
simon@2ndquadrant.com
In reply to: Peter Eisentraut (#82)
Re: Proposal for changes to recovery.conf API

Having already agreed to remove the two mentioned aspects, I'm just
replying to fill in some historical details.

On 11 January 2017 at 17:25, Peter Eisentraut
<peter.eisentraut@2ndquadrant.com> wrote:

On 1/11/17 5:27 AM, Simon Riggs wrote:

* Renaming primary_* parameters - Currently we use this config setting
even when connecting to a standby, so the parameter is confusingly
named, so 10.0 is a good chance to name it correctly. Will submit as
separate patch.

I don't subscribe to the idea that 10.0 is a better chance to change
something than any other time.

I agree that the naming has become inaccurate, but it still matches the
basic use case (one primary, one standby (heck, standby is also
inaccurate now!)), and I don't recall anyone being confused by this.
Also, it is debatable whether "sender" is better. Yes, it's a sender,
but sending what and to whom?

Sending server is the term already used to describe a server that is
either a master or a relaying standby.

But as already requested, I will remove from patch.

* Directory for signal files was in my understanding a primary goal of
the patch. I am happy to remove that into a later submission. That
resolves, for now, the issue with pg_basebackup -R.

I think the issue was that some people didn't want configuration files
in the data directory. By removing recovery.conf we accomplish that.
Signal/trigger files are not configuration (or at least it's much easier
to argue that), so I think having them in the data directory is fine.

There were a considerable number of people that pushed to make the
data directory non-user writable, which is where the signal directory
came from.

I can see the argument, but since those that spoke previously have
evaporated, I'm easy.

I'm concerned that having signal files somewhere else opens up a bunch
more edge cases that need to be considered. For example, what if
someone puts a signal file into a temporary directory that is cleared
after a server crash and restart. That can mess up a bunch of things.

We already have trigger_file as a way to specify an alternate
directory, so if I remove signal_file_directory I will need to replace
trigger_file as a parameter.

(I think I like trigger better than signal, btw. A signal is something
asynchronous.)

The code already calls them signal files, its just called trigger externally.

--
Simon Riggs http://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#84Peter Eisentraut
peter.eisentraut@2ndquadrant.com
In reply to: Simon Riggs (#83)
Re: Proposal for changes to recovery.conf API

On 1/11/17 12:53 PM, Simon Riggs wrote:

I'm concerned that having signal files somewhere else opens up a bunch
more edge cases that need to be considered. For example, what if
someone puts a signal file into a temporary directory that is cleared
after a server crash and restart. That can mess up a bunch of things.

We already have trigger_file as a way to specify an alternate
directory, so if I remove signal_file_directory I will need to replace
trigger_file as a parameter.

The trigger file is for promotion, which is just a momentary nudge to
the server. The recovery signal file or whatever we end up calling it
is long-term state, because if it disappears and the server restarts, it
will do something different. So they are different.

I'm not strictly opposed to making the name or directory configurable,
but I'm predicting it will raise more questions than we might want to
deal with.

--
Peter Eisentraut http://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#85Fujii Masao
masao.fujii@gmail.com
In reply to: Simon Riggs (#80)
Re: Proposal for changes to recovery.conf API

On Wed, Jan 11, 2017 at 7:36 PM, Simon Riggs <simon@2ndquadrant.com> wrote:

On 11 January 2017 at 09:51, Fujii Masao <masao.fujii@gmail.com> wrote:

5. recovery.conf parameters are all moved to postgresql.conf, with these changes

In current design of the patch, when recovery parameters are misconfigured
(e.g., set recovery_target_timeline to invalid timeline id) and
the configuration file is reloaded, the startup process emits FATAL error and
the server goes down. I don't think this is fine. Basically even
misconfiguration of the parameters should not cause the server crash.
If invalid settings are supplied, I think that we just should warn them
and ignore those new settings, like current other GUC is. Thought?

Thanks for your comments.

The specification of the recovery target parameters should be different, IMHO.

If the user is performing a recovery and the target of the recovery is
incorrectly specified then it is clear that the recovery cannot
continue with an imprecisely specified target.

Could you tell me what case of "the target of the recovery is incorrectly
specified" are you thinking of? For example, you're thinking the case where
invalid format of timestamp value is specified in recovery_target_time?
In this case, I think that we should issue a WARNING and ignore that invalid
setting when the configuration file is reloaded. Of course, if it's the server
startup time, such invalid setting should prevent the server from starting up.

Regarding recovery_target_timeline, isn't it better to mark that parameter
as PGC_POSTMASTER? I'm not sure if we really want to change the recovery
target timeline without server restart and also not sure if that operation is
safe.

Regards,

--
Fujii Masao

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#86Simon Riggs
simon@2ndquadrant.com
In reply to: Fujii Masao (#85)
Re: Proposal for changes to recovery.conf API

On 12 January 2017 at 05:49, Fujii Masao <masao.fujii@gmail.com> wrote:

On Wed, Jan 11, 2017 at 7:36 PM, Simon Riggs <simon@2ndquadrant.com> wrote:

On 11 January 2017 at 09:51, Fujii Masao <masao.fujii@gmail.com> wrote:

5. recovery.conf parameters are all moved to postgresql.conf, with these changes

In current design of the patch, when recovery parameters are misconfigured
(e.g., set recovery_target_timeline to invalid timeline id) and
the configuration file is reloaded, the startup process emits FATAL error and
the server goes down. I don't think this is fine. Basically even
misconfiguration of the parameters should not cause the server crash.
If invalid settings are supplied, I think that we just should warn them
and ignore those new settings, like current other GUC is. Thought?

Thanks for your comments.

The specification of the recovery target parameters should be different, IMHO.

If the user is performing a recovery and the target of the recovery is
incorrectly specified then it is clear that the recovery cannot
continue with an imprecisely specified target.

Could you tell me what case of "the target of the recovery is incorrectly
specified" are you thinking of? For example, you're thinking the case where
invalid format of timestamp value is specified in recovery_target_time?
In this case, I think that we should issue a WARNING and ignore that invalid
setting when the configuration file is reloaded. Of course, if it's the server
startup time, such invalid setting should prevent the server from starting up.

Regarding recovery_target_timeline, isn't it better to mark that parameter
as PGC_POSTMASTER? I'm not sure if we really want to change the recovery
target timeline without server restart and also not sure if that operation is
safe.

It's one of the main objectives of the patch to allow user to reset
parameters online so I don't think we should set PGC_POSTMASTER.

Of course, if the user provides an incorrect parameter value for
search, we have no way to understand their intentions. But we do know
they wanted to reset the parameter, so we should not continue using
the old parameter.

I understand your wish to throw a WARNING and "stay up", but in the
context of a recovery the whole purpose of starting the server is to
recover. If we are unable to do that because the user has failed to
set the parameter correctly then we should throw ERROR.

--
Simon Riggs http://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#87Peter Eisentraut
peter.eisentraut@2ndquadrant.com
In reply to: Simon Riggs (#79)
Re: Proposal for changes to recovery.conf API

On 1/11/17 5:27 AM, Simon Riggs wrote:

The main area of "design doubt" remains the implementation of the
recovery_target parameter set. Are we happy with the user interface
choices in the patch, given the understanding that the situation was
more comple than at first thought?

Could you summarize the current proposal(s)?

Personally, I don't immediately see the need to change anything from the
parameter names that I currently see in recovery.conf.sample.

--
Peter Eisentraut http://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#88Fujii Masao
masao.fujii@gmail.com
In reply to: Simon Riggs (#86)
Re: Proposal for changes to recovery.conf API

On Thu, Jan 12, 2017 at 6:46 PM, Simon Riggs <simon@2ndquadrant.com> wrote:

On 12 January 2017 at 05:49, Fujii Masao <masao.fujii@gmail.com> wrote:

On Wed, Jan 11, 2017 at 7:36 PM, Simon Riggs <simon@2ndquadrant.com> wrote:

On 11 January 2017 at 09:51, Fujii Masao <masao.fujii@gmail.com> wrote:

5. recovery.conf parameters are all moved to postgresql.conf, with these changes

In current design of the patch, when recovery parameters are misconfigured
(e.g., set recovery_target_timeline to invalid timeline id) and
the configuration file is reloaded, the startup process emits FATAL error and
the server goes down. I don't think this is fine. Basically even
misconfiguration of the parameters should not cause the server crash.
If invalid settings are supplied, I think that we just should warn them
and ignore those new settings, like current other GUC is. Thought?

Thanks for your comments.

The specification of the recovery target parameters should be different, IMHO.

If the user is performing a recovery and the target of the recovery is
incorrectly specified then it is clear that the recovery cannot
continue with an imprecisely specified target.

Could you tell me what case of "the target of the recovery is incorrectly
specified" are you thinking of? For example, you're thinking the case where
invalid format of timestamp value is specified in recovery_target_time?
In this case, I think that we should issue a WARNING and ignore that invalid
setting when the configuration file is reloaded. Of course, if it's the server
startup time, such invalid setting should prevent the server from starting up.

Regarding recovery_target_timeline, isn't it better to mark that parameter
as PGC_POSTMASTER? I'm not sure if we really want to change the recovery
target timeline without server restart and also not sure if that operation is
safe.

It's one of the main objectives of the patch to allow user to reset
parameters online so I don't think we should set PGC_POSTMASTER.

I'm OK to mark the recovery target parameters *except* recovery_target_timeline
as PGC_SIGHUP. But, as I told upthread, I'm wondering whether there is really
the use case where the target timeline needs to be changed online. Also I'm
wondering whether that online change of target timeline is actually safe.

Regards,

--
Fujii Masao

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#89Simon Riggs
simon@2ndquadrant.com
In reply to: Peter Eisentraut (#87)
1 attachment(s)
Re: Proposal for changes to recovery.conf API

On 12 January 2017 at 13:34, Peter Eisentraut
<peter.eisentraut@2ndquadrant.com> wrote:

On 1/11/17 5:27 AM, Simon Riggs wrote:

The main area of "design doubt" remains the implementation of the
recovery_target parameter set. Are we happy with the user interface
choices in the patch, given the understanding that the situation was
more comple than at first thought?

Could you summarize the current proposal(s)?

Personally, I don't immediately see the need to change anything from the
parameter names that I currently see in recovery.conf.sample.

New patch version implementing everything you requested, incl docs and
tap tests.

The patch as offered here is what I've been asked to do by everybody
as well as I can do it. I'm very happy to receive comments and to
rework the design based upon further feedback.

I'm not completely convinced this is a great design, so I'm happy to
hear input. pg_basebackup -R is the main wrinkle.

The timeline handling has a bug at present that I'm working on, but
I'm not worried it constitutes a major problem. Obviously it will be
fixed before commit, but the patch needs more discussion
now/yesterday.

All parameters are set at PGC_POSTMASTER for now.

--
Simon Riggs http://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services

Attachments:

newRecoveryAPI.v102f.patchapplication/octet-stream; name=newRecoveryAPI.v102f.patchDownload
diff --git a/doc/src/sgml/recovery-config.sgml b/doc/src/sgml/recovery-config.sgml
index a91864b..1841947 100644
--- a/doc/src/sgml/recovery-config.sgml
+++ b/doc/src/sgml/recovery-config.sgml
@@ -11,23 +11,44 @@
 
    <para>
     This chapter describes the settings available in the
-    <filename>recovery.conf</><indexterm><primary>recovery.conf</></>
-    file. They apply only for the duration of the
+    <filename>postgresql.conf</><indexterm><primary>postgresql.conf</></>
+    file that apply only for the duration of the
     recovery.  They must be reset for any subsequent recovery you wish to
-    perform.  They cannot be changed once recovery has begun.
+    perform.
    </para>
 
    <para>
-     Settings in <filename>recovery.conf</> are specified in the format
-     <literal>name = 'value'</>. One parameter is specified per line.
-     Hash marks (<literal>#</literal>) designate the rest of the
-     line as a comment.  To embed a single quote in a parameter
-     value, write two quotes (<literal>''</>).
+    The database server can also be started "in recovery" a term that covers
+    using the server as a standby or for executing a targeted recovery. Typically
+    standby mode would be used to provide high availability and/or read
+    scalability, whereas a targeted recovery is used to recover from data loss.
    </para>
 
    <para>
-    A sample file, <filename>share/recovery.conf.sample</>,
-    is provided in the installation's <filename>share/</> directory.
+    To start the server in standby mode create a zero-length file
+    called <filename>standby.signal</><indexterm><primary>standby.signal</></>
+    in the data directory. The server will enter recovery and
+    will not stop recovery when the end of archived WAL is reached, but
+    will keep trying to continue recovery by connecting to the sending server as
+    specified by the <varname>primary_conninfo</> setting and/or by
+    fetching new WAL segments using <varname>restore_command</>
+    In this mode you may use parameters
+    in both <xref linkend="archive-recovery-settings"> and
+    <xref linkend="standby-settings"> sections. Parameters from
+    <xref linkend="recovery-target-settings"> will not be used.
+   </para>
+
+   <para>
+    To start the server in targeted recovery create a zero-length file called
+    <filename>recovery.signal</><indexterm><primary>recovery.signal</></>
+    in the data directory.
+    If both <filename>standby.signal</> and <filename>recovery.signal</> files are
+    created, standby mode takes precedence. Targeted recovery mode will end when
+    end of archived WAL is reached, or when <varname>recovery_target</> is reached.
+    In this mode you may use parameters from both
+    <xref linkend="archive-recovery-settings"> and
+    <xref linkend="recovery-target-settings"> sections. Parameters from
+    <xref linkend="standby-settings"> will not be used.
    </para>
 
   <sect1 id="archive-recovery-settings">
@@ -35,6 +56,13 @@
     <title>Archive Recovery Settings</title>
      <variablelist>
 
+     <para>
+      Parameter settings may be changed in <filename>postgresql.conf</filename> or
+      by executing the <command>ALTER SYSTEM</command>. Changes will take some
+      time to take effect, so changes made while not in paused state may not
+      have the desired effect in all cases.
+     </para>
+
      <varlistentry id="restore-command" xreflabel="restore_command">
       <term><varname>restore_command</varname> (<type>string</type>)
       <indexterm>
@@ -45,7 +73,7 @@
        <para>
         The local shell command to execute to retrieve an archived segment of
         the WAL file series. This parameter is required for archive recovery,
-        but optional for streaming replication.
+        but optional for standby mode.
         Any <literal>%f</> in the string is
         replaced by the name of the file to retrieve from the archive,
         and any <literal>%p</> is replaced by the copy destination path name
@@ -154,99 +182,91 @@ restore_command = 'copy "C:\\server\\archivedir\\%f" "%p"'  # Windows
     <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_lsn</>, <varname>recovery_target_name</>,
-      <varname>recovery_target_time</>, or <varname>recovery_target_xid</>
-      can be used; if more than one of these is specified in the configuration
-      file, the last entry will be used.
+      By default, recovery will recover to the end of the WAL log. An earlier
+      stopping point may be specified using <varname>recovery_target_type</>
+      and in most cases also <varname>recovery_target_value</>, plus the optional
+      parameters <varname>recovery_target_inclusive</>,
+      <varname>recovery_targeti_timeline</> and <varname>recovery_target_action</>.
+     </para>
+
+     <para>
+      Parameter settings may be changed in <filename>postgresql.conf</filename> or
+      by executing the <command>ALTER SYSTEM</command>. Changes will take some
+      time to take effect, so changes made while not in paused state may not
+      have the desired effect in all cases.
      </para>
 
      <variablelist>
-     <varlistentry id="recovery-target" xreflabel="recovery_target">
-      <term><varname>recovery_target</varname><literal> = 'immediate'</literal>
+     <varlistentry id="recovery-target-type" xreflabel="recovery_target_type">
+      <term><varname>recovery_target_type</varname> (<type>enum</type>)
       <indexterm>
-        <primary><varname>recovery_target</> recovery parameter</primary>
+        <primary><varname>recovery_target_type</> targeted recovery parameter</primary>
       </indexterm>
       </term>
       <listitem>
        <para>
-        This parameter specifies that recovery should end as soon as a
-        consistent state is reached, i.e. as early as possible. When restoring
-        from an online backup, this means the point where taking the backup
-        ended.
+        <varname>recovery_target_type</varname> specifies the search criteria used for a
+        a targeted recovery. The default value is <literal>none</literal>. Valid
+        values are <literal>none</literal>, <literal>immediate</literal>,
+        <literal>name</literal>, <literal>timestamp</literal>,
+        <literal>xid</literal> and <literal>lsn</literal>.
        </para>
+
        <para>
-        Technically, this is a string parameter, but <literal>'immediate'</>
-        is currently the only allowed value.
+        Target-type <literal>none</literal> specifies that recovery will not stop
+        until it runs out of WAL, which is the default setting. When not in targeted
+        recovery this is the only meaningful setting.
        </para>
-      </listitem>
-     </varlistentry>
 
-     <varlistentry id="recovery-target-name" xreflabel="recovery_target_name">
-      <term><varname>recovery_target_name</varname> (<type>string</type>)
-      <indexterm>
-        <primary><varname>recovery_target_name</> recovery parameter</primary>
-      </indexterm>
-      </term>
-      <listitem>
        <para>
-        This parameter specifies the named restore point (created with
-        <function>pg_create_restore_point()</>) to which recovery will proceed.
+        Target-type <literal>immediate</literal> specifies that recovery should end as
+        soon as a consistent state is reached, i.e. as early as possible. When restoring
+        from an online backup, this means the point where taking the backup ended.
+        For this target-type, no additional target-specifiers influence the stopping
+        point.
        </para>
-      </listitem>
-     </varlistentry>
 
-     <varlistentry id="recovery-target-time" xreflabel="recovery_target_time">
-      <term><varname>recovery_target_time</varname> (<type>timestamp</type>)
-      <indexterm>
-        <primary><varname>recovery_target_time</> recovery parameter</primary>
-      </indexterm>
-      </term>
-      <listitem>
        <para>
-        This parameter specifies the time stamp up to which recovery
-        will proceed.
+        Target-type <literal>name</literal> specifies that recovery will
+        proceed to the point specified by <varname>recovery_target_value</varname>
+        when interpreted as the name of a restore point created with
+        <function>pg_create_restore_point()</>.
         The precise stopping point is also influenced by
-        <xref linkend="recovery-target-inclusive">.
+        <xref linkend="recovery-target-inclusive"> and
+        <xref linkend="recovery-target-timeline">.
+       </para>
+
+       <para>
+        Target-type <literal>timestamp</literal> specifies that recovery will
+        proceed to the point specified by <varname>recovery_target_value</varname>
+        when interpreted as the timestamp of a transaction commit or abort.
+        The precise stopping point is also influenced by
+        <xref linkend="recovery-target-inclusive"> and
+        <xref linkend="recovery-target-timeline">.
        </para>
-      </listitem>
-     </varlistentry>
 
-     <varlistentry id="recovery-target-xid" xreflabel="recovery_target_xid">
-      <term><varname>recovery_target_xid</varname> (<type>string</type>)
-      <indexterm>
-        <primary><varname>recovery_target_xid</> recovery parameter</primary>
-      </indexterm>
-      </term>
-      <listitem>
        <para>
-        This parameter specifies the transaction ID up to which recovery
-        will proceed. Keep in mind
+        Target-type <literal>xid</literal> specifies that recovery will
+        proceed to the point specified by <varname>recovery_target_value</varname>
+        when interpreted as the transaction ID of a transaction commit or abort.
+        Keep in mind
         that while transaction IDs are assigned sequentially at transaction
         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.
         The precise stopping point is also influenced by
-        <xref linkend="recovery-target-inclusive">.
+        <xref linkend="recovery-target-inclusive"> and
+        <xref linkend="recovery-target-timeline">.
        </para>
-      </listitem>
-     </varlistentry>
 
-     <varlistentry id="recovery-target-lsn" xreflabel="recovery_target_lsn">
-      <term><varname>recovery_target_lsn</varname> (<type>pg_lsn</type>)
-      <indexterm>
-        <primary><varname>recovery_target_lsn</> recovery parameter</primary>
-      </indexterm>
-      </term>
-      <listitem>
        <para>
-        This parameter specifies the LSN of the transaction log location up
-        to which recovery will proceed. The precise stopping point is also
-        influenced by <xref linkend="recovery-target-inclusive">. This
-        parameter is parsed using the system data type
-        <link linkend="datatype-pg-lsn"><type>pg_lsn</></link>.
+        Target-type <literal>lsn</literal> specifies that recovery will
+        proceed to the point specified by <varname>recovery_target_value</varname>
+        when interpreted as the LSN of any WAL record, parsed using the system
+        data type <link linkend="datatype-pg-lsn"><type>pg_lsn</></link>.
+        The precise stopping point is also influenced by
+        <xref linkend="recovery-target-inclusive"> and
+        <xref linkend="recovery-target-timeline">.
        </para>
       </listitem>
      </varlistentry>
@@ -258,6 +278,23 @@ restore_command = 'copy "C:\\server\\archivedir\\%f" "%p"'  # Windows
      </para>
 
      <variablelist>
+     <varlistentry id="recovery-target-value"
+                   xreflabel="recovery_target_value">
+      <term><varname>recovery_target_value</varname> (<type>string</type>)
+      <indexterm>
+        <primary><varname>recovery_target_value</> targeted recovery search parameter</primary>
+      </indexterm>
+      </term>
+      <listitem>
+       <para>
+        Specifies the stopping point for targeted recovery. The string value
+        is interpreted according to strict rules according to the value of
+        <varname>recovery_target_type</varname>. An empty string may be an
+        invalid value in some cases.
+       </para>
+      </listitem>
+     <variablelist>
+
      <varlistentry id="recovery-target-inclusive"
                    xreflabel="recovery_target_inclusive">
       <term><varname>recovery_target_inclusive</varname> (<type>boolean</type>)
@@ -357,24 +394,11 @@ restore_command = 'copy "C:\\server\\archivedir\\%f" "%p"'  # Windows
     <title>Standby Server Settings</title>
      <variablelist>
 
-       <varlistentry id="standby-mode" xreflabel="standby_mode">
-        <term><varname>standby_mode</varname> (<type>boolean</type>)
-        <indexterm>
-          <primary><varname>standby_mode</> recovery parameter</primary>
-        </indexterm>
-        </term>
-        <listitem>
-         <para>
-          Specifies whether to start the <productname>PostgreSQL</> server as
-          a standby. If this parameter is <literal>on</>, the server will
-          not stop recovery when the end of archived WAL is reached, but
-          will keep trying to continue recovery by fetching new WAL segments
-          using <varname>restore_command</>
-          and/or by connecting to the primary server as specified by the
-          <varname>primary_conninfo</> setting.
-         </para>
-        </listitem>
-       </varlistentry>
+     <para>
+      Parameter settings may be changed only at server start, though later
+      patches may add this capability.
+     </para>
+
        <varlistentry id="primary-conninfo" xreflabel="primary_conninfo">
         <term><varname>primary_conninfo</varname> (<type>string</type>)
         <indexterm>
@@ -384,7 +408,7 @@ restore_command = 'copy "C:\\server\\archivedir\\%f" "%p"'  # Windows
         <listitem>
          <para>
           Specifies a connection string to be used for the standby server
-          to connect with the primary. This string is in the format
+          to connect with a sending server. This string is in the format
           described in <xref linkend="libpq-connstring">. If any option is
           unspecified in this string, then the corresponding environment
           variable (see <xref linkend="libpq-envars">) is checked. If the
@@ -393,12 +417,12 @@ restore_command = 'copy "C:\\server\\archivedir\\%f" "%p"'  # Windows
          </para>
          <para>
           The connection string should specify the host name (or address)
-          of the primary server, as well as the port number if it is not
+          of the sending server, as well as the port number if it is not
           the same as the standby server's default.
           Also specify a user name corresponding to a suitably-privileged role
-          on the primary (see
+          on the sending server (see
           <xref linkend="streaming-replication-authentication">).
-          A password needs to be provided too, if the primary demands password
+          A password needs to be provided too, if the sender demands password
           authentication.  It can be provided in the
           <varname>primary_conninfo</varname> string, or in a separate
           <filename>~/.pgpass</> file on the standby server (use
@@ -411,6 +435,7 @@ restore_command = 'copy "C:\\server\\archivedir\\%f" "%p"'  # Windows
          </para>
         </listitem>
        </varlistentry>
+
        <varlistentry id="primary-slot-name" xreflabel="primary_slot_name">
         <term><varname>primary_slot_name</varname> (<type>string</type>)
         <indexterm>
@@ -420,7 +445,7 @@ restore_command = 'copy "C:\\server\\archivedir\\%f" "%p"'  # Windows
         <listitem>
          <para>
           Optionally specifies an existing replication slot to be used when
-          connecting to the primary via streaming replication to control
+          connecting to the sending server via streaming replication to control
           resource removal on the upstream node
           (see <xref linkend="streaming-replication-slots">).
           This setting has no effect if <varname>primary_conninfo</> is not
@@ -428,21 +453,6 @@ restore_command = 'copy "C:\\server\\archivedir\\%f" "%p"'  # Windows
          </para>
         </listitem>
        </varlistentry>
-       <varlistentry id="trigger-file" xreflabel="trigger_file">
-        <term><varname>trigger_file</varname> (<type>string</type>)
-        <indexterm>
-          <primary><varname>trigger_file</> recovery parameter</primary>
-        </indexterm>
-        </term>
-        <listitem>
-         <para>
-          Specifies a trigger file whose presence ends recovery in the
-          standby.  Even if this value is not set, you can still promote
-          the standby using <command>pg_ctl promote</>.
-          This setting has no effect if <varname>standby_mode</> is <literal>off</>.
-         </para>
-        </listitem>
-       </varlistentry>
 
      <varlistentry id="recovery-min-apply-delay" xreflabel="recovery_min_apply_delay">
       <term><varname>recovery_min_apply_delay</varname> (<type>integer</type>)
@@ -453,7 +463,7 @@ restore_command = 'copy "C:\\server\\archivedir\\%f" "%p"'  # Windows
       <listitem>
        <para>
         By default, a standby server restores WAL records from the
-        primary as soon as possible. It may be useful to have a time-delayed
+        sending server as soon as possible. It may be useful to have a time-delayed
         copy of the data, offering opportunities to correct data loss errors.
         This parameter allows you to delay recovery by a fixed period of time,
         measured in milliseconds if no unit is specified.  For example, if
diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c
index 5016273..25a2092 100644
--- a/src/backend/access/transam/xlog.c
+++ b/src/backend/access/transam/xlog.c
@@ -80,8 +80,10 @@ extern uint32 bootstrap_data_checksum_version;
 /* File path names (all relative to $PGDATA) */
 #define RECOVERY_COMMAND_FILE	"recovery.conf"
 #define RECOVERY_COMMAND_DONE	"recovery.done"
-#define PROMOTE_SIGNAL_FILE		"promote"
-#define FALLBACK_PROMOTE_SIGNAL_FILE "fallback_promote"
+#define PROMOTE_SIGNAL_FILE	"promote.signal"
+#define FALLBACK_PROMOTE_SIGNAL_FILE "fallback_promote.signal"
+#define RECOVERY_SIGNAL_FILE	"recovery.signal"
+#define STANDBY_SIGNAL_FILE	"standby.signal"
 
 
 /* User-settable parameters */
@@ -233,7 +235,7 @@ static int	LocalXLogInsertAllowed = -1;
 
 /*
  * When ArchiveRecoveryRequested is set, archive recovery was requested,
- * ie. recovery.conf file was present. When InArchiveRecovery is set, we are
+ * ie. signal files were present. When InArchiveRecovery is set, we are
  * currently recovering using offline XLOG archives. These variables are only
  * valid in the startup process.
  *
@@ -245,6 +247,9 @@ static int	LocalXLogInsertAllowed = -1;
 bool		ArchiveRecoveryRequested = false;
 bool		InArchiveRecovery = false;
 
+static bool   standby_signal_file_found = false;
+static bool   recovery_signal_file_found = false;
+
 /* Was the last xlog file restored from archive, or local? */
 static bool restoredFromArchive = false;
 
@@ -252,29 +257,35 @@ static bool restoredFromArchive = false;
 static char *replay_image_masked = NULL;
 static char *master_image_masked = NULL;
 
-/* options taken from recovery.conf for archive recovery */
-char	   *recoveryRestoreCommand = NULL;
-static char *recoveryEndCommand = NULL;
-static char *archiveCleanupCommand = NULL;
-static RecoveryTargetType recoveryTarget = RECOVERY_TARGET_UNSET;
-static bool recoveryTargetInclusive = true;
-static RecoveryTargetAction recoveryTargetAction = RECOVERY_TARGET_ACTION_PAUSE;
-static TransactionId recoveryTargetXid;
-static TimestampTz recoveryTargetTime;
-static char *recoveryTargetName;
-static XLogRecPtr recoveryTargetLSN;
-static int	recovery_min_apply_delay = 0;
-static TimestampTz recoveryDelayUntilTime;
-
-/* options taken from recovery.conf for XLOG streaming */
-static bool StandbyModeRequested = false;
-static char *PrimaryConnInfo = NULL;
-static char *PrimarySlotName = NULL;
-static char *TriggerFile = NULL;
+/* options formerly taken from recovery.conf for archive recovery */
+char *recoveryRestoreCommand = NULL;
+char *recoveryEndCommand = NULL;
+char *archiveCleanupCommand = NULL;
+RecoveryTargetType recoveryTarget = RECOVERY_TARGET_UNSET;
+char *recoveryTargetTypeString;
+char *recoveryTargetValue = NULL;
+bool recoveryTargetInclusive = true;
+RecoveryTargetAction recoveryTargetAction = RECOVERY_TARGET_ACTION_PAUSE;
+TransactionId recoveryTargetXid;
+TimestampTz recoveryTargetTime;
+char *recoveryTargetName;
+XLogRecPtr recoveryTargetLSN;
+int	recovery_min_apply_delay = 0;
+TimestampTz recoveryDelayUntilTime;
+
+/* options formerly taken from recovery.conf for XLOG streaming */
+bool StandbyModeRequested = false;
+char *PrimaryConnInfo = NULL;
+char *PrimarySlotName = NULL;
+char *TriggerFile = NULL;
 
 /* are we currently in standby mode? */
 bool		StandbyMode = false;
 
+char PromoteSignalFile[MAXPGPATH];
+char RecoverySignalFile[MAXPGPATH];
+char StandbySignalFile[MAXPGPATH];
+
 /* whether request for fast promotion has been made yet */
 static bool fast_promote = false;
 
@@ -296,7 +307,11 @@ static bool recoveryStopAfter;
  * the currently-scanned WAL record was generated).  We also need these
  * timeline values:
  *
- * recoveryTargetTLI: the desired timeline that we want to end in.
+ * recoveryTargetTimeLineGoal: what the user requested, if any
+ *
+ * recoveryTargetTLIRequested: numeric value of requested timeline, if constant
+ *
+ * recoveryTargetTLI: the currently understood target timeline; changes
  *
  * recoveryTargetIsLatest: was the requested target timeline 'latest'?
  *
@@ -312,7 +327,10 @@ static bool recoveryStopAfter;
  * file was created.)  During a sequential scan we do not allow this value
  * to decrease.
  */
-static TimeLineID recoveryTargetTLI;
+char *recoveryTargetTLIString = NULL;
+RecoveryTargetTimeLineGoal recoveryTargetTimeLineGoal = RECOVERY_TARGET_TIMELINE_CONTROLFILE;
+TimeLineID recoveryTargetTLIRequested = 0;
+TimeLineID recoveryTargetTLI = 0;
 static bool recoveryTargetIsLatest = false;
 static List *expectedTLEs;
 static TimeLineID curFileTLI;
@@ -624,12 +642,6 @@ typedef struct XLogCtlData
 	TimeLineID	PrevTimeLineID;
 
 	/*
-	 * archiveCleanupCommand is read from recovery.conf but needs to be in
-	 * shared memory so that the checkpointer process can access it.
-	 */
-	char		archiveCleanupCommand[MAXPGPATH];
-
-	/*
 	 * SharedRecoveryInProgress indicates if we're still in crash or archive
 	 * recovery.  Protected by info_lck.
 	 */
@@ -833,7 +845,7 @@ static bool holdingAllLocks = false;
 static MemoryContext walDebugCxt = NULL;
 #endif
 
-static void readRecoveryCommandFile(void);
+static void readRecoverySignalFile(void);
 static void exitArchiveRecovery(TimeLineID endTLI, XLogRecPtr endOfLog);
 static bool recoveryStopsBefore(XLogReaderState *record);
 static bool recoveryStopsAfter(XLogReaderState *record);
@@ -5083,251 +5095,182 @@ str_time(pg_time_t tnow)
 
 /*
  * See if there is a recovery command file (recovery.conf), and if so
- * read in parameters for archive recovery and XLOG streaming.
+ * throw an ERROR since as of PG10.0 we no longer recognize that.
+ *
+ * See if there are any recovery signal files and if so, set state for
+ * recovery.
  *
- * The file is parsed using the main configuration parser.
+ *   !!!! NOTE !!!!
+ * There is a discrepancy in the design here: pg_basebackup -R expects
+ * to be able to write a file containing backend parameters. To allow
+ * that the server reads parameters from a file called
+ * recovery.auto.conf in the data directory. So one might ask why we
+ * allow that and yet disallow the use of recovery.conf. I have no
+ * explanation for that, only what you see is what was requested.
  */
 static void
-readRecoveryCommandFile(void)
+readRecoverySignalFile(void)
 {
-	FILE	   *fd;
-	TimeLineID	rtli = 0;
-	bool		rtliGiven = false;
-	ConfigVariable *item,
-			   *head = NULL,
-			   *tail = NULL;
-	bool		recoveryTargetActionSet = false;
+	struct stat stat_buf;
 
+	if (IsBootstrapProcessingMode())
+		return;
 
-	fd = AllocateFile(RECOVERY_COMMAND_FILE, "r");
-	if (fd == NULL)
-	{
-		if (errno == ENOENT)
-			return;				/* not there, so no archive recovery */
+	/*
+	 * Set paths for named signal files
+	 */
+	snprintf(StandbySignalFile, MAXPGPATH, "%s", STANDBY_SIGNAL_FILE);
+	snprintf(RecoverySignalFile, MAXPGPATH, "%s", RECOVERY_SIGNAL_FILE);
+	snprintf(PromoteSignalFile, MAXPGPATH, "%s", PROMOTE_SIGNAL_FILE);
+
+	/*
+	 * Check for old recovery API file: recovery.conf
+	 */
+	if (stat(RECOVERY_COMMAND_FILE, &stat_buf) == 0)
 		ereport(FATAL,
-				(errcode_for_file_access(),
-				 errmsg("could not open recovery command file \"%s\": %m",
+			(errcode_for_file_access(),
+			 errmsg("deprecated API using recovery command file \"%s\"",
 						RECOVERY_COMMAND_FILE)));
-	}
+	/*
+	 * Remove unused .done file, if present. Ignore if absent.
+	 */
+	unlink(RECOVERY_COMMAND_DONE);
 
 	/*
-	 * Since we're asking ParseConfigFp() to report errors as FATAL, there's
-	 * no need to check the return value.
+	 * Check for recovery signal files and if found, fsync them
+	 * since they represent server state information.
+	 *
+	 * If present, standby signal file takes precedence.
+	 * If neither is present then we won't enter archive recovery.
 	 */
-	(void) ParseConfigFp(fd, RECOVERY_COMMAND_FILE, 0, FATAL, &head, &tail);
+	if (stat(StandbySignalFile, &stat_buf) == 0)
+	{
+		int         fd;
 
-	FreeFile(fd);
+		fd = BasicOpenFile(StandbySignalFile, O_RDWR | PG_BINARY | get_sync_bit(sync_method),
+							S_IRUSR | S_IWUSR);
+		pg_fsync(fd);
+		close(fd);
+		standby_signal_file_found = true;
+	}
+	else if (stat(RecoverySignalFile, &stat_buf) == 0)
+	{
+		int         fd;
 
-	for (item = head; item; item = item->next)
+		fd = BasicOpenFile(RecoverySignalFile, O_RDWR | PG_BINARY | get_sync_bit(sync_method),
+							S_IRUSR | S_IWUSR);
+		pg_fsync(fd);
+		close(fd);
+		recovery_signal_file_found = true;
+	}
+
+	StandbyModeRequested = false;
+	ArchiveRecoveryRequested = false;
+	if (standby_signal_file_found)
 	{
-		if (strcmp(item->name, "restore_command") == 0)
-		{
-			recoveryRestoreCommand = pstrdup(item->value);
-			ereport(DEBUG2,
-					(errmsg_internal("restore_command = '%s'",
-									 recoveryRestoreCommand)));
-		}
-		else if (strcmp(item->name, "recovery_end_command") == 0)
-		{
-			recoveryEndCommand = pstrdup(item->value);
-			ereport(DEBUG2,
-					(errmsg_internal("recovery_end_command = '%s'",
-									 recoveryEndCommand)));
-		}
-		else if (strcmp(item->name, "archive_cleanup_command") == 0)
-		{
-			archiveCleanupCommand = pstrdup(item->value);
-			ereport(DEBUG2,
-					(errmsg_internal("archive_cleanup_command = '%s'",
-									 archiveCleanupCommand)));
-		}
-		else if (strcmp(item->name, "recovery_target_action") == 0)
-		{
-			if (strcmp(item->value, "pause") == 0)
-				recoveryTargetAction = RECOVERY_TARGET_ACTION_PAUSE;
-			else if (strcmp(item->value, "promote") == 0)
-				recoveryTargetAction = RECOVERY_TARGET_ACTION_PROMOTE;
-			else if (strcmp(item->value, "shutdown") == 0)
-				recoveryTargetAction = RECOVERY_TARGET_ACTION_SHUTDOWN;
-			else
-				ereport(ERROR,
-						(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
-				errmsg("invalid value for recovery parameter \"%s\": \"%s\"",
-					   "recovery_target_action",
-					   item->value),
-						 errhint("Valid values are \"pause\", \"promote\", and \"shutdown\".")));
+		StandbyModeRequested = true;
+		ArchiveRecoveryRequested = true;
+	}
+	else if (recovery_signal_file_found)
+	{
+		StandbyModeRequested = false;
+		ArchiveRecoveryRequested = true;
+	}
+	else
+		return;
 
-			ereport(DEBUG2,
-					(errmsg_internal("recovery_target_action = '%s'",
-									 item->value)));
+	/*
+	 * We don't support standby_mode in standalone backends; that requires
+	 * other processes such as the WAL receiver to be alive.
+	 */
+	if (StandbyModeRequested && !IsUnderPostmaster)
+		ereport(FATAL,
+				(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+			errmsg("standby mode is not supported by single-user servers")));
 
-			recoveryTargetActionSet = true;
-		}
-		else if (strcmp(item->name, "recovery_target_timeline") == 0)
-		{
-			rtliGiven = true;
-			if (strcmp(item->value, "latest") == 0)
-				rtli = 0;
-			else
-			{
-				errno = 0;
-				rtli = (TimeLineID) strtoul(item->value, NULL, 0);
-				if (errno == EINVAL || errno == ERANGE)
-					ereport(FATAL,
-							(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
-							 errmsg("recovery_target_timeline is not a valid number: \"%s\"",
-									item->value)));
-			}
-			if (rtli)
-				ereport(DEBUG2,
-				   (errmsg_internal("recovery_target_timeline = %u", rtli)));
-			else
-				ereport(DEBUG2,
-					 (errmsg_internal("recovery_target_timeline = latest")));
-		}
-		else if (strcmp(item->name, "recovery_target_xid") == 0)
-		{
-			errno = 0;
-			recoveryTargetXid = (TransactionId) strtoul(item->value, NULL, 0);
-			if (errno == EINVAL || errno == ERANGE)
-				ereport(FATAL,
-						(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
-				  errmsg("recovery_target_xid is not a valid number: \"%s\"",
-						 item->value)));
-			ereport(DEBUG2,
-					(errmsg_internal("recovery_target_xid = %u",
-									 recoveryTargetXid)));
-			recoveryTarget = RECOVERY_TARGET_XID;
-		}
-		else if (strcmp(item->name, "recovery_target_time") == 0)
-		{
-			recoveryTarget = RECOVERY_TARGET_TIME;
+	logRecoveryParameters();
+	validateRecoveryParameters();
+}
 
-			/*
-			 * Convert the time string given by the user to TimestampTz form.
-			 */
-			recoveryTargetTime =
-				DatumGetTimestampTz(DirectFunctionCall3(timestamptz_in,
-												CStringGetDatum(item->value),
-												ObjectIdGetDatum(InvalidOid),
-														Int32GetDatum(-1)));
-			ereport(DEBUG2,
-					(errmsg_internal("recovery_target_time = '%s'",
-								   timestamptz_to_str(recoveryTargetTime))));
-		}
-		else if (strcmp(item->name, "recovery_target_name") == 0)
-		{
-			recoveryTarget = RECOVERY_TARGET_NAME;
+void
+logRecoveryParameters(void)
+{
+	int	normal_log_level = LOG; //DEBUG2;
 
-			recoveryTargetName = pstrdup(item->value);
-			if (strlen(recoveryTargetName) >= MAXFNAMELEN)
-				ereport(FATAL,
-						(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
-						 errmsg("recovery_target_name is too long (maximum %d characters)",
-								MAXFNAMELEN - 1)));
+	/*
+	 * Log messages for recovery parameters at server start
+	 */
+	ereport(normal_log_level,
+		(errmsg_internal("standby_mode = '%s'", (StandbyModeRequested ? "on " : "off"))));
 
-			ereport(DEBUG2,
-					(errmsg_internal("recovery_target_name = '%s'",
-									 recoveryTargetName)));
-		}
-		else if (strcmp(item->name, "recovery_target_lsn") == 0)
-		{
-			recoveryTarget = RECOVERY_TARGET_LSN;
+	if (recoveryRestoreCommand != NULL)
+		ereport(normal_log_level,
+			(errmsg_internal("restore_command = '%s'", recoveryRestoreCommand)));
 
-			/*
-			 * Convert the LSN string given by the user to XLogRecPtr form.
-			 */
-			recoveryTargetLSN =
-				DatumGetLSN(DirectFunctionCall3(pg_lsn_in,
-												CStringGetDatum(item->value),
-												ObjectIdGetDatum(InvalidOid),
-														Int32GetDatum(-1)));
-			ereport(DEBUG2,
-					(errmsg_internal("recovery_target_lsn = '%X/%X'",
-									 (uint32) (recoveryTargetLSN >> 32),
-									 (uint32) recoveryTargetLSN)));
-		}
-		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 value for recovery parameter \"%s\": \"%s\"",
-					   "recovery_target",
-					   item->value),
-					   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)
-		{
-			/*
-			 * does nothing if a recovery_target is not also set
-			 */
-			if (!parse_bool(item->value, &recoveryTargetInclusive))
-				ereport(ERROR,
-						(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
-						 errmsg("parameter \"%s\" requires a Boolean value",
-								"recovery_target_inclusive")));
-			ereport(DEBUG2,
-					(errmsg_internal("recovery_target_inclusive = %s",
-									 item->value)));
-		}
-		else if (strcmp(item->name, "standby_mode") == 0)
-		{
-			if (!parse_bool(item->value, &StandbyModeRequested))
-				ereport(ERROR,
-						(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
-						 errmsg("parameter \"%s\" requires a Boolean value",
-								"standby_mode")));
-			ereport(DEBUG2,
-					(errmsg_internal("standby_mode = '%s'", item->value)));
-		}
-		else if (strcmp(item->name, "primary_conninfo") == 0)
-		{
-			PrimaryConnInfo = pstrdup(item->value);
-			ereport(DEBUG2,
-					(errmsg_internal("primary_conninfo = '%s'",
-									 PrimaryConnInfo)));
-		}
-		else if (strcmp(item->name, "primary_slot_name") == 0)
-		{
-			ReplicationSlotValidateName(item->value, ERROR);
-			PrimarySlotName = pstrdup(item->value);
-			ereport(DEBUG2,
-					(errmsg_internal("primary_slot_name = '%s'",
-									 PrimarySlotName)));
-		}
-		else if (strcmp(item->name, "trigger_file") == 0)
+	if (recoveryEndCommand != NULL)
+		ereport(normal_log_level,
+			(errmsg_internal("recovery_end_command = '%s'", recoveryEndCommand)));
+
+	if (archiveCleanupCommand != NULL)
+		ereport(normal_log_level,
+			(errmsg_internal("archive_cleanup_command = '%s'", archiveCleanupCommand)));
+
+	if (PrimaryConnInfo != NULL)
+		ereport(normal_log_level,
+			(errmsg_internal("primary_conninfo = '%s'", PrimaryConnInfo)));
+
+	if (PrimarySlotName != NULL)
+		ereport(normal_log_level,
+			(errmsg_internal("primary_slot_name = '%s'", PrimarySlotName)));
+
+	if (recovery_min_apply_delay > 0)
+		ereport(normal_log_level,
+			(errmsg_internal("recovery_min_apply_delay = '%u'", recovery_min_apply_delay)));
+
+	/*
+	 * Check details for recovery target, if any
+	 */
+	if (recoveryTarget > RECOVERY_TARGET_UNSET)
+	{
+		ereport(normal_log_level,
+			(errmsg_internal("recovery_target_type = '%s'", RecoveryTargetText(recoveryTarget))));
+		if (recoveryTargetValue != NULL)
+			ereport(normal_log_level,
+				(errmsg_internal("recovery_target_value = '%s'", recoveryTargetValue)));
+		ereport(normal_log_level,
+			(errmsg_internal("recovery_target_inclusive = '%s'", (recoveryTargetInclusive ? "on " : "off"))));
+
+		if (recoveryTargetTimeLineGoal == RECOVERY_TARGET_TIMELINE_CONTROLFILE)
+			ereport(normal_log_level,
+				(errmsg_internal("recovery_target_timeline = '%u' (from controlfile)",
+					recoveryTargetTLI)));
+		else if (recoveryTargetTimeLineGoal == RECOVERY_TARGET_TIMELINE_LATEST)
+			ereport(normal_log_level,
+				(errmsg_internal("recovery_target_timeline = 'latest'")));
+		else
 		{
-			TriggerFile = pstrdup(item->value);
-			ereport(DEBUG2,
-					(errmsg_internal("trigger_file = '%s'",
-									 TriggerFile)));
+			Assert(recoveryTargetTimeLineGoal == RECOVERY_TARGET_TIMELINE_NUMERIC);
+			ereport(normal_log_level,
+				(errmsg_internal("recovery_target_timeline = '%u'",
+					recoveryTargetTLIRequested)));
 		}
-		else if (strcmp(item->name, "recovery_min_apply_delay") == 0)
-		{
-			const char *hintmsg;
+	}
 
-			if (!parse_int(item->value, &recovery_min_apply_delay, GUC_UNIT_MS,
-						   &hintmsg))
-				ereport(ERROR,
-						(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
-						 errmsg("parameter \"%s\" requires a temporal value",
-								"recovery_min_apply_delay"),
-						 hintmsg ? errhint("%s", _(hintmsg)) : 0));
-			ereport(DEBUG2,
-					(errmsg_internal("recovery_min_apply_delay = '%s'", item->value)));
-		}
-		else
+	ereport(normal_log_level,
+		(errmsg_internal("recovery_target_action = '%s'", RecoveryTargetActionText(recoveryTargetAction))));
+}
+
+void
+validateRecoveryParameters(void)
+{
+	if (!ArchiveRecoveryRequested)
+		return;
+
+	if (recoveryTarget > RECOVERY_TARGET_UNSET &&
+		recoveryTargetValue == NULL)
 			ereport(FATAL,
 					(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
-					 errmsg("unrecognized recovery parameter \"%s\"",
-							item->name)));
-	}
+					 errmsg("must specify recovery_target_value when recovery_target_type is set")));
 
 	/*
 	 * Check for compulsory parameters
@@ -5336,8 +5279,7 @@ readRecoveryCommandFile(void)
 	{
 		if (PrimaryConnInfo == NULL && recoveryRestoreCommand == NULL)
 			ereport(WARNING,
-					(errmsg("recovery command file \"%s\" specified neither primary_conninfo nor restore_command",
-							RECOVERY_COMMAND_FILE),
+					(errmsg("specified neither primary_conninfo nor restore_command"),
 					 errhint("The database server will regularly poll the pg_wal subdirectory to check for files placed there.")));
 	}
 	else
@@ -5345,8 +5287,7 @@ readRecoveryCommandFile(void)
 		if (recoveryRestoreCommand == NULL)
 			ereport(FATAL,
 					(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
-					 errmsg("recovery command file \"%s\" must specify restore_command when standby mode is not enabled",
-							RECOVERY_COMMAND_FILE)));
+					 errmsg("must specify restore_command when standby mode is not enabled")));
 	}
 
 	/*
@@ -5355,50 +5296,46 @@ readRecoveryCommandFile(void)
 	 * hot_standby = off, which was surprising behaviour.
 	 */
 	if (recoveryTargetAction == RECOVERY_TARGET_ACTION_PAUSE &&
-		recoveryTargetActionSet &&
 		!EnableHotStandby)
 		recoveryTargetAction = RECOVERY_TARGET_ACTION_SHUTDOWN;
 
 	/*
-	 * We don't support standby_mode in standalone backends; that requires
-	 * other processes such as the WAL receiver to be alive.
-	 */
-	if (StandbyModeRequested && !IsUnderPostmaster)
-		ereport(FATAL,
-				(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
-			errmsg("standby mode is not supported by single-user servers")));
-
-	/* Enable fetching from archive recovery area */
-	ArchiveRecoveryRequested = true;
-
-	/*
 	 * If user specified recovery_target_timeline, validate it or compute the
 	 * "latest" value.  We can't do this until after we've gotten the restore
 	 * command and set InArchiveRecovery, because we need to fetch timeline
 	 * history files from the archive.
 	 */
-	if (rtliGiven)
+	if (recoveryTargetTimeLineGoal == RECOVERY_TARGET_TIMELINE_NUMERIC)
 	{
-		if (rtli)
-		{
-			/* Timeline 1 does not have a history file, all else should */
-			if (rtli != 1 && !existsTimeLineHistory(rtli))
-				ereport(FATAL,
-						(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
-						 errmsg("recovery target timeline %u does not exist",
-								rtli)));
-			recoveryTargetTLI = rtli;
-			recoveryTargetIsLatest = false;
-		}
-		else
-		{
-			/* We start the "latest" search from pg_control's timeline */
-			recoveryTargetTLI = findNewestTimeLine(recoveryTargetTLI);
-			recoveryTargetIsLatest = true;
-		}
-	}
+		TimeLineID rtli = recoveryTargetTLIRequested;
+
+		/* Timeline 1 does not have a history file, all else should */
+		if (rtli != 1 && !existsTimeLineHistory(rtli))
+			ereport(FATAL,
+					(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+					 errmsg("recovery target timeline %u does not exist",
+							rtli)));
+		recoveryTargetTLI = rtli;
 
-	FreeConfigVariables(head);
+		/*
+		 * The user has requested a specific tli. This might be the latest
+		 * timeline but we don't know that; the point here is that we do not
+		 * allow the recoveryTargetTLI to follow any changes.
+		 */
+		recoveryTargetIsLatest = false;
+	}
+	else if (recoveryTargetTimeLineGoal == RECOVERY_TARGET_TIMELINE_LATEST)
+	{
+		/* We start the "latest" search from pg_control's timeline */
+		recoveryTargetTLI = findNewestTimeLine(recoveryTargetTLI);
+		recoveryTargetIsLatest = true;
+	}
+	else
+	{
+		/* else we just use the recoveryTargetTLI as already read from ControlFile */
+		Assert(recoveryTargetTimeLineGoal == RECOVERY_TARGET_TIMELINE_CONTROLFILE);
+		recoveryTargetIsLatest = false;
+	}
 }
 
 /*
@@ -5499,11 +5436,19 @@ exitArchiveRecovery(TimeLineID endTLI, XLogRecPtr endOfLog)
 	unlink(recoveryPath);		/* ignore any error */
 
 	/*
-	 * Rename the config file out of the way, so that we don't accidentally
+	 * Rename the signal files out of the way, so that we don't accidentally
 	 * re-enter archive recovery mode in a subsequent crash.
 	 */
-	unlink(RECOVERY_COMMAND_DONE);
-	durable_rename(RECOVERY_COMMAND_FILE, RECOVERY_COMMAND_DONE, FATAL);
+	if (unlink(StandbySignalFile) != 0 && standby_signal_file_found)
+		ereport(ERROR,
+			(errcode_for_file_access(),
+			 errmsg("could not remove file \"%s\": %m",
+					StandbySignalFile)));
+	if (unlink(RecoverySignalFile) != 0 && recovery_signal_file_found)
+		ereport(ERROR,
+			(errcode_for_file_access(),
+			 errmsg("could not remove file \"%s\": %m",
+					RecoverySignalFile)));
 
 	ereport(LOG,
 			(errmsg("archive recovery complete")));
@@ -6234,18 +6179,9 @@ StartupXLOG(void)
 		recoveryTargetTLI = ControlFile->checkPointCopy.ThisTimeLineID;
 
 	/*
-	 * Check for recovery control file, and if so set up state for offline
-	 * recovery
-	 */
-	readRecoveryCommandFile();
-
-	/*
-	 * Save archive_cleanup_command in shared memory so that other processes
-	 * can see it.
+	 * Check for signal files, and if so set up state for offline recovery
 	 */
-	strlcpy(XLogCtl->archiveCleanupCommand,
-			archiveCleanupCommand ? archiveCleanupCommand : "",
-			sizeof(XLogCtl->archiveCleanupCommand));
+	readRecoverySignalFile();
 
 	if (ArchiveRecoveryRequested)
 	{
@@ -6422,7 +6358,7 @@ StartupXLOG(void)
 		 * This can happen for example if a base backup is taken from a
 		 * running server using an atomic filesystem snapshot, without calling
 		 * pg_start/stop_backup. Or if you just kill a running master server
-		 * and put it into archive recovery by creating a recovery.conf file.
+		 * and put it into archive recovery by creating a recovery signal file.
 		 *
 		 * Our strategy in that case is to perform crash recovery first,
 		 * replaying all the WAL present in pg_wal, and only enter archive
@@ -6652,7 +6588,7 @@ StartupXLOG(void)
 
 	/*
 	 * Check whether we need to force recovery from WAL.  If it appears to
-	 * have been a clean shutdown and we did not have a recovery.conf file,
+	 * have been a clean shutdown and we did not have a recovery signal file,
 	 * then assume no recovery needed.
 	 */
 	if (checkPoint.redo < RecPtr)
@@ -6666,7 +6602,7 @@ StartupXLOG(void)
 		InRecovery = true;
 	else if (ArchiveRecoveryRequested)
 	{
-		/* force recovery due to presence of recovery.conf */
+		/* force recovery due to presence of recovery signal file */
 		InRecovery = true;
 	}
 
@@ -7171,7 +7107,6 @@ StartupXLOG(void)
 			/*
 			 * end of main redo apply loop
 			 */
-
 			if (reachedStopPoint)
 			{
 				if (!reachedConsistency)
@@ -9225,8 +9160,8 @@ CreateRestartPoint(int flags)
 	/*
 	 * Finally, execute archive_cleanup_command, if any.
 	 */
-	if (XLogCtl->archiveCleanupCommand[0])
-		ExecuteRecoveryCommand(XLogCtl->archiveCleanupCommand,
+	if (archiveCleanupCommand)
+		ExecuteRecoveryCommand(archiveCleanupCommand,
 							   "archive_cleanup_command",
 							   false);
 
@@ -11592,7 +11527,7 @@ WaitForWALToBecomeAvailable(XLogRecPtr RecPtr, bool randAccess,
 						}
 						curFileTLI = tli;
 						RequestXLogStreaming(tli, ptr, PrimaryConnInfo,
-											 PrimarySlotName);
+										PrimarySlotName);
 						receivedUpto = 0;
 					}
 
@@ -11920,14 +11855,15 @@ CheckForStandbyTrigger(void)
 		return true;
 	}
 
-	if (TriggerFile == NULL)
-		return false;
-
-	if (stat(TriggerFile, &stat_buf) == 0)
+	/*
+	 * Check for PromoteSignalFile... this is now a constant filename, with only the
+	 * filepath varying, so a user can still specify what they need.
+	 */
+	if (stat(PromoteSignalFile, &stat_buf) == 0)
 	{
 		ereport(LOG,
-				(errmsg("trigger file found: %s", TriggerFile)));
-		unlink(TriggerFile);
+				(errmsg("promote signal file found: %s", PromoteSignalFile)));
+		unlink(PromoteSignalFile);
 		triggered = true;
 		fast_promote = true;
 		return true;
@@ -11935,8 +11871,8 @@ CheckForStandbyTrigger(void)
 	else if (errno != ENOENT)
 		ereport(ERROR,
 				(errcode_for_file_access(),
-				 errmsg("could not stat trigger file \"%s\": %m",
-						TriggerFile)));
+				 errmsg("could not stat promote signal file \"%s\": %m",
+						PromoteSignalFile)));
 
 	return false;
 }
diff --git a/src/backend/access/transam/xlogarchive.c b/src/backend/access/transam/xlogarchive.c
index 7e91e8f..5751cb8 100644
--- a/src/backend/access/transam/xlogarchive.c
+++ b/src/backend/access/transam/xlogarchive.c
@@ -409,7 +409,7 @@ ExecuteRecoveryCommand(char *command, char *commandName, bool failOnSignal)
 
 		ereport((signaled && failOnSignal) ? FATAL : WARNING,
 		/*------
-		   translator: First %s represents a recovery.conf parameter name like
+		   translator: First %s represents a postgresql.conf parameter name like
 		  "recovery_end_command", the 2nd is the value of that parameter, the
 		  third an already translated error message. */
 				(errmsg("%s \"%s\": %s", commandName,
diff --git a/src/backend/access/transam/xlogfuncs.c b/src/backend/access/transam/xlogfuncs.c
index 27c0c56..61c1d4a 100644
--- a/src/backend/access/transam/xlogfuncs.c
+++ b/src/backend/access/transam/xlogfuncs.c
@@ -327,10 +327,11 @@ pg_create_restore_point(PG_FUNCTION_ARGS)
 
 	restore_name_str = text_to_cstring(restore_name);
 
-	if (strlen(restore_name_str) >= MAXFNAMELEN)
+	if (strlen(restore_name_str) >= MAXRESTOREPOINTNAMELEN)
 		ereport(ERROR,
 				(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
-				 errmsg("value too long for restore point (maximum %d characters)", MAXFNAMELEN - 1)));
+				 errmsg("value too long for restore point (maximum %d characters)",
+							MAXRESTOREPOINTNAMELEN - 1)));
 
 	restorepoint = XLogRestorePoint(restore_name_str);
 
diff --git a/src/backend/commands/extension.c b/src/backend/commands/extension.c
index 325f5b7..c699668 100644
--- a/src/backend/commands/extension.c
+++ b/src/backend/commands/extension.c
@@ -9,8 +9,8 @@
  * dependent objects can be associated with it.  An extension is created by
  * populating the pg_extension catalog from a "control" file.
  * The extension control file is parsed with the same parser we use for
- * postgresql.conf and recovery.conf.  An extension also has an installation
- * script file, containing SQL commands to create the extension's objects.
+ * postgresql.conf.  An extension also has an installation script file,
+ * containing SQL commands to create the extension's objects.
  *
  * Portions Copyright (c) 1996-2017, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
diff --git a/src/backend/postmaster/startup.c b/src/backend/postmaster/startup.c
index b172b5e..7003f044 100644
--- a/src/backend/postmaster/startup.c
+++ b/src/backend/postmaster/startup.c
@@ -153,6 +153,7 @@ HandleStartupProcInterrupts(void)
 	{
 		got_SIGHUP = false;
 		ProcessConfigFile(PGC_SIGHUP);
+		validateRecoveryParameters();
 	}
 
 	/*
diff --git a/src/backend/replication/walreceiver.c b/src/backend/replication/walreceiver.c
index 18d9d7e..3dacbde 100644
--- a/src/backend/replication/walreceiver.c
+++ b/src/backend/replication/walreceiver.c
@@ -70,9 +70,9 @@
 
 
 /* GUC variables */
-int			wal_receiver_status_interval;
-int			wal_receiver_timeout;
-bool		hot_standby_feedback;
+int wal_receiver_status_interval;
+int wal_receiver_timeout;
+bool hot_standby_feedback;
 
 /* libpqwalreceiver connection */
 static WalReceiverConn *wrconn = NULL;
@@ -425,9 +425,33 @@ WalReceiverMain(void)
 
 				if (got_SIGHUP)
 				{
+					char	*conninfo = pstrdup(PrimaryConnInfo);
+					char	*slotname = pstrdup(PrimarySlotName);
+
 					got_SIGHUP = false;
 					ProcessConfigFile(PGC_SIGHUP);
 					XLogWalRcvSendHSFeedback(true);
+
+					/*
+					 * If primary_conninfo has been changed while walreceiver is running,
+					 * shut down walreceiver so that a new walreceiver is started and
+					 * initiates replication with the new connection information.
+					 */
+					if (strcmp(conninfo, PrimaryConnInfo) != 0)
+						ereport(FATAL,
+								(errcode(ERRCODE_ADMIN_SHUTDOWN),
+								 errmsg("closing replication connection because primary_conninfo was changed")));
+
+					/*
+					 * And the same for primary_slot_name.
+					 */
+					if (strcmp(slotname, PrimarySlotName) != 0)
+						ereport(FATAL,
+								(errcode(ERRCODE_ADMIN_SHUTDOWN),
+								 errmsg("closing replication connection because primary_slot_name was changed")));
+
+					pfree(conninfo);
+					pfree(slotname);
 				}
 
 				/* See if we can read data immediately */
diff --git a/src/backend/utils/misc/guc-file.l b/src/backend/utils/misc/guc-file.l
index f01b814..2593065 100644
--- a/src/backend/utils/misc/guc-file.l
+++ b/src/backend/utils/misc/guc-file.l
@@ -190,13 +190,28 @@ ProcessConfigFileInternal(GucContext context, bool applySettings, int elevel)
 	}
 
 	/*
+	 * Read automatically generated configuration files.  Because these files
+	 * are in the data directory, we can't read them until the DataDir has
+	 * been set.
+	 *
+	 * First, parse any RECOVERY_AUTOCONF_FILENAME, if it has been generated
+	 * by pg_basebackup
+	 *
 	 * Parse the PG_AUTOCONF_FILENAME file, if present, after the main file to
-	 * replace any parameters set by ALTER SYSTEM command.  Because this file
-	 * is in the data directory, we can't read it until the DataDir has been
-	 * set.
+	 * replace any parameters set by ALTER SYSTEM command.
 	 */
 	if (DataDir)
 	{
+		if (!ParseConfigFile(RECOVERY_AUTOCONF_FILENAME, false,
+							 NULL, 0, 0, elevel,
+							 &head, &tail))
+		{
+			/* Syntax error(s) detected in the file, so bail out */
+			error = true;
+			ConfFileWithError = RECOVERY_AUTOCONF_FILENAME;
+			goto bail_out;
+		}
+
 		if (!ParseConfigFile(PG_AUTOCONF_FILENAME, false,
 							 NULL, 0, 0, elevel,
 							 &head, &tail))
diff --git a/src/backend/utils/misc/guc.c b/src/backend/utils/misc/guc.c
index 0707f66..c39c188 100644
--- a/src/backend/utils/misc/guc.c
+++ b/src/backend/utils/misc/guc.c
@@ -32,6 +32,7 @@
 #include "access/transam.h"
 #include "access/twophase.h"
 #include "access/xact.h"
+#include "access/xlog.h"
 #include "access/xlog_internal.h"
 #include "catalog/namespace.h"
 #include "commands/async.h"
@@ -80,6 +81,7 @@
 #include "utils/guc_tables.h"
 #include "utils/memutils.h"
 #include "utils/pg_locale.h"
+#include "utils/pg_lsn.h"
 #include "utils/plancache.h"
 #include "utils/portal.h"
 #include "utils/ps_status.h"
@@ -190,6 +192,15 @@ static void assign_application_name(const char *newval, void *extra);
 static bool check_cluster_name(char **newval, void **extra, GucSource source);
 static const char *show_unix_socket_permissions(void);
 static const char *show_log_file_mode(void);
+static bool check_recovery_target_type(char **newval, void **extra, GucSource source);
+static void assign_recovery_target_type(const char *newval, void *extra);
+static bool check_recovery_target_value(char **newval, void **extra, GucSource source);
+static void assign_recovery_target_value(const char *newval, void *extra);
+static bool check_recovery_target_timeline(char **newval, void **extra, GucSource source);
+static void assign_recovery_target_timeline(const char *newval, void *extra);
+static bool check_recovery_target_action(char **newval, void **extra, GucSource source);
+static void assign_recovery_target_action(const char *newval, void *extra);
+static bool check_primary_slot_name(char **newval, void **extra, GucSource source);
 
 /* Private functions in guc-file.l that need to be called from guc.c */
 static ConfigVariable *ProcessConfigFileInternal(GucContext context,
@@ -467,6 +478,8 @@ char	   *IdentFileName;
 char	   *external_pid_file;
 
 char	   *pgstat_temp_directory;
+char	*recovery_target_timeline_string;
+char	*recovery_target_action_string;
 
 char	   *application_name;
 
@@ -600,6 +613,10 @@ const char *const config_group_names[] =
 	gettext_noop("Write-Ahead Log / Checkpoints"),
 	/* WAL_ARCHIVING */
 	gettext_noop("Write-Ahead Log / Archiving"),
+	/* WAL_ARCHIVE_RECOVERY */
+	gettext_noop("Write-Ahead Log / Archive Recovery"),
+	/* WAL_RECOVERY_TARGET */
+	gettext_noop("Write-Ahead Log / Recovery Target"),
 	/* REPLICATION */
 	gettext_noop("Replication"),
 	/* REPLICATION_SENDING */
@@ -1553,6 +1570,16 @@ static struct config_bool ConfigureNamesBool[] =
 	},
 
 	{
+		{"recovery_target_inclusive", PGC_SIGHUP, WAL_RECOVERY_TARGET,
+			gettext_noop("Sets whether to include or exclude transaction with recovery target."),
+			NULL
+		},
+		&recoveryTargetInclusive,
+		true,
+		NULL, NULL, NULL
+	},
+
+	{
 		{"hot_standby", PGC_POSTMASTER, REPLICATION_STANDBY,
 			gettext_noop("Allows connections and queries during recovery."),
 			NULL
@@ -1793,8 +1820,19 @@ static struct config_int ConfigureNamesInt[] =
 	},
 
 	{
+		{"recovery_min_apply_delay", PGC_SIGHUP, REPLICATION_STANDBY,
+			gettext_noop("Sets the minimum delay to apply changes during recovery."),
+			NULL,
+			GUC_UNIT_MS
+		},
+		&recovery_min_apply_delay,
+		0, 0, INT_MAX,
+		NULL, NULL, NULL
+	},
+
+	{
 		{"wal_receiver_status_interval", PGC_SIGHUP, REPLICATION_STANDBY,
-			gettext_noop("Sets the maximum interval between WAL receiver status reports to the primary."),
+			gettext_noop("Sets the maximum interval between WAL receiver status reports to the sending server."),
 			NULL,
 			GUC_UNIT_S
 		},
@@ -1805,7 +1843,7 @@ static struct config_int ConfigureNamesInt[] =
 
 	{
 		{"wal_receiver_timeout", PGC_SIGHUP, REPLICATION_STANDBY,
-			gettext_noop("Sets the maximum wait time to receive data from the primary."),
+			gettext_noop("Sets the maximum wait time to receive data from the sending server."),
 			NULL,
 			GUC_UNIT_MS
 		},
@@ -3023,6 +3061,98 @@ static struct config_string ConfigureNamesString[] =
 	},
 
 	{
+		{"restore_command", PGC_SIGHUP, WAL_ARCHIVE_RECOVERY,
+			gettext_noop("Sets the shell command that will retrieve an archived WAL file."),
+			NULL
+		},
+		&recoveryRestoreCommand,
+		"",
+		NULL, NULL, NULL
+	},
+
+	{
+		{"archive_cleanup_command", PGC_SIGHUP, WAL_ARCHIVE_RECOVERY,
+			gettext_noop("Sets the shell command that will be executed at every restartpoint."),
+			NULL
+		},
+		&archiveCleanupCommand,
+		"",
+		NULL, NULL, NULL
+	},
+
+	{
+		{"recovery_end_command", PGC_SIGHUP, WAL_ARCHIVE_RECOVERY,
+			gettext_noop("Sets the shell command that will be executed once only at the end of recovery."),
+			NULL
+		},
+		&recoveryEndCommand,
+		"",
+		NULL, NULL, NULL
+	},
+
+	{
+		{"recovery_target_type", PGC_SIGHUP, WAL_RECOVERY_TARGET,
+			gettext_noop("Sets the type of desired recovery target."),
+			NULL
+		},
+		&recoveryTargetTypeString,
+		"",
+		check_recovery_target_type, assign_recovery_target_type, NULL
+	},
+
+	{
+		{"recovery_target_value", PGC_SIGHUP, WAL_RECOVERY_TARGET,
+			gettext_noop("Sets the value of the recovery taregt up to which recovery will proceed."),
+			NULL
+		},
+		&recoveryTargetValue,
+		"",
+		check_recovery_target_value, assign_recovery_target_value, NULL
+	},
+
+	{
+		{"recovery_target_timeline", PGC_POSTMASTER, WAL_RECOVERY_TARGET,
+			gettext_noop("Sets recovering into a particular timeline."),
+			NULL
+		},
+		&recovery_target_timeline_string,
+		"",
+		check_recovery_target_timeline, assign_recovery_target_timeline, NULL
+	},
+
+	{
+		{"recovery_target_action", PGC_SIGHUP, WAL_RECOVERY_TARGET,
+			gettext_noop("Sets the action to perform upon reaching the recovery target."),
+			NULL
+		},
+		&recovery_target_action_string,
+		"",
+		check_recovery_target_action, assign_recovery_target_action, NULL
+	},
+
+	{
+		{"primary_conninfo", PGC_SIGHUP, REPLICATION_STANDBY,
+			gettext_noop("Sets the connection string to be used to connect with the sending server."),
+			NULL,
+			GUC_SUPERUSER_ONLY
+		},
+		&PrimaryConnInfo,
+		"",
+		NULL, NULL, NULL
+	},
+
+	{
+		{"primary_slot_name", PGC_SIGHUP, REPLICATION_STANDBY,
+			gettext_noop("Sets the name of the replication slot to use on the sending server."),
+			NULL,
+			GUC_SUPERUSER_ONLY
+		},
+		&PrimarySlotName,
+		"",
+		check_primary_slot_name, NULL, NULL
+	},
+
+	{
 		{"client_encoding", PGC_USERSET, CLIENT_CONN_LOCALE,
 			gettext_noop("Sets the client's character set encoding."),
 			NULL,
@@ -10452,4 +10582,301 @@ show_log_file_mode(void)
 	return buf;
 }
 
+static bool
+check_recovery_target_type(char **newval, void **extra, GucSource source)
+{
+	RecoveryTargetType *myextra;
+	RecoveryTargetType rt = RECOVERY_TARGET_UNSET;
+
+	if (strcmp(*newval, "xid") == 0)
+		rt = RECOVERY_TARGET_XID;
+	else if (strcmp(*newval, "timestamp") == 0)
+		rt = RECOVERY_TARGET_TIME;
+	else if (strcmp(*newval, "name") == 0)
+		rt = RECOVERY_TARGET_NAME;
+	else if (strcmp(*newval, "lsn") == 0)
+		rt = RECOVERY_TARGET_LSN;
+	else if (strcmp(*newval, "immediate") == 0)
+		rt = RECOVERY_TARGET_IMMEDIATE;
+	else if (strcmp(*newval, "") != 0)
+	{
+		GUC_check_errdetail("recovery_target_type is not valid: \"%s\"", *newval);
+		return false;
+	}
+
+	myextra = (RecoveryTargetType *) guc_malloc(ERROR, sizeof(RecoveryTargetType));
+	*myextra = rt;
+	*extra = (void *) myextra;
+
+	return true;
+}
+
+static void
+assign_recovery_target_type(const char *newval, void *extra)
+{
+	recoveryTarget = *((RecoveryTargetType *) extra);
+}
+
+static bool
+check_recovery_target_value(char **newval, void **extra, GucSource source)
+{
+	bool valid = false;
+
+	/*
+	 * Value must be present in some cases, must not be present in others
+	 */
+	if (strcmp(*newval, "") == 0)
+	{
+		if (recoveryTarget == RECOVERY_TARGET_UNSET ||
+			recoveryTarget == RECOVERY_TARGET_IMMEDIATE)
+			valid = true;
+	}
+	else
+	{
+		if (recoveryTarget == RECOVERY_TARGET_UNSET ||
+			recoveryTarget == RECOVERY_TARGET_IMMEDIATE)
+			valid = false;
+		else
+			valid = true;
+	}
+
+	if (!valid)
+	{
+		GUC_check_errdetail("recovery_target_value is not valid: \"%s\"", *newval);
+		return false;
+	}
+
+	/*
+	 * We assume that recovery_target_type has already been parsed
+	 * since it sorts alphabetically before recovery_target_value.
+	 */
+	switch (recoveryTarget)
+	{
+		case RECOVERY_TARGET_UNSET:
+		case RECOVERY_TARGET_IMMEDIATE:
+			/* No value, so do nothing */
+			break;
+
+		case RECOVERY_TARGET_XID:
+			{
+				TransactionId	xid;
+				TransactionId	*myextra;
+
+				errno = 0;
+				xid = (TransactionId) strtoul(*newval, NULL, 0);
+				if (errno == EINVAL || errno == ERANGE)
+				{
+					GUC_check_errdetail("recovery_target_value is not a valid number: \"%s\"",
+								*newval);
+					return false;
+				}
+
+				myextra = (TransactionId *) guc_malloc(ERROR, sizeof(TransactionId));
+				*myextra = xid;
+				*extra = (void *) myextra;
+			}
+			break;
+
+		case RECOVERY_TARGET_TIME:
+			{
+				TimestampTz     time;
+				TimestampTz     *myextra;
+				MemoryContext oldcontext = CurrentMemoryContext;
+
+				PG_TRY();
+				{
+					time = DatumGetTimestampTz(DirectFunctionCall3(timestamptz_in,
+												CStringGetDatum(*newval),
+												ObjectIdGetDatum(InvalidOid),
+												Int32GetDatum(-1)));
+				}
+				PG_CATCH();
+				{
+					ErrorData  *edata;
+
+					/* Save error info */
+					MemoryContextSwitchTo(oldcontext);
+					edata = CopyErrorData();
+					FlushErrorState();
+
+					/* Pass the error message */
+					GUC_check_errdetail("%s", edata->message);
+					FreeErrorData(edata);
+					return false;
+				}
+				PG_END_TRY();
+
+				myextra = (TimestampTz *) guc_malloc(ERROR, sizeof(TimestampTz));
+				*myextra = time;
+				*extra = (void *) myextra;
+			}
+			break;
+
+		case RECOVERY_TARGET_NAME:
+			/* Use the value of newval directly */
+			if (strlen(*newval) > MAXRESTOREPOINTNAMELEN)
+			{
+				GUC_check_errdetail("recovery_target_value is too long (maximum %d characters)",
+									MAXRESTOREPOINTNAMELEN);
+				return false;
+			}
+			break;
+
+		case RECOVERY_TARGET_LSN:
+			{
+				XLogRecPtr	lsn;
+				XLogRecPtr	*myextra;
+				MemoryContext oldcontext = CurrentMemoryContext;
+
+				/*
+				 * Convert the LSN string given by the user to XLogRecPtr form.
+				 */
+				PG_TRY();
+				{
+					lsn =
+						DatumGetLSN(DirectFunctionCall3(pg_lsn_in,
+													CStringGetDatum(*newval),
+													ObjectIdGetDatum(InvalidOid),
+													Int32GetDatum(-1)));
+				}
+				PG_CATCH();
+				{
+					ErrorData  *edata;
+
+					/* Save error info */
+					MemoryContextSwitchTo(oldcontext);
+					edata = CopyErrorData();
+					FlushErrorState();
+
+					/* Pass the error message */
+					GUC_check_errdetail("%s", edata->message);
+					FreeErrorData(edata);
+					return false;
+				}
+				PG_END_TRY();
+
+				myextra = (XLogRecPtr *) guc_malloc(ERROR, sizeof(XLogRecPtr));
+				*myextra = lsn;
+				*extra = (void *) myextra;
+			}
+			break;
+	}
+
+	return true;
+}
+
+static void
+assign_recovery_target_value(const char *newval, void *extra)
+{
+	switch (recoveryTarget)
+	{
+		case RECOVERY_TARGET_UNSET:
+		case RECOVERY_TARGET_IMMEDIATE:
+			break;
+
+		case RECOVERY_TARGET_XID:
+			recoveryTargetXid = *((TransactionId *) extra);
+			break;
+
+		case RECOVERY_TARGET_TIME:
+			recoveryTargetTime = *((TimestampTz *) extra);
+			break;
+
+		case RECOVERY_TARGET_NAME:
+			if (newval && *newval)
+				recoveryTargetName = (char *) newval;
+			break;
+
+		case RECOVERY_TARGET_LSN:
+			recoveryTargetLSN = *((XLogRecPtr *) extra);
+			break;
+	}
+}
+
+static bool
+check_recovery_target_timeline(char **newval, void **extra, GucSource source)
+{
+	RecoveryTargetTimeLineGoal rttg = RECOVERY_TARGET_TIMELINE_CONTROLFILE;
+	RecoveryTargetTimeLineGoal *myextra;
+
+	if (strcmp(*newval, "latest") == 0)
+		rttg = RECOVERY_TARGET_TIMELINE_LATEST;
+	else if (strcmp(*newval, "controlfile") == 0 || strcmp(*newval, "") == 0)
+		rttg = RECOVERY_TARGET_TIMELINE_CONTROLFILE;
+	else
+	{
+		TimeLineID tli;
+
+		errno = 0;
+		tli = (TimeLineID) strtoul(*newval, NULL, 0);
+		if (errno == EINVAL || errno == ERANGE)
+		{
+			GUC_check_errdetail("recovery_target_timeline is not a valid number: \"%s\"",
+								*newval);
+			return false;
+		}
+		rttg = RECOVERY_TARGET_TIMELINE_NUMERIC;
+	}
+
+	myextra = (TimeLineID *) guc_malloc(ERROR, sizeof(TimeLineID));
+	*myextra = rttg;
+	*extra = (void *) myextra;
+
+	return true;
+}
+
+static void
+assign_recovery_target_timeline(const char *newval, void *extra)
+{
+	recoveryTargetTimeLineGoal = *((TimeLineID *) extra);
+	if (recoveryTargetTimeLineGoal == RECOVERY_TARGET_TIMELINE_NUMERIC)
+		recoveryTargetTLIRequested = (TimeLineID) strtoul(newval, NULL, 0);
+	else
+		recoveryTargetTLIRequested = 0;
+}
+
+static bool
+check_recovery_target_action(char **newval, void **extra, GucSource source)
+{
+	RecoveryTargetAction rta = RECOVERY_TARGET_ACTION_PAUSE;
+	RecoveryTargetAction *myextra;
+
+	if (strcmp(*newval, "pause") == 0)
+		rta = RECOVERY_TARGET_ACTION_PAUSE;
+	else if (strcmp(*newval, "promote") == 0)
+		rta = RECOVERY_TARGET_ACTION_PROMOTE;
+	else if (strcmp(*newval, "shutdown") == 0)
+		rta = RECOVERY_TARGET_ACTION_SHUTDOWN;
+	else if (strcmp(*newval, "") != 0)
+	{
+		GUC_check_errdetail("recovery_target_action is not valid: \"%s\"", *newval);
+		return false;
+	}
+
+	myextra = (RecoveryTargetAction *) guc_malloc(ERROR, sizeof(RecoveryTargetAction));
+	*myextra = rta;
+	*extra = (void *) myextra;
+
+	return true;
+}
+
+static void
+assign_recovery_target_action(const char *newval, void *extra)
+{
+	recoveryTargetAction = *((RecoveryTargetAction *) extra);
+}
+
+static bool
+check_primary_slot_name(char **newval, void **extra, GucSource source)
+{
+	if (strcmp(*newval,"") != 0 &&
+		!ReplicationSlotValidateName(*newval, WARNING))
+	{
+		GUC_check_errdetail("primary_slot_name is not valid: \"%s\"", *newval);
+		return false;
+	}
+
+	return true;
+}
+
 #include "guc-file.c"
diff --git a/src/backend/utils/misc/postgresql.conf.sample b/src/backend/utils/misc/postgresql.conf.sample
index 157d775..ea2bb4f 100644
--- a/src/backend/utils/misc/postgresql.conf.sample
+++ b/src/backend/utils/misc/postgresql.conf.sample
@@ -221,6 +221,26 @@
 #archive_timeout = 0		# force a logfile segment switch after this
 				# number of seconds; 0 disables
 
+# - Archive Recovery -
+#restore_command = ''		# command to use to restore an archived logfile segment
+				# placeholders: %p = path of file to restore
+				#               %f = file name only
+				# e.g. 'cp /mnt/server/archivedir/%f %p'
+#archive_cleanup_command = ''	# command to execute at every restartpoint
+#recovery_end_command = ''	# command to execute at completion of recovery
+
+# - Recovery Target -
+
+# Set these only when performing a targeted recovery
+
+#recovery_target_type=''	# 'xid', 'time', 'name', 'lsn',
+				# or 'immediate'
+#recovery_target_value=''	# value interpreted according to type
+#recovery_target_inclusive = on
+#recovery_target_timeline = ''	# unset means read from controlfile (default),
+				# or set to 'latest' or timeline ID
+#recovery_target_action = '' 	# 'pause', 'promote', 'shutdown'
+
 
 #------------------------------------------------------------------------------
 # REPLICATION
@@ -254,6 +274,8 @@
 
 # These settings are ignored on a master server.
 
+#primary_conninfo = ''			# connection string on sending server
+#primary_slot_name = ''			# connection slot on sending server
 #hot_standby = off			# "on" allows queries during recovery
 					# (change requires restart)
 #max_standby_archive_delay = 30s	# max delay before canceling queries
diff --git a/src/bin/pg_basebackup/pg_basebackup.c b/src/bin/pg_basebackup/pg_basebackup.c
index 9020fb1..007b4a6 100644
--- a/src/bin/pg_basebackup/pg_basebackup.c
+++ b/src/bin/pg_basebackup/pg_basebackup.c
@@ -127,9 +127,12 @@ static int	has_xlogendptr = 0;
 static volatile LONG has_xlogendptr = 0;
 #endif
 
-/* Contents of recovery.conf to be generated */
+/* Contents of configuration file to be generated */
 static PQExpBuffer recoveryconfcontents = NULL;
 
+#define RECOVERY_AUTOCONF_FILENAME	"recovery.auto.conf"
+#define STANDBY_SIGNAL_FILE 		"standby.signal"
+
 /* Function headers */
 static void usage(void);
 static void disconnect_and_exit(int code);
@@ -337,7 +340,7 @@ usage(void)
 	printf(_("  -r, --max-rate=RATE    maximum transfer rate to transfer data directory\n"
 	  "                         (in kB/s, or use suffix \"k\" or \"M\")\n"));
 	printf(_("  -R, --write-recovery-conf\n"
-			 "                         write recovery.conf for replication\n"));
+			 "                         write recovery.conf.auto for replication\n"));
 	printf(_("  -S, --slot=SLOTNAME    replication slot to use\n"));
 	printf(_("      --no-slot          prevent creation of temporary replication slot\n"));
 	printf(_("  -T, --tablespace-mapping=OLDDIR=NEWDIR\n"
@@ -1073,8 +1076,8 @@ ReceiveTarFile(PGconn *conn, PGresult *res, int rownum)
 		{
 			/*
 			 * End of chunk. If requested, and this is the base tablespace,
-			 * write recovery.conf into the tarfile. When done, close the file
-			 * (but not stdout).
+			 * write configuration file into the tarfile. When done, close the
+			 * file (but not stdout).
 			 *
 			 * Also, write two completely empty blocks at the end of the tar
 			 * file, as required by some tar programs.
@@ -1088,7 +1091,7 @@ ReceiveTarFile(PGconn *conn, PGresult *res, int rownum)
 				char		header[512];
 				int			padding;
 
-				tarCreateHeader(header, "recovery.conf", NULL,
+				tarCreateHeader(header, RECOVERY_AUTOCONF_FILENAME, NULL,
 								recoveryconfcontents->len,
 								0600, 04000, 02000,
 								time(NULL));
@@ -1099,6 +1102,14 @@ ReceiveTarFile(PGconn *conn, PGresult *res, int rownum)
 				WRITE_TAR_DATA(recoveryconfcontents->data, recoveryconfcontents->len);
 				if (padding)
 					WRITE_TAR_DATA(zerobuf, padding);
+
+				tarCreateHeader(header, STANDBY_SIGNAL_FILE, NULL,
+								0, /* zero-length file */
+								0600, 04000, 02000,
+								time(NULL));
+
+				WRITE_TAR_DATA(header, sizeof(header));
+				WRITE_TAR_DATA(zerobuf, 511);
 			}
 
 			/* 2 * 512 bytes empty data at end of file */
@@ -1142,8 +1153,8 @@ ReceiveTarFile(PGconn *conn, PGresult *res, int rownum)
 		if (!writerecoveryconf || !basetablespace)
 		{
 			/*
-			 * When not writing recovery.conf, or when not working on the base
-			 * tablespace, we never have to look for an existing recovery.conf
+			 * When not writing config file, or when not working on the base
+			 * tablespace, we never have to look for an existing configuration
 			 * file in the stream.
 			 */
 			WRITE_TAR_DATA(copybuf, r);
@@ -1151,7 +1162,7 @@ ReceiveTarFile(PGconn *conn, PGresult *res, int rownum)
 		else
 		{
 			/*
-			 * Look for a recovery.conf in the existing tar stream. If it's
+			 * Look for a config file in the existing tar stream. If it's
 			 * there, we must skip it so we can later overwrite it with our
 			 * own version of the file.
 			 *
@@ -1196,13 +1207,15 @@ ReceiveTarFile(PGconn *conn, PGresult *res, int rownum)
 						/*
 						 * We have the complete header structure in tarhdr,
 						 * look at the file metadata: - the subsequent file
-						 * contents have to be skipped if the filename is
-						 * recovery.conf - find out the size of the file
+						 * contents have to be skipped if the filename matches
+						 * config file - find out the size of the file
 						 * padded to the next multiple of 512
+						 *
+						 * Note we don't skip STANDBY_SIGNAL_FILE (correct??)
 						 */
 						int			padding;
 
-						skip_file = (strcmp(&tarhdr[0], "recovery.conf") == 0);
+						skip_file = (strcmp(&tarhdr[0], RECOVERY_AUTOCONF_FILENAME) == 0);
 
 						filesz = read_tar_number(&tarhdr[124], 12);
 
@@ -1569,7 +1582,7 @@ escape_quotes(const char *src)
 }
 
 /*
- * Create a recovery.conf file in memory using a PQExpBuffer
+ * Create a configuration file in memory using a PQExpBuffer
  */
 static void
 GenerateRecoveryConf(PGconn *conn)
@@ -1653,8 +1666,9 @@ GenerateRecoveryConf(PGconn *conn)
 
 
 /*
- * Write a recovery.conf file into the directory specified in basedir,
+ * Write the configuration file into the directory specified in basedir,
  * with the contents already collected in memory.
+ * Then write the signal file into the basedir also.
  */
 static void
 WriteRecoveryConf(void)
@@ -1662,8 +1676,7 @@ WriteRecoveryConf(void)
 	char		filename[MAXPGPATH];
 	FILE	   *cf;
 
-	sprintf(filename, "%s/recovery.conf", basedir);
-
+	snprintf(filename, MAXPGPATH, "%s/%s", basedir, RECOVERY_AUTOCONF_FILENAME);
 	cf = fopen(filename, "w");
 	if (cf == NULL)
 	{
@@ -1680,6 +1693,16 @@ WriteRecoveryConf(void)
 	}
 
 	fclose(cf);
+
+	snprintf(filename, MAXPGPATH, "%s/%s", basedir, STANDBY_SIGNAL_FILE);
+	cf = fopen(filename, "w");
+	if (cf == NULL)
+	{
+		fprintf(stderr, _("%s: could not create file \"%s\": %s\n"), progname, filename, strerror(errno));
+		disconnect_and_exit(1);
+	}
+
+	fclose(cf);
 }
 
 
@@ -1735,7 +1758,7 @@ BaseBackup(void)
 	}
 
 	/*
-	 * Build contents of recovery.conf if requested
+	 * Build contents of configuration file if requested
 	 */
 	if (writerecoveryconf)
 		GenerateRecoveryConf(conn);
@@ -2018,7 +2041,7 @@ BaseBackup(void)
 #endif
 	}
 
-	/* Free the recovery.conf contents */
+	/* Free the configuration file contents */
 	destroyPQExpBuffer(recoveryconfcontents);
 
 	/*
diff --git a/src/bin/pg_basebackup/t/010_pg_basebackup.pl b/src/bin/pg_basebackup/t/010_pg_basebackup.pl
index 29f519d..d8132ab 100644
--- a/src/bin/pg_basebackup/t/010_pg_basebackup.pl
+++ b/src/bin/pg_basebackup/t/010_pg_basebackup.pl
@@ -212,16 +212,13 @@ SKIP:
 
 $node->command_ok([ 'pg_basebackup', '-D', "$tempdir/backupR", '-R' ],
 	'pg_basebackup -R runs');
-ok(-f "$tempdir/backupR/recovery.conf", 'recovery.conf was created');
-my $recovery_conf = slurp_file "$tempdir/backupR/recovery.conf";
+ok(-f "$tempdir/backupR/recovery.auto.conf", 'recovery conf file was created');
+ok(-f "$tempdir/backupR/standby.signal", 'standby mode is configured');
+my $recovery_conf = slurp_file "$tempdir/backupR/recovery.auto.conf";
 
 my $port = $node->port;
 like(
 	$recovery_conf,
-	qr/^standby_mode = 'on'\n/m,
-	'recovery.conf sets standby_mode');
-like(
-	$recovery_conf,
 	qr/^primary_conninfo = '.*port=$port.*'\n/m,
 	'recovery.conf sets primary_conninfo');
 
@@ -278,6 +275,6 @@ $node->command_ok(
 		'stream',        '-S', 'slot1',                  '-R' ],
 	'pg_basebackup with replication slot and -R runs');
 like(
-	slurp_file("$tempdir/backupxs_sl_R/recovery.conf"),
+	slurp_file("$tempdir/backupxs_sl_R/recovery.auto.conf"),
 	qr/^primary_slot_name = 'slot1'\n/m,
-	'recovery.conf sets primary_slot_name');
+	'recovery conf file sets primary_slot_name');
diff --git a/src/bin/pg_rewind/RewindTest.pm b/src/bin/pg_rewind/RewindTest.pm
index c67212f..3ccb201 100644
--- a/src/bin/pg_rewind/RewindTest.pm
+++ b/src/bin/pg_rewind/RewindTest.pm
@@ -136,7 +136,7 @@ sub create_standby
 	my $connstr_master = $node_master->connstr();
 
 	$node_standby->append_conf(
-		"recovery.conf", qq(
+		"postgresql.conf", qq(
 primary_conninfo='$connstr_master application_name=rewind_standby'
 standby_mode=on
 recovery_target_timeline='latest'
@@ -234,7 +234,7 @@ sub run_pg_rewind
 	# Plug-in rewound node to the now-promoted standby node
 	my $port_standby = $node_standby->port;
 	$node_master->append_conf(
-		'recovery.conf', qq(
+		'postgresql.conf', qq(
 primary_conninfo='port=$port_standby'
 standby_mode=on
 recovery_target_timeline='latest'
diff --git a/src/include/access/xlog.h b/src/include/access/xlog.h
index 9f036c7..cb5bdac 100644
--- a/src/include/access/xlog.h
+++ b/src/include/access/xlog.h
@@ -87,6 +87,41 @@ typedef enum
 	RECOVERY_TARGET_IMMEDIATE
 } RecoveryTargetType;
 
+#define RecoveryTargetText(t) ( \
+	t == RECOVERY_TARGET_UNSET ? "unset" : ( \
+	t == RECOVERY_TARGET_XID   ? "xid" : ( \
+	t == RECOVERY_TARGET_TIME  ? "timestamp" : ( \
+	t == RECOVERY_TARGET_NAME  ? "name" : ( \
+	t == RECOVERY_TARGET_LSN   ? "lsn" : \
+					"immediate" )))))
+
+/*
+ * Recovery target action.
+ */
+typedef enum
+{
+	RECOVERY_TARGET_ACTION_PAUSE,
+	RECOVERY_TARGET_ACTION_PROMOTE,
+	RECOVERY_TARGET_ACTION_SHUTDOWN
+} RecoveryTargetAction;
+
+#define RecoveryTargetActionText(t) ( \
+	t == RECOVERY_TARGET_ACTION_PAUSE   ? "pause" : ( \
+	t == RECOVERY_TARGET_ACTION_PROMOTE ? "promote" : ( \
+						"shutdown" )))
+/*
+ * Recovery target TimeLine goal
+ */
+typedef enum
+{
+	RECOVERY_TARGET_TIMELINE_CONTROLFILE,
+	RECOVERY_TARGET_TIMELINE_LATEST,
+	RECOVERY_TARGET_TIMELINE_NUMERIC
+} RecoveryTargetTimeLineGoal;
+
+/* Max length of named restore points */
+#define MAXRESTOREPOINTNAMELEN 64
+
 extern XLogRecPtr ProcLastRecPtr;
 extern XLogRecPtr XactLastRecEnd;
 extern PGDLLIMPORT XLogRecPtr XactLastCommitEnd;
@@ -111,6 +146,36 @@ extern bool log_checkpoints;
 
 extern int	CheckPointSegments;
 
+/* options previously taken from recovery.conf for archive recovery */
+extern char *recoveryRestoreCommand;
+extern char *recoveryEndCommand;
+extern char *archiveCleanupCommand;
+extern char *recoveryTargetTypeString;
+extern RecoveryTargetType recoveryTarget;
+extern char *recoveryTargetValue;
+extern bool recoveryTargetInclusive;
+extern RecoveryTargetAction recoveryTargetAction;
+extern TransactionId recoveryTargetXid;
+extern TimestampTz recoveryTargetTime;
+extern char *recoveryTargetName;
+extern XLogRecPtr recoveryTargetLSN;
+extern int     recovery_min_apply_delay;
+
+/* option set locally in Startup process only when signal files exist */
+extern bool StandbyModeRequested;
+extern bool StandbyMode;
+
+/* options for WALreceiver.c */
+extern char *PrimaryConnInfo;
+extern char *PrimarySlotName;
+
+extern char *signal_file_directory;
+
+extern char *recoveryTargetTLIString;
+extern RecoveryTargetTimeLineGoal recoveryTargetTimeLineGoal;
+extern TimeLineID recoveryTargetTLIRequested;
+extern TimeLineID recoveryTargetTLI;
+
 /* Archive modes */
 typedef enum ArchiveMode
 {
@@ -240,6 +305,8 @@ extern const char *xlog_identify(uint8 info);
 
 extern void issue_xlog_fsync(int fd, XLogSegNo segno);
 
+extern void logRecoveryParameters(void);
+extern void validateRecoveryParameters(void);
 extern bool RecoveryInProgress(void);
 extern bool HotStandbyActive(void);
 extern bool HotStandbyActiveInReplay(void);
diff --git a/src/include/access/xlog_internal.h b/src/include/access/xlog_internal.h
index 578bff5..e4e4ac0 100644
--- a/src/include/access/xlog_internal.h
+++ b/src/include/access/xlog_internal.h
@@ -246,16 +246,6 @@ typedef struct XLogRecData
 } XLogRecData;
 
 /*
- * Recovery target action.
- */
-typedef enum
-{
-	RECOVERY_TARGET_ACTION_PAUSE,
-	RECOVERY_TARGET_ACTION_PROMOTE,
-	RECOVERY_TARGET_ACTION_SHUTDOWN
-} RecoveryTargetAction;
-
-/*
  * Method table for resource managers.
  *
  * This struct must be kept in sync with the PG_RMGR definition in
diff --git a/src/include/utils/guc.h b/src/include/utils/guc.h
index 7dd3780..829c0d2 100644
--- a/src/include/utils/guc.h
+++ b/src/include/utils/guc.h
@@ -34,6 +34,11 @@
 #define PG_AUTOCONF_FILENAME		"postgresql.auto.conf"
 
 /*
+ * Recovery commands generated by pg_basebackup
+ */
+#define RECOVERY_AUTOCONF_FILENAME	"recovery.auto.conf"
+
+/*
  * Certain options can only be set at certain times. The rules are
  * like this:
  *
diff --git a/src/include/utils/guc_tables.h b/src/include/utils/guc_tables.h
index 2da9115..c75537c 100644
--- a/src/include/utils/guc_tables.h
+++ b/src/include/utils/guc_tables.h
@@ -68,6 +68,8 @@ enum config_group
 	WAL_SETTINGS,
 	WAL_CHECKPOINTS,
 	WAL_ARCHIVING,
+	WAL_ARCHIVE_RECOVERY,
+	WAL_RECOVERY_TARGET,
 	REPLICATION,
 	REPLICATION_SENDING,
 	REPLICATION_MASTER,
diff --git a/src/test/perl/PostgresNode.pm b/src/test/perl/PostgresNode.pm
index 4018f0a..f0eec62 100644
--- a/src/test/perl/PostgresNode.pm
+++ b/src/test/perl/PostgresNode.pm
@@ -413,6 +413,8 @@ sub init
 	print $conf "fsync = off\n";
 	print $conf "log_line_prefix = '%m [%p] %q%a '\n";
 	print $conf "log_statement = all\n";
+	print $conf "log_replication_commands = on\n";
+	print $conf "log_min_messages = DEBUG2\n";
 	print $conf "port = $port\n";
 
 	if ($params{allows_streaming})
@@ -589,8 +591,6 @@ of a backup previously created on that node with $node->backup.
 
 Does not start the node after initializing it.
 
-A recovery.conf is not created.
-
 pg_hba.conf is configured to allow replication connections. Pass the keyword
 parameter hba_permit_replication => 0 to disable this.
 
@@ -760,13 +760,16 @@ sub enable_streaming
 	my ($self, $root_node) = @_;
 	my $root_connstr = $root_node->connstr;
 	my $name         = $self->name;
+	my $pgdata  = $self->data_dir;
 
 	print "### Enabling streaming replication for node \"$name\"\n";
 	$self->append_conf(
-		'recovery.conf', qq(
+		'postgresql.conf', qq(
 primary_conninfo='$root_connstr application_name=$name'
-standby_mode=on
 ));
+	open my $standbysignal, ">>$pgdata/standby.signal";
+	print $standbysignal "\n# Allow replication (set up by PostgresNode.pm)\n";
+	close $standbysignal;
 }
 
 # Internal routine to enable archive recovery command on a standby node
@@ -775,6 +778,7 @@ sub enable_restoring
 	my ($self, $root_node) = @_;
 	my $path = $root_node->archive_dir;
 	my $name = $self->name;
+	my $pgdata  = $self->data_dir;
 
 	print "### Enabling WAL restore for node \"$name\"\n";
 
@@ -791,10 +795,12 @@ sub enable_restoring
 	  : qq{cp "$path/%f" "%p"};
 
 	$self->append_conf(
-		'recovery.conf', qq(
+		'postgresql.conf', qq(
 restore_command = '$copy_command'
-standby_mode = on
 ));
+	open my $standbysignal, ">>$pgdata/standby.signal";
+	print $standbysignal "\n# Allow replication (set up by PostgresNode.pm)\n";
+	close $standbysignal;
 }
 
 # Internal routine to enable archiving
diff --git a/src/test/recovery/t/001_stream_rep.pl b/src/test/recovery/t/001_stream_rep.pl
index 7fb2e9e..411c279 100644
--- a/src/test/recovery/t/001_stream_rep.pl
+++ b/src/test/recovery/t/001_stream_rep.pl
@@ -68,12 +68,12 @@ my ($slotname_1, $slotname_2) = ('standby_1', 'standby_2');
 $node_master->append_conf('postgresql.conf', "max_replication_slots = 4\n");
 $node_master->restart;
 is($node_master->psql('postgres', qq[SELECT pg_create_physical_replication_slot('$slotname_1');]), 0, 'physical slot created on master');
-$node_standby_1->append_conf('recovery.conf', "primary_slot_name = $slotname_1\n");
+$node_standby_1->append_conf('postgresql.conf', "primary_slot_name = $slotname_1\n");
 $node_standby_1->append_conf('postgresql.conf', "wal_receiver_status_interval = 1\n");
 $node_standby_1->append_conf('postgresql.conf', "max_replication_slots = 4\n");
 $node_standby_1->restart;
 is($node_standby_1->psql('postgres', qq[SELECT pg_create_physical_replication_slot('$slotname_2');]), 0, 'physical slot created on intermediate replica');
-$node_standby_2->append_conf('recovery.conf', "primary_slot_name = $slotname_2\n");
+$node_standby_2->append_conf('postgresql.conf', "primary_slot_name = $slotname_2\n");
 $node_standby_2->append_conf('postgresql.conf', "wal_receiver_status_interval = 1\n");
 $node_standby_2->restart;
 
diff --git a/src/test/recovery/t/003_recovery_targets.pl b/src/test/recovery/t/003_recovery_targets.pl
index b7b0caa..a23b35f 100644
--- a/src/test/recovery/t/003_recovery_targets.pl
+++ b/src/test/recovery/t/003_recovery_targets.pl
@@ -3,7 +3,7 @@ use strict;
 use warnings;
 use PostgresNode;
 use TestLib;
-use Test::More tests => 9;
+use Test::More tests => 5;
 
 # Create and test a standby from given backup, with a certain
 # recovery target.
@@ -23,7 +23,7 @@ sub test_recovery_standby
 	foreach my $param_item (@$recovery_params)
 	{
 		$node_standby->append_conf(
-			'recovery.conf',
+			'postgresql.conf',
 			qq($param_item
 ));
 	}
@@ -100,47 +100,20 @@ $node_master->safe_psql('postgres',
 $node_master->safe_psql('postgres', "SELECT pg_switch_wal()");
 
 # Test recovery targets
-my @recovery_params = ("recovery_target = 'immediate'");
+my @recovery_params = ("recovery_target_type = 'immediate'");
 test_recovery_standby('immediate target',
 	'standby_1', $node_master, \@recovery_params, "1000", $lsn1);
-@recovery_params = ("recovery_target_xid = '$recovery_txid'");
+@recovery_params = ("recovery_target_type = 'xid'", "recovery_target_value = '$recovery_txid'");
 test_recovery_standby('XID', 'standby_2', $node_master, \@recovery_params,
 	"2000", $lsn2);
-@recovery_params = ("recovery_target_time = '$recovery_time'");
+@recovery_params = ("recovery_target_type = 'timestamp'", "recovery_target_value = '$recovery_time'");
 test_recovery_standby('time', 'standby_3', $node_master, \@recovery_params,
 	"3000", $lsn3);
-@recovery_params = ("recovery_target_name = '$recovery_name'");
+@recovery_params = ("recovery_target_type = 'name'", "recovery_target_value = '$recovery_name'");
 test_recovery_standby('name', 'standby_4', $node_master, \@recovery_params,
 	"4000", $lsn4);
-@recovery_params = ("recovery_target_lsn = '$recovery_lsn'");
+@recovery_params = ("recovery_target_type = 'lsn'", "recovery_target_value = '$recovery_lsn'");
 test_recovery_standby('LSN', 'standby_5', $node_master, \@recovery_params,
 	"5000", $lsn5);
 
-# Multiple targets
-# Last entry has priority (note that an array respects the order of items
-# not hashes).
-@recovery_params = (
-	"recovery_target_name = '$recovery_name'",
-	"recovery_target_xid  = '$recovery_txid'",
-	"recovery_target_time = '$recovery_time'");
-test_recovery_standby('name + XID + time',
-	'standby_6', $node_master, \@recovery_params, "3000", $lsn3);
-@recovery_params = (
-	"recovery_target_time = '$recovery_time'",
-	"recovery_target_name = '$recovery_name'",
-	"recovery_target_xid  = '$recovery_txid'");
-test_recovery_standby('time + name + XID',
-	'standby_7', $node_master, \@recovery_params, "2000", $lsn2);
-@recovery_params = (
-	"recovery_target_xid  = '$recovery_txid'",
-	"recovery_target_time = '$recovery_time'",
-	"recovery_target_name = '$recovery_name'");
-test_recovery_standby('XID + time + name',
-	'standby_8', $node_master, \@recovery_params, "4000", $lsn4);
-@recovery_params = (
-	"recovery_target_xid  = '$recovery_txid'",
-	"recovery_target_time = '$recovery_time'",
-	"recovery_target_name = '$recovery_name'",
-	"recovery_target_lsn = '$recovery_lsn'",);
-test_recovery_standby('XID + time + name + LSN',
-	'standby_9', $node_master, \@recovery_params, "5000", $lsn5);
+# Tests for multiple targets no longer needed from 10.0 onwards
diff --git a/src/test/recovery/t/004_timeline_switch.pl b/src/test/recovery/t/004_timeline_switch.pl
index 7c6587a..2abdd71 100644
--- a/src/test/recovery/t/004_timeline_switch.pl
+++ b/src/test/recovery/t/004_timeline_switch.pl
@@ -41,10 +41,10 @@ $node_master->teardown_node;
 $node_standby_1->promote;
 
 # Switch standby 2 to replay from standby 1
-rmtree($node_standby_2->data_dir . '/recovery.conf');
+#----still needed?    rmtree($node_standby_2->data_dir . '/recovery.conf');
 my $connstr_1 = $node_standby_1->connstr;
 $node_standby_2->append_conf(
-	'recovery.conf', qq(
+	'postgresql.conf', qq(
 primary_conninfo='$connstr_1 application_name=@{[$node_standby_2->name]}'
 standby_mode=on
 recovery_target_timeline='latest'
diff --git a/src/test/recovery/t/005_replay_delay.pl b/src/test/recovery/t/005_replay_delay.pl
index cd9e8f5..b7de0a9 100644
--- a/src/test/recovery/t/005_replay_delay.pl
+++ b/src/test/recovery/t/005_replay_delay.pl
@@ -25,7 +25,7 @@ my $delay        = 3;
 $node_standby->init_from_backup($node_master, $backup_name,
 	has_streaming => 1);
 $node_standby->append_conf(
-	'recovery.conf', qq(
+	'postgresql.conf', qq(
 recovery_min_apply_delay = '${delay}s'
 ));
 $node_standby->start;
#90Michael Paquier
michael.paquier@gmail.com
In reply to: Simon Riggs (#89)
Re: Proposal for changes to recovery.conf API

On Fri, Feb 24, 2017 at 7:39 PM, Simon Riggs <simon@2ndquadrant.com> wrote:

New patch version implementing everything you requested, incl docs and
tap tests.

The TAP changes look good to me. I thought that more diffs would have
been necessary.

The patch as offered here is what I've been asked to do by everybody
as well as I can do it. I'm very happy to receive comments and to
rework the design based upon further feedback.

This meritates a summary after all has happened on this thread and
this topic. After reading this patch, here is what this is doing:
- The existing standby_mode in recovery.conf is removed, replaced by
standby.trigger to decide if a node in recovery should be put in
standby mode.
- recovery.trigger can be used to put a node in recovery. When both
standby.trigger and recovery.trigger are specified, standby.trigger
takes priority.
- Two GUC parameters, recovery_target_type and recovery_target_value,
replace all the existing recovery target parameters.
- pg_basebackup -R generates recovery.conf.auto.
- trigger_file is removed.
FWIW, my only complain is about the removal of trigger_file, this is
useful to detect a trigger file on a different partition that PGDATA!
Keeping it costs also nothing..

I'm not completely convinced this is a great design, so I'm happy to
hear input. pg_basebackup -R is the main wrinkle.

Yeah, I can imagine that. It is easy to append a new file to a
tarball, harder to add data to an existing file perhaps?

The timeline handling has a bug at present that I'm working on, but
I'm not worried it constitutes a major problem. Obviously it will be
fixed before commit, but the patch needs more discussion
now/yesterday.

Running the tests I can see failures in 004_timeline_switch.pl, which
is what you likely mention here, as well as a failure in
008_fsm_truncation.pl. I can also see that 009_twophase.pl is
freezing.

All parameters are set at PGC_POSTMASTER for now.

Thanks for considering that, this makes the review of this patch
easier, and that's complicated enough as-is.

-test_recovery_standby('XID + time + name + LSN',
-   'standby_9', $node_master, \@recovery_params, "5000", $lsn5);
+# Tests for multiple targets no longer needed from 10.0 onwards
I would be fine without any comments as well. What's the point of
mentioning a past state here?
 # Switch standby 2 to replay from standby 1
-rmtree($node_standby_2->data_dir . '/recovery.conf');
+#----still needed?    rmtree($node_standby_2->data_dir . '/recovery.conf');
 my $connstr_1 = $node_standby_1->connstr;
No need to worry here, this can be removed.

osx:func.sgml:18159:19:X: reference to non-existent ID "RECOVERY-TARGET-NAME"
osx:release-9.1.sgml:9814:23:X: reference to non-existent ID
"RECOVERY-TARGET-NAME"
osx:recovery-config.sgml:311:26:X: reference to non-existent ID
"RECOVERY-TARGET-XID"
osx:recovery-config.sgml:310:43:X: reference to non-existent ID
"RECOVERY-TARGET-TIME"
osx:release-9.4.sgml:8034:27:X: reference to non-existent ID "RECOVERY-TARGET"
Documentation fails to compile.

+    The database server can also be started "in recovery" a term that covers
+    using the server as a standby or for executing a targeted
recovery. Typically
+    standby mode would be used to provide high availability and/or read
+    scalability, whereas a targeted recovery is used to recover from data loss.
Double quotes directly used in a documentation paragraph are not nice.
You should add a comma after "in recovery".
+    To start the server in standby mode create a zero-length file
+    called <filename>standby.signal</><indexterm><primary>standby.signal</></>
Zero-length file is not mandatory. Only creating a file is.
+     <para>
+      Parameter settings may be changed in
<filename>postgresql.conf</filename> or
+      by executing the <command>ALTER SYSTEM</command>. Changes will take some
+      time to take effect, so changes made while not in paused state may not
+      have the desired effect in all cases.
+     </para>
But those parameters are PGC_POSTMASTER?!
+      By default, recovery will recover to the end of the WAL log. An earlier
+      stopping point may be specified using <varname>recovery_target_type</>
+      and in most cases also <varname>recovery_target_value</>, plus
the optional
+      parameters <varname>recovery_target_inclusive</>,
+      <varname>recovery_targeti_timeline</> and
<varname>recovery_target_action</>.
+     </para>
"recovery will recover" is a bad phrasing, I would change that to
"recovery will process".
There is also a typo => s/targeti/target/.
+     <para>
+      Parameter settings may be changed only at server start, though later
+      patches may add this capability.
+     </para>
I would just say that "new values for those parameters are considered
only at restart of the server". There is no need to speculate about a
potential future in the documentation. If nothing happens this would
remain incorrect.
         By default, a standby server restores WAL records from the
-        primary as soon as possible. It may be useful to have a time-delayed
+        sending server as soon as possible. It may be useful to have
a time-delayed
Perhaps that's a separate patch? Cascading servers can have a delay even now.

Perhaps it would be worth mentioning promote.signal in the docs?

- * recoveryTargetTLI: the desired timeline that we want to end in.
+ * recoveryTargetTimeLineGoal: what the user requested, if any
+ *
+ * recoveryTargetTLIRequested: numeric value of requested timeline, if constant
+ *
+ * recoveryTargetTLI: the currently understood target timeline; changes
Hm.. This looks like too much complication...
+       fd = BasicOpenFile(StandbySignalFile, O_RDWR | PG_BINARY |
get_sync_bit(sync_method),
+                           S_IRUSR | S_IWUSR);
+       pg_fsync(fd);
+       close(fd);
+       standby_signal_file_found = true;
Forgetting to sync the parent directory here, no?

+ int normal_log_level = LOG; //DEBUG2;
Let's not forget that.

+   if (unlink(StandbySignalFile) != 0 && standby_signal_file_found)
+       ereport(ERROR,
+           (errcode_for_file_access(),
+            errmsg("could not remove file \"%s\": %m",
+                   StandbySignalFile)));
+   if (unlink(RecoverySignalFile) != 0 && recovery_signal_file_found)
+       ereport(ERROR,
+           (errcode_for_file_access(),
+            errmsg("could not remove file \"%s\": %m",
+                   RecoverySignalFile)));
unlink() is not durable... It may be better to rename those files
using durable_rename() so as it is durable.
-   if (strlen(restore_name_str) >= MAXFNAMELEN)
+   if (strlen(restore_name_str) >= MAXRESTOREPOINTNAMELEN
Not sure there is much point to have a new variable here.
+                   /*
+                    * If primary_conninfo has been changed while
walreceiver is running,
+                    * shut down walreceiver so that a new walreceiver
is started and
+                    * initiates replication with the new connection
information.
+                    */
+                   if (strcmp(conninfo, PrimaryConnInfo) != 0)
+                       ereport(FATAL,
+                               (errcode(ERRCODE_ADMIN_SHUTDOWN),
+                                errmsg("closing replication
connection because primary_conninfo was changed")));
This cannot happen, those are postmaster parameters. Let's remove any
complications if those are not needed.
+       if (!ParseConfigFile(RECOVERY_AUTOCONF_FILENAME, false,
+                            NULL, 0, 0, elevel,
+                            &head, &tail))
+       {
+           /* Syntax error(s) detected in the file, so bail out */
+           error = true;
+           ConfFileWithError = RECOVERY_AUTOCONF_FILENAME;
+           goto bail_out;
+       }
Surely this should be documented at least on pg_backbackup page.
+   else if (strcmp(*newval, "controlfile") == 0 || strcmp(*newval, "") == 0)
+       rttg = RECOVERY_TARGET_TIMELINE_CONTROLFILE;
What the idea behind "controlfile" as value for recovery_target_timeline?
+# - Archive Recovery -
+#restore_command = ''      # command to use to restore an archived
logfile segment
+               # placeholders: %p = path of file to restore
+               #               %f = file name only
+               # e.g. 'cp /mnt/server/archivedir/%f %p
It may be good to mention that those parameters are ignored on a primary server.
+                        *
+                        * Note we don't skip STANDBY_SIGNAL_FILE (correct??)
                         */
My guess is that those should be kept in backups by default, and
dropped if -R is used.
+#define RecoveryTargetText(t) ( \
+   t == RECOVERY_TARGET_UNSET ? "unset" : ( \
+   t == RECOVERY_TARGET_XID   ? "xid" : ( \
+   t == RECOVERY_TARGET_TIME  ? "timestamp" : ( \
+   t == RECOVERY_TARGET_NAME  ? "name" : ( \
+   t == RECOVERY_TARGET_LSN   ? "lsn" : \
+                   "immediate" )))))
The default value should be "none", not "immediate".

Perhaps it is time to introduce a xlogrecovery.c and reduce again the
size of xlog.c...
--
Michael

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#91Simon Riggs
simon@2ndquadrant.com
In reply to: Michael Paquier (#90)
Re: Proposal for changes to recovery.conf API

On 25 February 2017 at 13:58, Michael Paquier <michael.paquier@gmail.com> wrote:

- trigger_file is removed.
FWIW, my only complain is about the removal of trigger_file, this is
useful to detect a trigger file on a different partition that PGDATA!
Keeping it costs also nothing..

Sorry, that is just an error of implementation, not intention. I had
it on my list to keep, at your request.

New version coming up.

--
Simon Riggs http://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#92Robert Haas
robertmhaas@gmail.com
In reply to: Simon Riggs (#83)
Re: Proposal for changes to recovery.conf API

On Wed, Jan 11, 2017 at 11:23 PM, Simon Riggs <simon@2ndquadrant.com> wrote:

I think the issue was that some people didn't want configuration files
in the data directory. By removing recovery.conf we accomplish that.
Signal/trigger files are not configuration (or at least it's much easier
to argue that), so I think having them in the data directory is fine.

There were a considerable number of people that pushed to make the
data directory non-user writable, which is where the signal directory
came from.

Specifically, it's a problem for Debian's packaging conventions,
right? The data directory can contain anything that the server itself
will write, but configuration files that are written for the server to
read are supposed to go in some external location dictated by Debian's
packaging policy.

Things like trigger files aren't configuration files per se, so maybe
it's OK if those still get written into the data directory. Even if
not, that seems like a separate patch. In my view, based on Michael's
description of what the current patch version does, it's a clear step
forward. Other steps can be taken at another time, if required.

--
Robert Haas
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#93Robert Haas
robertmhaas@gmail.com
In reply to: Michael Paquier (#90)
Re: Proposal for changes to recovery.conf API

On Sat, Feb 25, 2017 at 7:28 PM, Michael Paquier
<michael.paquier@gmail.com> wrote:

- pg_basebackup -R generates recovery.conf.auto.

Does anything cause that file to get read?

Wouldn't it be better to just append to postgresql.conf.auto?

--
Robert Haas
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#94Josh Berkus
josh@berkus.org
In reply to: Robert Haas (#92)
Re: Proposal for changes to recovery.conf API

On 02/26/2017 12:55 AM, Robert Haas wrote:

On Wed, Jan 11, 2017 at 11:23 PM, Simon Riggs <simon@2ndquadrant.com> wrote:

I think the issue was that some people didn't want configuration files
in the data directory. By removing recovery.conf we accomplish that.
Signal/trigger files are not configuration (or at least it's much easier
to argue that), so I think having them in the data directory is fine.

There were a considerable number of people that pushed to make the
data directory non-user writable, which is where the signal directory
came from.

Specifically, it's a problem for Debian's packaging conventions,
right? The data directory can contain anything that the server itself
will write, but configuration files that are written for the server to
read are supposed to go in some external location dictated by Debian's
packaging policy.

Things like trigger files aren't configuration files per se, so maybe
it's OK if those still get written into the data directory. Even if
not, that seems like a separate patch. In my view, based on Michael's
description of what the current patch version does, it's a clear step
forward. Other steps can be taken at another time, if required.

From the perspective of containerized Postgres, you want config files to
go into one (non-writeable) directory, and anything which is writeable
by the DB server to go into another directory (and preferably, a single
directory).

A trigger file (that is, assuming an empty one, and recovery config
merged with pg.conf) would thus be writeable, non-configuration data
which goes in the data directory.

Users manually writing the trigger file doesn't show up as a problem
since, in a containerized environment, they can't. It's either written
by postgres itself, or by management software which runs as the postgres
user.

--
Josh Berkus
Containers & Databases Oh My!

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#95Michael Paquier
michael.paquier@gmail.com
In reply to: Robert Haas (#93)
Re: Proposal for changes to recovery.conf API

On Sun, Feb 26, 2017 at 5:56 PM, Robert Haas <robertmhaas@gmail.com> wrote:

On Sat, Feb 25, 2017 at 7:28 PM, Michael Paquier
<michael.paquier@gmail.com> wrote:

- pg_basebackup -R generates recovery.conf.auto.

Does anything cause that file to get read?

Wouldn't it be better to just append to postgresql.conf.auto?

Yeah, that would be cleaner than having the backend look for an extra
hardcoded path. Looking at pg_basebackup.c, actually it would not be
difficult to append data to an existing file: look for the file in the
tar stream, and when it is here save its content for later and bypass
it. Once the tar stream is written, just use the data saved previously
and append the parameters at the end of it.
--
Michael

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#96David Steele
david@pgmasters.net
In reply to: Simon Riggs (#91)
Re: Proposal for changes to recovery.conf API

Hi Simon,

On 2/25/17 2:43 PM, Simon Riggs wrote:

On 25 February 2017 at 13:58, Michael Paquier <michael.paquier@gmail.com> wrote:

- trigger_file is removed.
FWIW, my only complain is about the removal of trigger_file, this is
useful to detect a trigger file on a different partition that PGDATA!
Keeping it costs also nothing..

Sorry, that is just an error of implementation, not intention. I had
it on my list to keep, at your request.

New version coming up.

Do you have an idea when the new version will be available?

Thanks,
--
-David
david@pgmasters.net

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#97Josh Berkus
josh@berkus.org
In reply to: David Steele (#96)
Re: Proposal for changes to recovery.conf API

On 03/02/2017 07:13 AM, David Steele wrote:

Hi Simon,

On 2/25/17 2:43 PM, Simon Riggs wrote:

On 25 February 2017 at 13:58, Michael Paquier <michael.paquier@gmail.com> wrote:

- trigger_file is removed.
FWIW, my only complain is about the removal of trigger_file, this is
useful to detect a trigger file on a different partition that PGDATA!
Keeping it costs also nothing..

Sorry, that is just an error of implementation, not intention. I had
it on my list to keep, at your request.

New version coming up.

Do you have an idea when the new version will be available?

Please? Having yet another PostgreSQL release go by without fixing
recovery.conf would make me very sad.

--
Josh Berkus
Containers & Databases Oh My!

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#98Simon Riggs
simon@2ndquadrant.com
In reply to: Josh Berkus (#97)
Re: Proposal for changes to recovery.conf API

On 7 March 2017 at 23:31, Josh Berkus <josh@berkus.org> wrote:

On 03/02/2017 07:13 AM, David Steele wrote:

Hi Simon,

On 2/25/17 2:43 PM, Simon Riggs wrote:

On 25 February 2017 at 13:58, Michael Paquier <michael.paquier@gmail.com> wrote:

- trigger_file is removed.
FWIW, my only complain is about the removal of trigger_file, this is
useful to detect a trigger file on a different partition that PGDATA!
Keeping it costs also nothing..

Sorry, that is just an error of implementation, not intention. I had
it on my list to keep, at your request.

New version coming up.

Do you have an idea when the new version will be available?

Please? Having yet another PostgreSQL release go by without fixing
recovery.conf would make me very sad.

I share your pain, but there are various things about this patch that
make me uncomfortable. I believe we are looking for an improved design
not just a different design.

I think the best time to commit such a patch is at the beginning of a
new cycle, so people have a chance to pick out pieces they don't like
and incrementally propose changes.

So I am going to mark this MovedToNextCF, barring objections from
committers willing to make it happen in this release.

--
Simon Riggs http://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#99Michael Paquier
michael.paquier@gmail.com
In reply to: Simon Riggs (#98)
Re: Proposal for changes to recovery.conf API

On Mon, Mar 27, 2017 at 5:27 PM, Simon Riggs <simon@2ndquadrant.com> wrote:

I share your pain, but there are various things about this patch that
make me uncomfortable. I believe we are looking for an improved design
not just a different design.

I think the best time to commit such a patch is at the beginning of a
new cycle, so people have a chance to pick out pieces they don't like
and incrementally propose changes.

So I am going to mark this MovedToNextCF, barring objections from
committers willing to make it happen in this release.

+1.
-- 
Michael

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#100Daniel Gustafsson
daniel@yesql.se
In reply to: Simon Riggs (#98)
Re: Proposal for changes to recovery.conf API

On 27 Mar 2017, at 10:27, Simon Riggs <simon@2ndquadrant.com> wrote:

On 7 March 2017 at 23:31, Josh Berkus <josh@berkus.org> wrote:

On 03/02/2017 07:13 AM, David Steele wrote:

Hi Simon,

On 2/25/17 2:43 PM, Simon Riggs wrote:

On 25 February 2017 at 13:58, Michael Paquier <michael.paquier@gmail.com> wrote:

- trigger_file is removed.
FWIW, my only complain is about the removal of trigger_file, this is
useful to detect a trigger file on a different partition that PGDATA!
Keeping it costs also nothing..

Sorry, that is just an error of implementation, not intention. I had
it on my list to keep, at your request.

New version coming up.

Do you have an idea when the new version will be available?

Please? Having yet another PostgreSQL release go by without fixing
recovery.conf would make me very sad.

I share your pain, but there are various things about this patch that
make me uncomfortable. I believe we are looking for an improved design
not just a different design.

I think the best time to commit such a patch is at the beginning of a
new cycle, so people have a chance to pick out pieces they don't like
and incrementally propose changes.

So I am going to mark this MovedToNextCF, barring objections from
committers willing to make it happen in this release.

Next CF has now become This CF, has there been any work on this highly sought
after patch? Would be good to get closure on this early in the v11 cycle.

cheers ./daniel

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#101Simon Riggs
simon@2ndquadrant.com
In reply to: Daniel Gustafsson (#100)
Re: Proposal for changes to recovery.conf API

On 5 September 2017 at 06:47, Daniel Gustafsson <daniel@yesql.se> wrote:

On 27 Mar 2017, at 10:27, Simon Riggs <simon@2ndquadrant.com> wrote:

On 7 March 2017 at 23:31, Josh Berkus <josh@berkus.org> wrote:

On 03/02/2017 07:13 AM, David Steele wrote:

Hi Simon,

On 2/25/17 2:43 PM, Simon Riggs wrote:

On 25 February 2017 at 13:58, Michael Paquier <michael.paquier@gmail.com> wrote:

- trigger_file is removed.
FWIW, my only complain is about the removal of trigger_file, this is
useful to detect a trigger file on a different partition that PGDATA!
Keeping it costs also nothing..

Sorry, that is just an error of implementation, not intention. I had
it on my list to keep, at your request.

New version coming up.

Do you have an idea when the new version will be available?

Please? Having yet another PostgreSQL release go by without fixing
recovery.conf would make me very sad.

I share your pain, but there are various things about this patch that
make me uncomfortable. I believe we are looking for an improved design
not just a different design.

I think the best time to commit such a patch is at the beginning of a
new cycle, so people have a chance to pick out pieces they don't like
and incrementally propose changes.

So I am going to mark this MovedToNextCF, barring objections from
committers willing to make it happen in this release.

Next CF has now become This CF, has there been any work on this highly sought
after patch? Would be good to get closure on this early in the v11 cycle.

I've not worked on this at all, so it isn't ready for re-review and commit.

I get the "lets do it early" thing and will try to extract a subset in October.

--
Simon Riggs http://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#102Michael Paquier
michael.paquier@gmail.com
In reply to: Simon Riggs (#101)
Re: Proposal for changes to recovery.conf API

On Wed, Sep 6, 2017 at 12:31 AM, Simon Riggs <simon@2ndquadrant.com> wrote:

I've not worked on this at all, so it isn't ready for re-review and commit.

I get the "lets do it early" thing and will try to extract a subset in October.

Nice to see that you are still planning to work on that. I would
suggest to move this item to the next open CF then, with "waiting on
author" as status.
--
Michael

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers