
in_hot_standby is a light, not a switch. It reports whether the server you are connected to is currently a hot standby: on when your session is riding a replica that is replaying WAL and taking read-only queries (the state hot_standby enables), off on a primary. It is a boolean, it arrived in PostgreSQL 14, and its context is none of the usual four, because nobody gets to change it:
1 postgres=# SET in_hot_standby = off;
2 ERROR: parameter "in_hot_standby" cannot be changed
3 postgres=# ALTER SYSTEM SET in_hot_standby = off;
4 ERROR: parameter "in_hot_standby" cannot be changed
Putting it in postgresql.conf earns you the same sentence in the server log at reload. It lives in the Preset Options category with block_size and the other look-but-don’t-touch parameters, and it is the only member of that category whose value can change while the server is running. Once, in one direction.
So why does a status flag get to be a GUC at all, when pg_is_in_recovery() already exists? For a human at a psql prompt, SHOW in_hot_standby is merely a nicer spelling of that function. The machinery is aimed at software, and the machinery is GUC_REPORT: a short list of parameters (server_version, client_encoding, TimeZone, and friends) that the server pushes to the client as ParameterStatus protocol messages, once at connection startup and again whenever one changes. PostgreSQL 14 added in_hot_standby (and default_transaction_read_only) to that list so libpq could learn a server’s role for free, before running a single query.
That was the point of the exercise. The same release extended target_session_attrs beyond read-write to read-only, primary, standby, and prefer-standby. Hand libpq a multi-host connection string and it walks the list, checking each server’s reported values until one qualifies:
1 psql "host=db1,db2 target_session_attrs=standby"
No probe queries on a 14-or-later server; the answer came in with the startup packet exchange. Against an older server, libpq falls back to asking out loud: SELECT pg_is_in_recovery() for the primary/standby modes, SHOW transaction_read_only for the read-write/read-only ones. The two families are not synonyms, either. read-write will reject a primary running default_transaction_read_only = on, while primary will accept it, because primary cares about recovery state and nothing else.
The one transition this parameter ever makes is promotion: on to off, never back, because turning a primary into a standby requires a restart, and the implementation leans on exactly that; the comment in guc.c reads “we rely on the assumption that it can never transition from false to true.” The part I like is when the client hears about it. The check runs as a backend is about to go idle, just before it sends ReadyForQuery, so nothing is pushed to a connection that is sitting quietly. Promote a standby under a live session and you get this sequence:
1 connected to standby ParameterStatus: in_hot_standby = on
2 pg_ctl promote
3 before any new command ParameterStatus: in_hot_standby = on (stale)
4 SHOW in_hot_standby; off (live)
5 after that ReadyForQuery ParameterStatus: in_hot_standby = off
SHOW asks shared memory directly, so it is current the instant recovery ends; the reported value your driver caches updates only when the server next talks to you, which it does not do unprompted. Note the ordering: the session became writable before the client was told it was writable. Nobody is disconnected at promotion; existing sessions simply stop being read-only, and each one finds out attached to the response of the first command it runs afterward.
That delivery schedule is the operational fact to keep. A pooler or health check that trusts its cached in_hot_standby can be stale for as long as the connection stays quiet, and target_session_attrs is evaluated at connection time only; nobody re-routes an established session. If you are building read/write routing, consume the ParameterStatus message (libpq hands it to you via PQparameterStatus()), and remember how it travels: with your next query’s response, not as breaking news. The server is glad to tell you it has been promoted. It waits for you to speak first.

Comments
Nothing yet. Say the first thing.
Sign in to join the conversation.