Re: [PATCH] Extensible ReadyForQuery wire protocol message and C hook, for connection pools and WAIT FOR LSN

From: Manu <manuelreyesbravo(at)gmail(dot)com>
To: Hannu Krosing <hannuk(at)google(dot)com>
Cc: pgsql-hackers(at)lists(dot)postgresql(dot)org
Subject: Re: [PATCH] Extensible ReadyForQuery wire protocol message and C hook, for connection pools and WAIT FOR LSN
Date: 2026-10-17 20:39:00
Message-ID: 179226954033.1033182.6926602894967135011@gmail.com
Views: Whole Thread | Raw Message | Download mbox | Resend email
Thread:
Lists: pgsql-hackers

Hi Hannu,

> Measuring the indicators against SQL in the same session, four edge cases
> still report a state that does not match the catalog.

I went back to this with sanitizers, a session generator with a catalog
oracle, and a protocol fuzzer, all on v3 over master 26bd08fb5c. A few things
worth flagging; the four v3 cases are only used here as controls.

Indicator desyncs -- the class is larger than those four. Over 24,000 random
sessions (246,368 oracle checks) the generator found five more mechanisms.
Three are false negatives with a live temporary table (T=0), which a pool
would read as a clean session; two need only one connection:

- CREATE TEMP TABLE t; BEGIN; DROP TABLE t; ROLLBACK -> T=0, but t is back.
- CREATE TEMP TABLE t; BEGIN; DISCARD TEMP; ROLLBACK -> T=0, t is back.
- a SERIALIZABLE COMMIT that drops a temp table and then fails with 40001
-> T=0, the table survives (two sessions).

The ROLLBACK/40001 restore and DISCARD-in-a-block are master's behaviour; what
the patch adds is the indicator not reflecting it. Two more are about what T
counts: a temp type or function created through pg_temp_N (not the pg_temp
alias) leaves T=0, and DROP SCHEMA pg_temp_N CASCADE leaves T=1 with nothing
there. The doc says "temporary tables" while the code counts more kinds of
temp object, so that one depends on which definition you intend. P and l had
no mismatches in the whole run.

ready_for_query_hook, error path: a hook that raises ERROR puts the backend
into a loop that sends ErrorResponse without stopping (about a million in two
seconds here), stays at 100% CPU after the client disconnects, and does not
respond to pg_terminate_backend() or "pg_ctl stop -m fast" -- only SIGKILL
ends it. Might be worth saying in the hook's contract that it must not throw,
or wrapping the call so a thrown error can't recurse through ReadyForQuery.

Compatibility: plain is transparent for libpq 14..master. rich at connect
fails cleanly ("message contents do not agree with length in message type
Z"). But turning rich on mid-session, by SET or by reload, hangs every
unpatched libpq I tried (7 of 7, 14 through master) -- so enabling it on a
connection an old client is already using is not safe. Also, patched libpq
drops the connection once a rich 'Z' is longer than 30,000 bytes (master's
long-message guard doesn't list 'Z'), so a hook with a large payload can trip
that.

Memory: the server was clean under ASan+UBSan across make check,
installcheck-parallel with every session in rich plus a hook on each
ReadyForQuery, the TAP suites, and the 24,000 generator sessions. One
client-side report: PQtrace reads past libpq's input buffer on a malformed
rich 'Z' (fe-trace.c), but master's tracer has the same over-read for a
DataRow, so it's a new instance of an existing pattern rather than something
new to the design.

Happy to share the generator, the minimal repros and the hook test module.

Regards,
Manu

In response to

Browse pgsql-hackers by date

  From Date Subject
Next Message Tom Lane 2026-10-17 22:51:44 Re: query_int wrongly parses zero-padded input
Previous Message Raja Sai pranav 2026-10-17 20:31:11 Re: Possible race condition in pg_basebackup