Skip to content

fix: resolve reserve() hang with fetch_types disabled - #1220

Open
Aquiko wants to merge 1 commit into
porsager:masterfrom
Aquiko:fix/fetch-types-false-reserve
Open

Aquiko wants to merge 1 commit into
porsager:masterfrom
Aquiko:fix/fetch-types-false-reserve

Conversation

@Aquiko

@Aquiko Aquiko commented Sep 15, 2026

Copy link
Copy Markdown

Fixes #1219.

reserve() on a cold pool never settles when fetch_types: false: the ReadyForQuery handler returns early instead of falling through to onopen(), so nothing resolves the reserve. With fetch_types: true the array-type fetch round trip falls through and resolves it, which is why it only reproduces with the option disabled.

This makes the non-fetching path fall through to onopen() when initial is a reserve, and adds a cold-pool regression test.

With `fetch_types: false`, a queued `reserve()` on a cold pool never
settled: the ReadyForQuery handler returned early instead of falling
through to `onopen()`, so nothing resolved the reserve. Fall through to
match the `fetch_types: true` path, and add a cold-pool regression test.
@okomarov

Copy link
Copy Markdown

Looks good, quick fix, test and comment guard against a random change of flow with the reintroduction of a return, but rewriting is not worth how quickly this can get through.

@Aquiko

Aquiko commented Sep 22, 2026

Copy link
Copy Markdown
Author

Verified against a real Postgres instance:

  • origin/master (unpatched): reserve() with fetch_types: false never settles — hits a 10s timeout.
  • This branch: settles immediately (select 1 as x → 1).

The regression test I added (reserve with fetch_types: false on a cold pool) exercises exactly this path, so it should confirm the same once CI runs.

spy4x added a commit to spy4x/ts-libs that referenced this pull request Sep 24, 2026
The old test's SET client_min_messages query warmed the pool the same
way the fix's own warm-up does, so removing the fix left the test
green. Move client_min_messages into the connection options and send
no query before runMigrations, so the hang reproduces again (~5s,
then red). Force-close the client in finally instead of a graceful
end + query, which used to hand the stuck reserve() a connection and
recreate the table it was supposed to drop.

porsager/postgres#1220 is an open, unmerged third-party PR against the
bug filed as #1219 — not a landed upstream fix. Corrected in the doc
comment, the test and README, and named the one race the workaround
does not close.
spy4x added a commit to spy4x/ts-libs that referenced this pull request Sep 24, 2026
* fix(server): unhang runMigrations on fetch_types: false

sql.reserve() never resolved when it had to open a fresh connection on
a client built with fetch_types: false — postgres@3.4.7's
ReadyForQuery only hands a new connection back to a pending reserve()
through the branch that also fetches array-type OIDs, which
fetch_types: false skips (upstream: porsager/postgres#1220). A plain
query on the pool before reserve() leaves a connection idle, so
reserve() takes the pool's synchronous already-idle path and never
hits the buggy one.

* fix(server): repair fetch_types test and PR reference

The old test's SET client_min_messages query warmed the pool the same
way the fix's own warm-up does, so removing the fix left the test
green. Move client_min_messages into the connection options and send
no query before runMigrations, so the hang reproduces again (~5s,
then red). Force-close the client in finally instead of a graceful
end + query, which used to hand the stuck reserve() a connection and
recreate the table it was supposed to drop.

porsager/postgres#1220 is an open, unmerged third-party PR against the
bug filed as #1219 — not a landed upstream fix. Corrected in the doc
comment, the test and README, and named the one race the workaround
does not close.

* docs(server): correct the reserve race wording

The stuck connection is freed by max_lifetime, and a query after reserve() never runs; both from the round-2 reviewer of #167.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

fetch_types: false, deadlocks reserve() and alternatively pulls ~10KB from server before any work and can burn GBs of egress per month

2 participants