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
optionspath
+
+
+
+
+
+
+
@@ -72,6 +79,14 @@ PostgreSQL documentation
+
+
+
+
+
+
+
+ options
@@ -167,6 +182,15 @@ PostgreSQL documentation
are sent to the terminal. These default behaviors can be changed
by using to append the server's output to a log file.
Use of either or output redirection is recommended.
+ Different startup methods can be selected with the
+ option. Normal mode starts the server for read/write,
+ overriding any previous use in Recover mode.
+ Recover 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.
+ Continue 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.
@@ -215,7 +239,7 @@ PostgreSQL documentation
In 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).
@@ -292,7 +316,20 @@ PostgreSQL documentation
Specifies the shutdown mode. mode
can be smart, fast, or
immediate, or the first letter of one of
- these three. If this is omitted, fast is used.
+ these. If this is omitted, fast is used.
+
+
+
+
+
+
+
+
+
+ Specifies the start mode. start-mode
+ can be normal, or recover,
+ or continue, or the first letter of one of
+ these. If this is omitted, continue is used.
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)