Add require_wal_receiver connection parameter to libpq

Started by Jim Jonesabout 2 months ago1 messageshackers
Beta feature

Hackorum builds and tests every patch posted to the lists, not only commitfest submissions. This is Hackorum's own CI rather than the PostgreSQL project's, and it is still under testing - please report anything that looks wrong.

needs rebasesuccessCI history

You can run a PostgreSQL built from this patch straight from Docker, with no checkout and no build:

docker run --rm -p 5432:5432 ghcr.io/hackorum-dev/postgres-patch:t253691
psql -h localhost -U postgres

Built from patchset v1 (message #1), September 23, 2026 at 09:40 PM.

Every patchset is also pushed to a branch of our PostgreSQL fork, so you can check out the same tree CI built. Without a PostgreSQL checkout:

git clone --branch t253691_1 https://github.com/hackorum-dev/postgres.git

In a checkout you already have, add the fork once:

git remote add hackorum https://github.com/hackorum-dev/postgres.git

then, for this patchset and every later one:

git fetch hackorum t253691_1 && git checkout t253691_1

Patchset v1 (message #1) is on t253691_1

Jump to latest
#1Jim Jones
jim.jones@uni-muenster.de

Hi,

When a connection string lists several hosts,
target_session_attrs=standby (or prefer-standby) picks the first host
that reports pg_is_in_recovery(). That confirms the server is a standby,
but says nothing about whether it still has a live WAL receiver attached
to its upstream. A standby whose primary connection was lost still
reports itself as a standby and is pretty much indistinguishable from a
healthy one, so libpq happily routes read-only traffic to a server that
potentially stopped receiving WAL for quite some time.

I'd like to propose a parameter that makes libpq look at
pg_stat_wal_receiver on the candidate standby and move on to the next
host when no WAL receiver is running there:

psql "host=h1,h2,h3 port=5111,5222,5333 dbname=db \
target_session_attrs=standby require_wal_receiver=1"

It applies on top of target_session_attrs rather than replacing it, and
is skipped where it is meaningless: on a primary, when
target_session_attrs requires a primary/read-write session, on
replication connections, and on the prefer-standby fallback pass, so
prefer-standby still degrades to "any server" instead of failing outright.

The current PoC is quite simplistic. The CONNECTION_CHECK_STANDBY state
already runs "SELECT pg_catalog.pg_is_in_recovery()" for
standby/prefer-standby, so the check rides along on that query as a
second column -- an EXISTS over pg_stat_wal_receiver. The obvious
alternative -- comparing pg_last_wal_receive_lsn() with
pg_last_wal_replay_lsn() -- does not cover the same case. A standby that
lost its upstream received everything it will ever receive and replayed
all of it, so its lag is exactly zero and it passes any threshold.

Two things I'd like input on before going forward:

1) The EXISTS test is deliberately weak. pg_stat_wal_receiver might
simply report a connection attempt, so a WAL receiver stuck connecting
to an unreachable primary still produces a row and passes the current
test. Testing status = 'streaming' would close that, but every column
except pid is NULL for roles without pg_read_all_stats, so the strict
form only works for privileged users. Which trade-off is preferable? I'd
rather not make the semantics privilege-dependent.

2) Is a separate boolean the right approach, or should this be a new
target_session_attrs value? I went with a separate parameter because
otherwise standby, prefer-standby, read-only and any would each need a
paired variant.

It's important to note that this is a "liveness check", not a freshness
guarantee: a standby that passes may still be far behind in replay, and
a standby recovering only from archive is rejected even if current.

Is this the right direction? Any feedback is much appreciated!

Best, Jim

Attachments:

t253691_1
v1-0001-Add-require_wal_receiver-connection-parameter-to-.patchtext/x-patch; charset=UTF-8; name=v1-0001-Add-require_wal_receiver-connection-parameter-to-.patchDownload+624-4