Skip to content

Draft: Fix PostgreSQL server-side connection leak on failed establishment (too many clients)#1219

Open
skunkworker wants to merge 1 commit into
jruby:72-stablefrom
skunkworker:pr/pg-connection-leak-fix
Open

Draft: Fix PostgreSQL server-side connection leak on failed establishment (too many clients)#1219
skunkworker wants to merge 1 commit into
jruby:72-stablefrom
skunkworker:pr/pg-connection-leak-fix

Conversation

@skunkworker

Copy link
Copy Markdown
Contributor

Summary

Fixes a server-side connection leak when establishing a PostgreSQL connection fails after the physical backend is already open. Split out of #1216 as an independent, high-priority fix.

AR-JDBC connects eagerly inside the Java connection object's constructor, and @raw_connection is only assigned after new_client returns. PostgreSQL's newConnection() opens the physical backend (super.newConnection()) and then runs post-connect setup (unwrap + addDataType registrations). If any of that setup throws:

  • the physical backend is already open, held only in a local variable;
  • the constructor throws, so new_client never returns → @raw_connection stays nil;
  • AR's safety net attempt_configure_connectiondisconnect! runs @raw_connection&.disconnect!, which is a nil no-op → the backend is never closed.

The leaked backend persists until non-deterministic JVM GC closes the socket. Under a test/connect loop these accumulate until PostgreSQL max_connections is hit, producing FATAL: sorry, too many clients already, which then cascades into ActiveRecord::ConnectionTimeoutError as pooled connections are exhausted.

Fix

Wrap the post-connect setup in newConnection() in try { … } catch (SQLException | RuntimeException ex) { connection.close(); throw ex; }, closing the freshly-opened backend deterministically before propagating. This mirrors the native adapter discarding a connection that could not be fully established.

  • super.newConnection() stays outside the try — if the physical connect itself fails, there is nothing to close.
  • unwrap(PGConnection.class) is now inside the try — the previous code leaked there too.
  • A close() failure is attached via addSuppressed so the original exception is preserved.

Why this matters now

This is the root cause of the too many clients already cascade seen in the Rails Tests (Postgres) CI jobs (Rails tests intentionally open bad/aborted connections, e.g. test_reconnect_after_bad_connection_on_check_version). The leak is pre-existing — it was simply masked while CI ran on jruby-head (which crashed early). Landing this first makes the other split-out PRs' Rails-test results readable instead of drowning in the cascade.

Independent of #1218 (no shared files). Targets 72-stable.

🤖 Generated with Claude Code

If post-connect setup (PGConnection unwrap / addDataType registration) throws,
close the freshly-opened physical connection before propagating, otherwise the
server-side backend leaks - the caller only ever sees the exception and never
gets a handle to close. Mirrors the native adapter discarding a connection
that could not be fully established.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@skunkworker skunkworker changed the title Fix PostgreSQL server-side connection leak on failed establishment (too many clients) Draft: Fix PostgreSQL server-side connection leak on failed establishment (too many clients) Jun 25, 2026
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.

1 participant