When a client runs RESET <param>, PgDog clears its record of the server's tracked
parameters, assuming the session is back to its defaults and will be re-synced on the
next checkout (backend/server.rs, CommandComplete handling: "RESET" => self.client_params.clear()).
That assumption breaks inside a transaction. A ROLLBACK undoes the RESET,
reviving the parameter values that were in effect before it — but PgDog has just
stopped tracking them. The connection returns to the pool carrying session state the
pooler doesn't know about, so no RESET is generated for the next client, and that
client inherits the state.
With search_path this is user-visible as 42P01 relation "..." does not exist on
unqualified table names, for every client that gets the connection, until it is
recycled (server_lifetime) or the pooler is restarted.
pg_dump -t <table> emits exactly this sequence, so dumping a single table through
the pooler poisons a connection.
- pgdog v0.1.51 (latest release),
query_parser = "on" - transaction pooling (default), plain PostgreSQL 18 backend
default_pool_size = 1in the repro only to make the reuse deterministic; with a larger pool the poisoned connection simply bites intermittently
# pgdog.toml
[general]
query_parser = "on"
default_pool_size = 1
min_pool_size = 1
[[databases]]
name = "pgdog"
host = "postgres"
port = 5432# 1. baseline — fresh client through pgdog
psql -h <pgdog> -p 6432 -U pgdog -d pgdog -tA -c "SELECT current_setting('search_path')"
# "$user", public
# 2. poison: RESET inside a transaction, then ROLLBACK
psql -h <pgdog> -p 6432 -U pgdog -d pgdog <<'SQL'
SET search_path TO '';
BEGIN;
SET TRANSACTION ISOLATION LEVEL REPEATABLE READ, READ ONLY;
RESET search_path;
ROLLBACK;
SQL
# 3. fresh clients inherit the poisoned connection
for i in $(seq 1 5); do
psql -h <pgdog> -p 6432 -U pgdog -d pgdog -tA -c "SELECT current_setting('search_path')"
done
# "" ×5
# ... and unqualified names break
psql -h <pgdog> -p 6432 -U pgdog -d pgdog -tA -c 'SELECT count(*) FROM some_table'
# ERROR: relation "some_table" does not existThe same happens with a real dump, no hand-written SQL involved:
pg_dump -h <pgdog> -p 6432 -U pgdog -d pgdog -t some_table > /dev/null
psql -h <pgdog> -p 6432 -U pgdog -d pgdog -tA -c "SELECT current_setting('search_path')"
# ""Notes:
- Verify with
SELECT current_setting('search_path')rather thanSHOW search_path:SHOWcan be answered by the pooler itself,current_setting()goes to the server. The unqualified-SELECTfailure confirms it independently. - Removing any single statement from the sequence makes it go away — including
SET TRANSACTION ISOLATION LEVEL, which is what forces the client to be paired with a server connection before theRESETruns. - A full
pg_dump(no-t) does not leave the connection poisoned; the single-table form does.
A connection whose session state may have been revived by ROLLBACK should not be
handed to another client as-is. Marking it dirty so the existing checkin cleanup
(RESET ALL, pg_advisory_unlock_all(), DISCARD TEMP) runs is enough; plain RESET
outside a transaction needs no extra work.