From bdf9a409005f0ea209c640935d9827369725e241 Mon Sep 17 00:00:00 2001 From: Elvis Pranskevichus Date: Fri, 17 Mar 2017 13:25:08 -0400 Subject: [PATCH v1] Add and report the new "in_hot_standby" GUC pseudo-variable. Currently, clients wishing to know when the server exits hot standby have to resort to polling, which is suboptimal. This adds the new "in_hot_standby" GUC variable that is reported via a ParameterStatus message. This allows the clients to: (a) know right away that they are connected to a server in hot standby; and (b) know immediately when a server exits hot standby. This change will be most beneficial to various connection pooling systems. --- doc/src/sgml/high-availability.sgml | 4 +-- doc/src/sgml/libpq.sgml | 1 + doc/src/sgml/protocol.sgml | 1 + doc/src/sgml/ref/show.sgml | 9 +++++++ src/backend/access/transam/xlog.c | 4 ++- src/backend/commands/async.c | 48 ++++++++++++++++++++++++++++++++++++ src/backend/storage/ipc/procarray.c | 30 ++++++++++++++++++++++ src/backend/storage/ipc/procsignal.c | 3 +++ src/backend/storage/ipc/standby.c | 9 +++++++ src/backend/tcop/postgres.c | 6 ++++- src/backend/utils/init/postinit.c | 6 +++++ src/backend/utils/misc/check_guc | 10 ++++---- src/backend/utils/misc/guc.c | 15 +++++++++++ src/include/commands/async.h | 7 ++++++ src/include/storage/procarray.h | 2 ++ src/include/storage/procsignal.h | 2 ++ src/include/storage/standby.h | 1 + 17 files changed, 149 insertions(+), 9 deletions(-) diff --git a/doc/src/sgml/high-availability.sgml b/doc/src/sgml/high-availability.sgml index cc84b911b0..44795c5bcc 100644 --- a/doc/src/sgml/high-availability.sgml +++ b/doc/src/sgml/high-availability.sgml @@ -1831,8 +1831,8 @@ if (!triggered) - Users will be able to tell whether their session is read-only by - issuing SHOW transaction_read_only. In addition, a set of + Users will be able to tell whether their session is in hot standby mode by + issuing SHOW in_hot_standby. In addition, a set of functions () allow users to access information about the standby server. These allow you to write programs that are aware of the current state of the database. These diff --git a/doc/src/sgml/libpq.sgml b/doc/src/sgml/libpq.sgml index 4bc5bf3192..367ec4460d 100644 --- a/doc/src/sgml/libpq.sgml +++ b/doc/src/sgml/libpq.sgml @@ -1706,6 +1706,7 @@ const char *PQparameterStatus(const PGconn *conn, const char *paramName); server_encoding, client_encoding, application_name, + in_hot_standby, is_superuser, session_authorization, DateStyle, diff --git a/doc/src/sgml/protocol.sgml b/doc/src/sgml/protocol.sgml index 7c82b48845..0fafaf6788 100644 --- a/doc/src/sgml/protocol.sgml +++ b/doc/src/sgml/protocol.sgml @@ -1123,6 +1123,7 @@ server_encoding, client_encoding, application_name, + in_hot_standby, is_superuser, session_authorization, DateStyle, diff --git a/doc/src/sgml/ref/show.sgml b/doc/src/sgml/ref/show.sgml index 46bb239baf..cf21bd961a 100644 --- a/doc/src/sgml/ref/show.sgml +++ b/doc/src/sgml/ref/show.sgml @@ -78,6 +78,15 @@ SHOW ALL + IN_HOT_STANDBY + + + True if the server is in Hot Standby mode. + + + + + LC_COLLATE diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c index cdb3a8ac1d..acca53b12f 100644 --- a/src/backend/access/transam/xlog.c +++ b/src/backend/access/transam/xlog.c @@ -7654,8 +7654,10 @@ StartupXLOG(void) * Shutdown the recovery environment. This must occur after * RecoverPreparedTransactions(), see notes for lock_twophase_recover() */ - if (standbyState != STANDBY_DISABLED) + if (standbyState != STANDBY_DISABLED) { ShutdownRecoveryTransactionEnvironment(); + SendHotStandbyExitSignal(); + } /* Shut down xlogreader */ if (readFile >= 0) diff --git a/src/backend/commands/async.c b/src/backend/commands/async.c index e32d7a1d4e..8bc365489a 100644 --- a/src/backend/commands/async.c +++ b/src/backend/commands/async.c @@ -355,6 +355,8 @@ static List *upperPendingNotifies = NIL; /* list of upper-xact lists */ */ volatile sig_atomic_t notifyInterruptPending = false; +volatile sig_atomic_t hotStandbyExitInterruptPending = false; + /* True if we've registered an on_shmem_exit cleanup */ static bool unlistenExitRegistered = false; @@ -1734,6 +1736,52 @@ ProcessNotifyInterrupt(void) /* + * HandleHotStandbyExitInterrupt + * + * Signal handler portion of interrupt handling. Let the backend know + * that the server has exited the recovery mode. + */ +void +HandleHotStandbyExitInterrupt(void) +{ + /* + * Note: this is called by a SIGNAL HANDLER. You must be very wary what + * you do here. + */ + + /* signal that work needs to be done */ + hotStandbyExitInterruptPending = true; + + /* make sure the event is processed in due course */ + SetLatch(MyLatch); +} + + +/* + * ProcessHotStandbyExitInterrupt + * + * This is called just after waiting for a frontend command. If a + * interrupt arrives (via HandleHotStandbyExitInterrupt()) while reading, + * the read will be interrupted via the process's latch, and this routine + * will get called. + */ +void +ProcessHotStandbyExitInterrupt(void) +{ + hotStandbyExitInterruptPending = false; + + SetConfigOption("in_hot_standby", "off", + PGC_INTERNAL, PGC_S_OVERRIDE); + + /* + * Flush output buffer so that clients receive the ParameterStatus + * message as soon as possible. + */ + pq_flush(); +} + + +/* * Read all pending notifications from the queue, and deliver appropriate * ones to my frontend. Stop when we reach queue head or an uncommitted * notification. diff --git a/src/backend/storage/ipc/procarray.c b/src/backend/storage/ipc/procarray.c index 0f8f435faf..b76ae35f87 100644 --- a/src/backend/storage/ipc/procarray.c +++ b/src/backend/storage/ipc/procarray.c @@ -2937,6 +2937,36 @@ CountOtherDBBackends(Oid databaseId, int *nbackends, int *nprepared) } /* + * SendSignalToAllBackends --- send a signal to all backends. + */ +void +SendSignalToAllBackends(ProcSignalReason reason) +{ + ProcArrayStruct *arrayP = procArray; + int index; + pid_t pid = 0; + + LWLockAcquire(ProcArrayLock, LW_SHARED); + + for (index = 0; index < arrayP->numProcs; index++) + { + int pgprocno = arrayP->pgprocnos[index]; + volatile PGPROC *proc = &allProcs[pgprocno]; + VirtualTransactionId procvxid; + + GET_VXID_FROM_PGPROC(procvxid, *proc); + + pid = proc->pid; + if (pid != 0) + { + (void) SendProcSignal(pid, reason, procvxid.backendId); + } + } + + LWLockRelease(ProcArrayLock); +} + +/* * ProcArraySetReplicationSlotXmin * * Install limits to future computations of the xmin horizon to prevent vacuum diff --git a/src/backend/storage/ipc/procsignal.c b/src/backend/storage/ipc/procsignal.c index 4a21d5512d..3918330ee3 100644 --- a/src/backend/storage/ipc/procsignal.c +++ b/src/backend/storage/ipc/procsignal.c @@ -288,6 +288,9 @@ procsignal_sigusr1_handler(SIGNAL_ARGS) if (CheckProcSignal(PROCSIG_RECOVERY_CONFLICT_BUFFERPIN)) RecoveryConflictInterrupt(PROCSIG_RECOVERY_CONFLICT_BUFFERPIN); + if (CheckProcSignal(PROCSIG_HOTSTANDBY_EXIT)) + HandleHotStandbyExitInterrupt(); + SetLatch(MyLatch); latch_sigusr1_handler(); diff --git a/src/backend/storage/ipc/standby.c b/src/backend/storage/ipc/standby.c index 6259070722..f5155dd80c 100644 --- a/src/backend/storage/ipc/standby.c +++ b/src/backend/storage/ipc/standby.c @@ -111,6 +111,15 @@ ShutdownRecoveryTransactionEnvironment(void) VirtualXactLockTableCleanup(); } +/* + * SendHotStandbyExitSignal + * Signal backends that the server has exited Hot Standby. + */ +void +SendHotStandbyExitSignal(void) +{ + SendSignalToAllBackends(PROCSIG_HOTSTANDBY_EXIT); +} /* * ----------------------------------------------------- diff --git a/src/backend/tcop/postgres.c b/src/backend/tcop/postgres.c index b07d6c6cb9..4a2a4abb6f 100644 --- a/src/backend/tcop/postgres.c +++ b/src/backend/tcop/postgres.c @@ -532,9 +532,13 @@ ProcessClientReadInterrupt(bool blocked) if (catchupInterruptPending) ProcessCatchupInterrupt(); - /* Process sinval catchup interrupts that happened while reading */ + /* Process NOTIFY interrupts that happened while reading */ if (notifyInterruptPending) ProcessNotifyInterrupt(); + + /* Process recovery exit interrupts that happened while reading */ + if (hotStandbyExitInterruptPending) + ProcessHotStandbyExitInterrupt(); } else if (ProcDiePending && blocked) { diff --git a/src/backend/utils/init/postinit.c b/src/backend/utils/init/postinit.c index 9f938f2d27..d0041de00b 100644 --- a/src/backend/utils/init/postinit.c +++ b/src/backend/utils/init/postinit.c @@ -1013,6 +1013,12 @@ InitPostgres(const char *in_dbname, Oid dboid, const char *username, * selected the active user and gotten the right GUC settings. */ + /* set in_hot_standby */ + if (HotStandbyActive()) { + SetConfigOption("in_hot_standby", "on", + PGC_INTERNAL, PGC_S_OVERRIDE); + } + /* set default namespace search path */ InitializeSearchPath(); diff --git a/src/backend/utils/misc/check_guc b/src/backend/utils/misc/check_guc index d228bbed68..1795471fd4 100755 --- a/src/backend/utils/misc/check_guc +++ b/src/backend/utils/misc/check_guc @@ -17,11 +17,11 @@ ## postgresql.conf.sample), it should be listed here so that it ## can be ignored INTENTIONALLY_NOT_INCLUDED="debug_deadlocks \ -is_superuser lc_collate lc_ctype lc_messages lc_monetary lc_numeric lc_time \ -pre_auth_delay role seed server_encoding server_version server_version_int \ -session_authorization trace_lock_oidmin trace_lock_table trace_locks trace_lwlocks \ -trace_notify trace_userlocks transaction_isolation transaction_read_only \ -zero_damaged_pages" +is_superuser in_hot_standby lc_collate lc_ctype lc_messages lc_monetary \ +lc_numeric lc_time pre_auth_delay role seed server_encoding server_version \ +server_version_int session_authorization trace_lock_oidmin trace_lock_table \ +trace_locks trace_lwlocks trace_notify trace_userlocks transaction_isolation \ +transaction_read_only zero_damaged_pages" ### What options are listed in postgresql.conf.sample, but don't appear ### in guc.c? diff --git a/src/backend/utils/misc/guc.c b/src/backend/utils/misc/guc.c index 4feb26aa7a..b34553df81 100644 --- a/src/backend/utils/misc/guc.c +++ b/src/backend/utils/misc/guc.c @@ -496,6 +496,7 @@ int huge_pages; */ static char *syslog_ident_str; static bool session_auth_is_superuser; +static bool server_in_hot_standby; static double phony_random_seed; static char *client_encoding_string; static char *datestyle_string; @@ -934,6 +935,20 @@ static struct config_bool ConfigureNamesBool[] = NULL, NULL, NULL }, { + /* + * Not for general use --- used to indicate whether + * the server is in hot standby. + */ + {"in_hot_standby", PGC_INTERNAL, UNGROUPED, + gettext_noop("Shows whether the server is in hot standby mode."), + NULL, + GUC_REPORT | GUC_NO_SHOW_ALL | GUC_NO_RESET_ALL | GUC_NOT_IN_SAMPLE | GUC_DISALLOW_IN_FILE + }, + &server_in_hot_standby, + false, + NULL, NULL, NULL + }, + { {"bonjour", PGC_POSTMASTER, CONN_AUTH_SETTINGS, gettext_noop("Enables advertising the server via Bonjour."), NULL diff --git a/src/include/commands/async.h b/src/include/commands/async.h index b7842d1a0f..d21f8d59ae 100644 --- a/src/include/commands/async.h +++ b/src/include/commands/async.h @@ -24,6 +24,7 @@ extern bool Trace_notify; extern volatile sig_atomic_t notifyInterruptPending; +extern volatile sig_atomic_t hotStandbyExitInterruptPending; extern Size AsyncShmemSize(void); extern void AsyncShmemInit(void); @@ -54,4 +55,10 @@ extern void HandleNotifyInterrupt(void); /* process interrupts */ extern void ProcessNotifyInterrupt(void); +/* signal handler for inbound notifies (PROCSIG_HOTSTANDBY_EXIT) */ +extern void HandleHotStandbyExitInterrupt(void); + +/* process recovery exit event */ +extern void ProcessHotStandbyExitInterrupt(void); + #endif /* ASYNC_H */ diff --git a/src/include/storage/procarray.h b/src/include/storage/procarray.h index 9d5a13eb3b..a35759e141 100644 --- a/src/include/storage/procarray.h +++ b/src/include/storage/procarray.h @@ -79,6 +79,8 @@ extern int CountUserBackends(Oid roleid); extern bool CountOtherDBBackends(Oid databaseId, int *nbackends, int *nprepared); +extern void SendSignalToAllBackends(ProcSignalReason reason); + extern void XidCacheRemoveRunningXids(TransactionId xid, int nxids, const TransactionId *xids, TransactionId latestXid); diff --git a/src/include/storage/procsignal.h b/src/include/storage/procsignal.h index d068dde5d7..557318a4c1 100644 --- a/src/include/storage/procsignal.h +++ b/src/include/storage/procsignal.h @@ -41,6 +41,8 @@ typedef enum PROCSIG_RECOVERY_CONFLICT_BUFFERPIN, PROCSIG_RECOVERY_CONFLICT_STARTUP_DEADLOCK, + PROCSIG_HOTSTANDBY_EXIT, /* postmaster has exited hot standby */ + NUM_PROCSIGNALS /* Must be last! */ } ProcSignalReason; diff --git a/src/include/storage/standby.h b/src/include/storage/standby.h index 3ecc446083..dabd8a3839 100644 --- a/src/include/storage/standby.h +++ b/src/include/storage/standby.h @@ -26,6 +26,7 @@ extern int max_standby_streaming_delay; extern void InitRecoveryTransactionEnvironment(void); extern void ShutdownRecoveryTransactionEnvironment(void); +extern void SendHotStandbyExitSignal(void); extern void ResolveRecoveryConflictWithSnapshot(TransactionId latestRemovedXid, RelFileNode node); -- 2.11.1