Description
DbConnectionFactory.getConnection() returns a ThreadLocal-managed connection. Using it in try-with-resources causes close() to run on that connection when the block exits, which corrupts ThreadLocal state and can lead to connection leaks or pool exhaustion.
Problem: Callers (e.g. DatabaseHealthCheck, DatabaseHealthEventManager) use:
try (Connection conn = DbConnectionFactory.getConnection()) { ... }
When close() runs, it returns the connection to the pool but leaves a stale reference in the ThreadLocal, or worse, removes the wrong connection from the holder.
Proposed solution: Add a wrapper (e.g. TryWithResourcesSafeConnection) that only actually closes the connection when it was opened by the current call — matching the semantics of @CloseDBIfOpened. When ownsConnection is true, close() removes from ThreadLocal and closes. When false, close() is a no-op.
Acceptance Criteria
Additional Context
Description
DbConnectionFactory.getConnection()returns a ThreadLocal-managed connection. Using it in try-with-resources causesclose()to run on that connection when the block exits, which corrupts ThreadLocal state and can lead to connection leaks or pool exhaustion.Problem: Callers (e.g.
DatabaseHealthCheck,DatabaseHealthEventManager) use:When
close()runs, it returns the connection to the pool but leaves a stale reference in the ThreadLocal, or worse, removes the wrong connection from the holder.Proposed solution: Add a wrapper (e.g.
TryWithResourcesSafeConnection) that only actually closes the connection when it was opened by the current call — matching the semantics of@CloseDBIfOpened. WhenownsConnectionis true,close()removes from ThreadLocal and closes. When false,close()is a no-op.Acceptance Criteria
getConnection()returns a wrapper that is safe for try-with-resourcescloseSilently()/closeAndCommit()continue to work unchangedDbConnectionFactory.getConnection() != connection) still work (same wrapper instance returned for existing connection)closeConnection()handles the wrapper correctly (no ConcurrentModificationException)Additional Context
DotConnectionWrapperandDotJobConnectionWrapperexist; the new wrapper would extendDotConnectionWrapperand overrideclose()