Skip to content

fix(pg-pool): honor connectionTimeoutMillis and don't leak timed-out connections#3711

Open
malbolged wants to merge 2 commits into
brianc:masterfrom
malbolged:fix/pg-pool-connection-timeout-leak-3543
Open

fix(pg-pool): honor connectionTimeoutMillis and don't leak timed-out connections#3711
malbolged wants to merge 2 commits into
brianc:masterfrom
malbolged:fix/pg-pool-connection-timeout-leak-3543

Conversation

@malbolged

@malbolged malbolged commented Jul 17, 2026

Copy link
Copy Markdown

Fixes #3543.

newClient() sets a connectionTimeoutMillis timer, but the client.connect() success path never checks whether it fired. A connection that finishes establishing after the deadline is handed to the caller anyway — silently exceeding the timeout — and kept in the pool.

The timeout now rejects the checkout at the deadline, drops the client, and ends the connection cleanly rather than destroying the socket. A connection that completes after the timeout is closed instead of handed back. A client that reports isConnected() with no connection object (the native client) is still exempt, as before.

…connections

When connectionTimeoutMillis fired while a new connection was still being
established, the connect callback's success path never checked whether the
timeout had already fired. If the connection finished establishing just after
the deadline, the pool would resolve the checkout successfully - silently
violating connectionTimeoutMillis - and retain a connection it had already
given up on. The timeout callback also tore down the local socket with
stream.destroy() instead of terminating the connection cleanly.

This changes newClient() to:

- reject the checkout at the deadline and remove the client from _clients
  immediately, so a connect that hangs (or never calls back) can neither delay
  the timeout nor leak into _clients
- terminate the half-open connection cleanly (connection.end() sends a proper
  Terminate) instead of destroying the local socket
- silence the dying client's 'error' events before ending it, so they don't
  surface as an unhandled 'error' (which can crash the process)
- discard, rather than hand back, a connection that finishes establishing after
  the timeout already rejected the checkout

Adds a deterministic regression test (a 200ms connect against a 100ms timeout)
that fails on the previous behavior and passes with this change.

Fixes brianc#3543
@brianc

brianc commented Jul 17, 2026

Copy link
Copy Markdown
Owner

Write the PR description yourself and dont submit a huge wall of text slop from an agent. Getting a 55 line change PR is fine but not if it is accompanied by a thousand line, zero effort PR description.

The previous commit hoisted `timeoutHit = true` and the checkout rejection above
the `if (client.connection) / else if (!client.isConnected())` check, which
dropped an intentional behavior: a client with no connection object that already
reports `isConnected()` (the native client) was never timed out, because it has
connected and simply hasn't invoked its connect callback yet.

Restore that exemption by returning early from the timeout callback in that case,
which fixes `should connect if timeout is passed, but native client in connected
state`.
@malbolged

Copy link
Copy Markdown
Author

Write the PR description yourself and dont submit a huge wall of text slop from an agent. Getting a 55 line change PR is fine but not if it is accompanied by a thousand line, zero effort PR description.

I'm sorry, I asked claude to generate a PR description because I didn't want to manually write it and it generated a wall of slop that made no sense, I apologize for that.

PS: First and last time I ask it to generate PR description, I also asked it to generate the unit test.

@brianc

brianc commented Jul 17, 2026

Copy link
Copy Markdown
Owner

Its okay! I was a bit spicy last night after a long work week. As a maintainer the low effort slop PRs start to beat me down.

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.

[pg-pool] Connection timeouts cause connection leaks

2 participants