*** a/doc/src/sgml/func.sgml --- b/doc/src/sgml/func.sgml *************** *** 13602,13607 **** postgres=# SELECT * FROM pg_xlogfile_name_offset(pg_stop_backup()); --- 13602,13622 ---- the function returns NULL. + + + pg_trigger_failover() + + bool + Request the standby to end recovery and become the master. + Even if is not specified, this triggers + failover. But it's important to specify trigger_file + to enable failover even when the server is not in Hot Standby mode and + the function is not allowed to run. Superuser permission is required + to trigger failover. This function returns false if the + server is not running recovery, true otherwise. Note that + this cannot bring up the standby which uses pg_standby. + + *** a/doc/src/sgml/high-availability.sgml --- b/doc/src/sgml/high-availability.sgml *************** *** 610,621 **** protocol to make nodes agree on a serializable transactional order. later disconnected, the standby goes back to step 1 and tries to restore the file from the archive again. This loop of retries from the archive, pg_xlog, and via streaming replication goes on until the server ! is stopped or failover is triggered by a trigger file. 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. --- 610,623 ---- later disconnected, the standby goes back to step 1 and tries to restore the file from the archive again. This loop of retries from the archive, pg_xlog, and via streaming replication goes on until the server ! is stopped or failover is triggered by a trigger file or ! pg_trigger_failover. Standby mode is exited and the server switches to normal operation, ! when a trigger file is found (trigger_file) or ! pg_trigger_failover is called. 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. *************** *** 694,699 **** protocol to make nodes agree on a serializable transactional order. --- 696,706 ---- If you're setting up the standby server for reporting purposes, with no plans to fail over to it, trigger_file is not required. + Even if trigger_file is not specified, you can trigger + failover by using pg_trigger_failover. But it's important + to specify trigger_file to enable failover even when + the server is not in Hot Standby mode and the function is not allowed + to run. *************** *** 927,934 **** 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. --- 934,942 ---- 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 or call pg_trigger_failover. ! If trigger_file is not given and hot_standby is not ! enabled, 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. *** a/doc/src/sgml/recovery-config.sgml --- b/doc/src/sgml/recovery-config.sgml *************** *** 288,295 **** 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. --- 288,296 ---- Specifies a trigger file whose presence ends recovery in the ! standby. Even if no trigger file is specified, you can request the standby ! to exit recovery by using pg_trigger_failover if hot standby ! is enabled. This setting has no effect if standby_mode is off. *** a/src/backend/access/transam/xlog.c --- b/src/backend/access/transam/xlog.c *************** *** 416,421 **** typedef struct XLogCtlData --- 416,427 ---- /* timestamp of last COMMIT/ABORT record replayed (or being replayed) */ TimestampTz recoveryLastXTime; + /* + * failoverRequested indicates if failover has been requested via + * pg_trigger_failover. Protected by info_lck. + */ + bool failoverRequested; + slock_t info_lck; /* locks shared variables shown above */ } XLogCtlData; *************** *** 8869,8874 **** pg_last_xlog_replay_location(PG_FUNCTION_ARGS) --- 8875,8914 ---- } /* + * Promote the standby to the master by requesting startup process to + * end recovery. + * + * Even if trigger_file parameter is not specified in recovery.conf, + * this function triggers failover. + * + * Returns FALSE if there is no startup process that we request to + * end recovery, TRUE otherwise. + * + * XXX: Should this return FALSE if standby mode is not enabled too? + */ + Datum + pg_trigger_failover(PG_FUNCTION_ARGS) + { + /* use volatile pointer to prevent code rearrangement */ + volatile XLogCtlData *xlogctl = XLogCtl; + + if (!superuser()) + ereport(ERROR, + (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), + (errmsg("must be superuser to trigger failover")))); + + if (!RecoveryInProgress()) + PG_RETURN_BOOL(false); + + SpinLockAcquire(&xlogctl->info_lck); + xlogctl->failoverRequested = true; + SpinLockRelease(&xlogctl->info_lck); + + WakeupRecovery(); + PG_RETURN_BOOL(true); + } + + /* * Compute an xlog file name and decimal byte offset given a WAL location, * such as is returned by pg_stop_backup() or pg_xlog_switch(). * *************** *** 9516,9524 **** retry: failedSources |= sources; /* ! * Check to see if the trigger file exists. Note that we ! * do this only after failure, so when you create the ! * trigger file, we still finish replaying as much as we * can from archive and pg_xlog before failover. */ if (CheckForStandbyTrigger()) --- 9556,9564 ---- failedSources |= sources; /* ! * Check to see if failover has been requested. Note that ! * we do this only after failure, so when you request ! * failover, we still finish replaying as much as we * can from archive and pg_xlog before failover. */ if (CheckForStandbyTrigger()) *************** *** 9690,9704 **** 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; --- 9730,9764 ---- } /* ! * Check to see if failover has been requested. If it's been done, ! * request postmaster to shut down walreceiver, wait for it to exit, ! * remove the trigger file if the request has come via it, ! * and return true. */ static bool CheckForStandbyTrigger(void) { + /* use volatile pointer to prevent code rearrangement */ + volatile XLogCtlData *xlogctl = XLogCtl; + bool failoverRequested = false; struct stat stat_buf; + SpinLockAcquire(&xlogctl->info_lck); + failoverRequested = xlogctl->failoverRequested; + + /* + * It's not strictly necessary to reset the flag since we will not re-enter + * the standby mode after failover, but let's do it for the sake of tidiness. + */ + xlogctl->failoverRequested = false; + SpinLockRelease(&xlogctl->info_lck); + + if (failoverRequested) + { + ShutdownWalRcv(); + return true; + } + if (TriggerFile == NULL) return false; *** a/src/include/access/xlog_internal.h --- b/src/include/access/xlog_internal.h *************** *** 271,276 **** extern Datum pg_current_xlog_location(PG_FUNCTION_ARGS); --- 271,277 ---- extern Datum pg_current_xlog_insert_location(PG_FUNCTION_ARGS); extern Datum pg_last_xlog_receive_location(PG_FUNCTION_ARGS); extern Datum pg_last_xlog_replay_location(PG_FUNCTION_ARGS); + extern Datum pg_trigger_failover(PG_FUNCTION_ARGS); extern Datum pg_xlogfile_name_offset(PG_FUNCTION_ARGS); extern Datum pg_xlogfile_name(PG_FUNCTION_ARGS); extern Datum pg_is_in_recovery(PG_FUNCTION_ARGS); *** a/src/include/catalog/pg_proc.h --- b/src/include/catalog/pg_proc.h *************** *** 3380,3385 **** DATA(insert OID = 3820 ( pg_last_xlog_receive_location PGNSP PGUID 12 1 0 0 f f --- 3380,3387 ---- DESCR("current xlog flush location"); DATA(insert OID = 3821 ( pg_last_xlog_replay_location PGNSP PGUID 12 1 0 0 f f f t f v 0 0 25 "" _null_ _null_ _null_ _null_ pg_last_xlog_replay_location _null_ _null_ _null_ )); DESCR("last xlog replay location"); + DATA(insert OID = 3819 ( pg_trigger_failover PGNSP PGUID 12 1 0 0 f f f t f v 0 0 16 "" _null_ _null_ _null_ _null_ pg_trigger_failover _null_ _null_ _null_ )); + DESCR("trigger failover"); DATA(insert OID = 2621 ( pg_reload_conf PGNSP PGUID 12 1 0 0 f f f t f v 0 0 16 "" _null_ _null_ _null_ _null_ pg_reload_conf _null_ _null_ _null_ )); DESCR("reload configuration files");