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 options path + + + + + + + @@ -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)