Skip to content

Keep a terminated reserved connection out of the pool (fixes #1199) - #1240

Open
Mariomarquezt wants to merge 1 commit into
porsager:masterfrom
Mariomarquezt:fix-1199-dead-reserved
Open

Mariomarquezt wants to merge 1 commit into
porsager:masterfrom
Mariomarquezt:fix-1199-dead-reserved

Conversation

@Mariomarquezt

Copy link
Copy Markdown

Fixes #1199.

The defect

When the server terminates the backend of a reserved connection, onclose() moves the connection to the closed queue and sets c.reserved = null. It can also reconnect it at once for a waiting query. The ReservedSql handle does not know any of this:

The fix

onclose() calls a closed hook on the reservation before it clears c.reserved. The hook records the close error and rejects the handle's own overflow queue. After that, the handle is dead for good:

  • queries on it reject with the close error (CONNECTION_CLOSED);
  • release() does nothing, because the connection already belongs to the pool's closed queue, which reconnects it as usual.

Healthy reservations do not change.

Tests

Two new tests in tests/index.js against a real server:

  • Reserved connection rejects queries after its backend is terminated
  • Releasing a terminated reserved connection does not return it to the pool

On a CI-like setup (PostgreSQL 17 with the workflow's pg_hba.conf, ssl=on, wal_level=logical, max_prepared_transactions=100, and a second server on 5433; Node 20):

  • without the fix, node tests/index.js dies with the uncaught TypeError above;
  • with the fix, all 266 tests pass in test:esm and in test:cjs (264 existing + 2 new).

Deno was not run locally.

Related open PRs

These fix nearby parts of the same close path and do not overlap with this change: #1209 (guard nextWrite() itself), #1215 (reject transaction work after its connection closes), #1229 (a queued reserve() stranded by a termination). This PR only covers the reserved handle and release().

We have run this change as a local patch against 3.4.9 in production since 2026-09-30.

🤖 Generated with Claude Code

When the server terminates the backend of a reserved connection, onclose
moves the connection to the closed queue, and may reconnect it at once
for a waiting query. The ReservedSql handle still pointed at it:

- later queries on the handle called execute() on a connection with a
  null socket, so nextWrite() threw an uncaught TypeError and the query
  never settled;
- release() called onopen() and put the dead connection in the open
  queue, so the next pool query hung (porsager#1199).

onclose now marks the handle closed. Its queued queries are rejected,
later queries reject with the close error, and release() leaves the
connection with the pool, which reconnects it as usual.

Fixes porsager#1199
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.

bug: pool never recovers after releasing a reserved connection whose backend was terminated.

1 participant