*** 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 + failover + -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 FAILOVER_SIGNAL_FILE "failover" /* 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 standby_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) + { + standby_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 failover has been requested by pg_ctl. 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 (standby_triggered) + { + ereport(LOG, + (errmsg("failover requested"))); + ShutdownWalRcv(); + standby_triggered = false; + return true; + } + if (TriggerFile == NULL) return false; *************** *** 9743,9748 **** CheckForStandbyTrigger(void) --- 9763,9789 ---- } /* + * Check to see if a failover has been requested by pg_ctl. Should be + * called by postmaster after receiving SIGUSR1. + */ + bool + CheckFailoverSignal(void) + { + struct stat stat_buf; + + if (stat(FAILOVER_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(FAILOVER_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 *************** *** 4271,4276 **** sigusr1_handler(SIGNAL_ARGS) --- 4271,4284 ---- WalReceiverPID = StartWalReceiver(); } + if (CheckFailoverSignal() && 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 *************** *** 63,68 **** typedef enum --- 63,69 ---- START_COMMAND, STOP_COMMAND, RESTART_COMMAND, + FAILOVER_COMMAND, RELOAD_COMMAND, STATUS_COMMAND, KILL_COMMAND, *************** *** 107,112 **** static void do_init(void); --- 108,114 ---- static void do_start(void); static void do_stop(void); static void do_restart(void); + static void do_failover(void); static void do_reload(void); static void do_status(void); static void do_kill(pgpid_t pid); *************** *** 146,151 **** static char postopts_file[MAXPGPATH]; --- 148,154 ---- static char pid_file[MAXPGPATH]; static char backup_file[MAXPGPATH]; static char recovery_file[MAXPGPATH]; + static char failover_file[MAXPGPATH]; #if defined(HAVE_GETRLIMIT) && defined(RLIMIT_CORE) static void unlimit_core_size(void); *************** *** 871,877 **** do_stop(void) /* ! * restart/reload routines */ static void --- 874,880 ---- /* ! * restart/failover/reload routines */ static void *************** *** 964,969 **** do_restart(void) --- 967,1029 ---- do_start(); } + static void + do_failover(void) + { + FILE *fofile; + 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 request failover; " + "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 request failover; " + "server is not in standby mode\n"), + progname); + exit(1); + } + + if ((fofile = fopen(failover_file, "w")) == NULL) + { + write_stderr(_("%s: could not create failover signal file \"%s\": %s\n"), + progname, failover_file, strerror(errno)); + exit(1); + } + if (fclose(fofile)) + { + write_stderr(_("%s: could not write failover signal file \"%s\": %s\n"), + progname, failover_file, strerror(errno)); + exit(1); + } + + sig = SIGUSR1; + if (kill((pid_t) pid, sig) != 0) + { + write_stderr(_("%s: could not send failover signal (PID: %ld): %s\n"), + progname, pid, strerror(errno)); + exit(1); + } + + print_msg(_("failover requested\n")); + } + static void do_reload(void) *************** *** 1616,1632 **** 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); ! printf(_(" %s start [-w] [-t SECS] [-D DATADIR] [-s] [-l FILENAME] [-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); ! printf(_(" %s reload [-D DATADIR] [-s]\n"), progname); ! printf(_(" %s status [-D DATADIR]\n"), progname); ! printf(_(" %s kill SIGNALNAME PID\n"), progname); #if defined(WIN32) || defined(__CYGWIN__) printf(_(" %s register [-N SERVICENAME] [-U USERNAME] [-P PASSWORD] [-D DATADIR]\n" " [-S START-TYPE] [-w] [-t SECS] [-o \"OPTIONS\"]\n"), progname); --- 1676,1693 ---- static void do_help(void) { ! printf(_("%s is a utility to start, stop, restart, failover, 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); ! printf(_(" %s start [-w] [-t SECS] [-D DATADIR] [-s] [-l FILENAME] [-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); ! printf(_(" %s failover [-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); #if defined(WIN32) || defined(__CYGWIN__) printf(_(" %s register [-N SERVICENAME] [-U USERNAME] [-P PASSWORD] [-D DATADIR]\n" " [-S START-TYPE] [-w] [-t SECS] [-o \"OPTIONS\"]\n"), progname); *************** *** 1949,1954 **** main(int argc, char **argv) --- 2010,2017 ---- ctl_command = STOP_COMMAND; else if (strcmp(argv[optind], "restart") == 0) ctl_command = RESTART_COMMAND; + else if (strcmp(argv[optind], "failover") == 0) + ctl_command = FAILOVER_COMMAND; else if (strcmp(argv[optind], "reload") == 0) ctl_command = RELOAD_COMMAND; else if (strcmp(argv[optind], "status") == 0) *************** *** 2035,2040 **** main(int argc, char **argv) --- 2098,2104 ---- 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(failover_file, MAXPGPATH, "%s/failover", pg_data); } switch (ctl_command) *************** *** 2054,2059 **** main(int argc, char **argv) --- 2118,2126 ---- case RESTART_COMMAND: do_restart(); break; + case FAILOVER_COMMAND: + do_failover(); + 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 CheckFailoverSignal(void); extern void WakeupRecovery(void); extern XLogRecPtr do_pg_start_backup(const char *backupidstr, bool fast);