*** a/doc/src/sgml/high-availability.sgml --- b/doc/src/sgml/high-availability.sgml *************** *** 615,621 **** protocol to make nodes agree on a serializable transactional order. Standby mode is exited and the server switches to normal operation, ! when a trigger file is found (trigger_file). Before failover, any WAL immediately available in the archive or in pg_xlog will be restored, but no attempt is made to connect to the master. --- 615,622 ---- Standby mode is exited and the server switches to normal operation, ! when pg_ctl promote is run or a trigger file is found ! (trigger_file). Before failover, any WAL immediately available in the archive or in pg_xlog will be restored, but no attempt is made to connect to the master. *************** *** 684,694 **** protocol to make nodes agree on a serializable transactional order. If you're setting up the standby server for high availability purposes, set up WAL archiving, connections and authentication like the primary server, because the standby server will work as a primary server after ! failover. You will also need to set trigger_file to make ! it possible to fail over. ! If you're setting up the standby server for reporting ! purposes, with no plans to fail over to it, trigger_file ! is not required. --- 685,692 ---- If you're setting up the standby server for high availability purposes, set up WAL archiving, connections and authentication like the primary server, because the standby server will work as a primary server after ! failover. If you're planning to use pg_ctl promote to ! fail over, trigger_file is not required. *************** *** 709,715 **** protocol to make nodes agree on a serializable transactional order. standby_mode = 'on' primary_conninfo = 'host=192.168.1.50 port=5432 user=foo password=foopass' restore_command = 'cp /path/to/archive/%f %p' - trigger_file = '/path/to/trigger_file' archive_cleanup_command = 'pg_archivecleanup /path/to/archive %r' --- 707,712 ---- *************** *** 948,960 **** primary_conninfo = 'host=192.168.1.50 port=5432 user=foo password=foopass' ! To trigger failover of a log-shipping standby server, create a trigger file with the filename and path specified by the trigger_file ! setting in recovery.conf. If trigger_file is ! not given, there is no way to exit recovery in the standby and promote ! it to a master. That can be useful for e.g reporting servers that are ! only used to offload read-only queries from the primary, not for high ! availability purposes. --- 945,956 ---- ! To trigger failover of a log-shipping standby server, ! run pg_ctl promote or create a trigger file with the filename and path specified by the trigger_file ! setting in recovery.conf. If you're planning to use ! pg_ctl promote to fail over, trigger_file is ! not required. *** a/doc/src/sgml/recovery-config.sgml --- b/doc/src/sgml/recovery-config.sgml *************** *** 298,305 **** restore_command = 'copy "C:\\server\\archivedir\\%f" "%p"' # Windows Specifies a trigger file whose presence ends recovery in the ! standby. If no trigger file is specified, the standby never exits ! recovery. This setting has no effect if standby_mode is off. --- 298,305 ---- Specifies a trigger file whose presence ends recovery in the ! standby. If you plan to use pg_ctl promote to promote ! the standby, no trigger file needs to be specified. This setting has no effect if standby_mode is off. *** a/doc/src/sgml/ref/pg_ctl-ref.sgml --- b/doc/src/sgml/ref/pg_ctl-ref.sgml *************** *** 77,82 **** PostgreSQL documentation --- 77,89 ---- pg_ctl + promote + -s + -D datadir + + + + pg_ctl reload -s -D datadir *************** *** 184,189 **** PostgreSQL documentation --- 191,201 ---- + In mode, the standby server that is + running in the specified data directory is promoted to the primary. + + + mode simply sends the postgres process a SIGHUP signal, causing it to reread its configuration files *** a/src/backend/access/transam/xlog.c --- b/src/backend/access/transam/xlog.c *************** *** 64,69 **** --- 64,70 ---- #define BACKUP_LABEL_OLD "backup_label.old" #define RECOVERY_COMMAND_FILE "recovery.conf" #define RECOVERY_COMMAND_DONE "recovery.done" + #define PROMOTE_SIGNAL_FILE "promote" /* User-settable parameters */ *************** *** 546,551 **** typedef struct xl_parameter_change --- 547,553 ---- */ static volatile sig_atomic_t got_SIGHUP = false; static volatile sig_atomic_t shutdown_requested = false; + static volatile sig_atomic_t promote_triggered = false; /* * Flag set when executing a restore command, to tell SIGTERM signal handler *************** *** 9196,9201 **** StartupProcSigUsr1Handler(SIGNAL_ARGS) --- 9198,9211 ---- latch_sigusr1_handler(); } + /* SIGUSR2: set flag to finish recovery */ + static void + StartupProcTriggerHandler(SIGNAL_ARGS) + { + promote_triggered = true; + WakeupRecovery(); + } + /* SIGHUP: set flag to re-read config file at next convenient time */ static void StartupProcSigHupHandler(SIGNAL_ARGS) *************** *** 9273,9279 **** StartupProcessMain(void) pqsignal(SIGALRM, SIG_IGN); pqsignal(SIGPIPE, SIG_IGN); pqsignal(SIGUSR1, StartupProcSigUsr1Handler); ! pqsignal(SIGUSR2, SIG_IGN); /* * Reset some signals that are accepted by postmaster but not here --- 9283,9289 ---- pqsignal(SIGALRM, SIG_IGN); pqsignal(SIGPIPE, SIG_IGN); pqsignal(SIGUSR1, StartupProcSigUsr1Handler); ! pqsignal(SIGUSR2, StartupProcTriggerHandler); /* * Reset some signals that are accepted by postmaster but not here *************** *** 9719,9733 **** emode_for_corrupt_record(int emode, XLogRecPtr RecPtr) } /* ! * Check to see if the trigger file exists. If it does, request postmaster ! * to shut down walreceiver, wait for it to exit, remove the trigger ! * file, and return true. */ static bool CheckForStandbyTrigger(void) { struct stat stat_buf; if (TriggerFile == NULL) return false; --- 9729,9753 ---- } /* ! * Check to see if the user-specified trigger file exists and ! * if a promote request has arrived. If either does, ! * request postmaster to shut down walreceiver, wait for it to exit, ! * and return true. */ static bool CheckForStandbyTrigger(void) { struct stat stat_buf; + if (promote_triggered) + { + ereport(LOG, + (errmsg("received promote request"))); + ShutdownWalRcv(); + promote_triggered = false; + return true; + } + if (TriggerFile == NULL) return false; *************** *** 9743,9748 **** CheckForStandbyTrigger(void) --- 9763,9789 ---- } /* + * Check to see if a promote request has arrived. Should be + * called by postmaster after receiving SIGUSR1. + */ + bool + CheckPromoteSignal(void) + { + struct stat stat_buf; + + if (stat(PROMOTE_SIGNAL_FILE, &stat_buf) == 0) + { + /* + * Since we are in a signal handler, it's not safe + * to elog. We silently ignore the error of unlink. + */ + unlink(PROMOTE_SIGNAL_FILE); + return true; + } + return false; + } + + /* * Wake up startup process to replay newly arrived WAL, or to notice that * failover has been requested. */ *** a/src/backend/postmaster/postmaster.c --- b/src/backend/postmaster/postmaster.c *************** *** 4272,4277 **** sigusr1_handler(SIGNAL_ARGS) --- 4272,4285 ---- WalReceiverPID = StartWalReceiver(); } + if (CheckPromoteSignal() && StartupPID != 0 && + (pmState == PM_STARTUP || pmState == PM_RECOVERY || + pmState == PM_HOT_STANDBY || pmState == PM_WAIT_READONLY)) + { + /* Tell startup process to finish recovery */ + signal_child(StartupPID, SIGUSR2); + } + PG_SETMASK(&UnBlockSig); errno = save_errno; *** a/src/bin/pg_ctl/pg_ctl.c --- b/src/bin/pg_ctl/pg_ctl.c *************** *** 62,67 **** typedef enum --- 62,68 ---- START_COMMAND, STOP_COMMAND, RESTART_COMMAND, + PROMOTE_COMMAND, RELOAD_COMMAND, STATUS_COMMAND, KILL_COMMAND, *************** *** 96,101 **** static char postopts_file[MAXPGPATH]; --- 97,103 ---- static char pid_file[MAXPGPATH]; static char backup_file[MAXPGPATH]; static char recovery_file[MAXPGPATH]; + static char promote_file[MAXPGPATH]; #if defined(WIN32) || defined(__CYGWIN__) static DWORD pgctl_start_type = SERVICE_AUTO_START; *************** *** 124,129 **** static void do_init(void); --- 126,132 ---- static void do_start(void); static void do_stop(void); static void do_restart(void); + static void do_promote(void); static void do_reload(void); static void do_status(void); static void do_kill(pgpid_t pid); *************** *** 872,878 **** do_stop(void) /* ! * restart/reload routines */ static void --- 875,881 ---- /* ! * restart/promote/reload routines */ static void *************** *** 965,970 **** do_restart(void) --- 968,1033 ---- do_start(); } + static void + do_promote(void) + { + FILE *prmfile; + pgpid_t pid; + struct stat statbuf; + + pid = get_pgpid(); + + if (pid == 0) /* no pid file */ + { + write_stderr(_("%s: PID file \"%s\" does not exist\n"), progname, pid_file); + write_stderr(_("Is server running?\n")); + exit(1); + } + else if (pid < 0) /* standalone backend, not postmaster */ + { + pid = -pid; + write_stderr(_("%s: cannot promote server; " + "single-user server is running (PID: %ld)\n"), + progname, pid); + exit(1); + } + + /* If recovery.conf doesn't exist, the server is not in standby mode */ + if (stat(recovery_file, &statbuf) != 0) + { + write_stderr(_("%s: cannot promote server; " + "server is not in standby mode\n"), + progname); + exit(1); + } + + if ((prmfile = fopen(promote_file, "w")) == NULL) + { + write_stderr(_("%s: could not create promote signal file \"%s\": %s\n"), + progname, promote_file, strerror(errno)); + exit(1); + } + if (fclose(prmfile)) + { + write_stderr(_("%s: could not write promote signal file \"%s\": %s\n"), + progname, promote_file, strerror(errno)); + exit(1); + } + + sig = SIGUSR1; + if (kill((pid_t) pid, sig) != 0) + { + write_stderr(_("%s: could not send promote signal (PID: %ld): %s\n"), + progname, pid, strerror(errno)); + if (unlink(promote_file) != 0) + write_stderr(_("%s: could not remove promote signal file \"%s\": %s\n"), + progname, promote_file, strerror(errno)); + exit(1); + } + + print_msg(_("server promoting\n")); + } + static void do_reload(void) *************** *** 1617,1623 **** do_advice(void) static void do_help(void) { ! printf(_("%s is a utility to start, stop, restart, reload configuration files,\n" "report the status of a PostgreSQL server, or signal a PostgreSQL process.\n\n"), progname); printf(_("Usage:\n")); printf(_(" %s init[db] [-D DATADIR] [-s] [-o \"OPTIONS\"]\n"), progname); --- 1680,1686 ---- static void do_help(void) { ! printf(_("%s is a utility to start, stop, restart, promote, reload configuration files,\n" "report the status of a PostgreSQL server, or signal a PostgreSQL process.\n\n"), progname); printf(_("Usage:\n")); printf(_(" %s init[db] [-D DATADIR] [-s] [-o \"OPTIONS\"]\n"), progname); *************** *** 1625,1630 **** do_help(void) --- 1688,1694 ---- 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); + printf(_(" %s promote [-D DATADIR] [-s]\n"), progname); printf(_(" %s reload [-D DATADIR] [-s]\n"), progname); printf(_(" %s status [-D DATADIR]\n"), progname); printf(_(" %s kill SIGNALNAME PID\n"), progname); *************** *** 1950,1955 **** main(int argc, char **argv) --- 2014,2021 ---- ctl_command = STOP_COMMAND; else if (strcmp(argv[optind], "restart") == 0) ctl_command = RESTART_COMMAND; + else if (strcmp(argv[optind], "promote") == 0) + ctl_command = PROMOTE_COMMAND; else if (strcmp(argv[optind], "reload") == 0) ctl_command = RELOAD_COMMAND; else if (strcmp(argv[optind], "status") == 0) *************** *** 2036,2041 **** main(int argc, char **argv) --- 2102,2108 ---- 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(promote_file, MAXPGPATH, "%s/promote", pg_data); } switch (ctl_command) *************** *** 2055,2060 **** main(int argc, char **argv) --- 2122,2130 ---- case RESTART_COMMAND: do_restart(); break; + case PROMOTE_COMMAND: + do_promote(); + break; case RELOAD_COMMAND: do_reload(); break; *** a/src/include/access/xlog.h --- b/src/include/access/xlog.h *************** *** 310,315 **** extern TimeLineID GetRecoveryTargetTLI(void); --- 310,316 ---- extern void HandleStartupProcInterrupts(void); extern void StartupProcessMain(void); + extern bool CheckPromoteSignal(void); extern void WakeupRecovery(void); extern XLogRecPtr do_pg_start_backup(const char *backupidstr, bool fast);