pjfanning opened a new pull request, #488:
URL: https://github.com/apache/pekko-persistence-r2dbc/pull/488

   ### Motivation
   
   `R2dbcExecutor` can leak pooled connections when the first call it makes on 
a freshly acquired
   connection fails.
   
   * `withConnection` schedules the `close-calls-exceeding` watchdog *inside* 
the `flatMap` on
     `beginTransaction()`. If `beginTransaction()` fails, that block never 
runs, so neither the
     watchdog nor the `result.failed` / `result.flatMap` close handlers exist, 
and the connection is
     never returned to the pool. This is reachable whenever the pool hands out 
a connection the
     server has already dropped (idle timeout, failover, restart), because 
`BEGIN` is then the first
     statement to fail. Every occurrence permanently burns one slot of 
`max-size`, so a backend blip
     degrades into pool exhaustion. All transactional work goes through this 
path: journal writes and
     deletes, `R2dbcSession.withSession`, and the migration tool.
   * `withAutoCommitConnection` has the same shape for a failing 
`setAutoCommit(true)`. There the
     watchdog is already scheduled first, so the connection is held for 
`close-calls-exceeding`
     (20 seconds by default) rather than forever — and forever when 
`close-calls-exceeding = off`.
   * `select` and `withAutoCommitConnection` call `connection.close()` 
unguarded, while
     `commitAndClose` / `rollbackAndClose` wrap the same call in 
`ignoreConnectionClosedException`
     because `io.r2dbc.pool.PooledConnection` throws `IllegalStateException` 
synchronously from
     `assertNotClosed` when the connection was already closed. In `select` that 
throw happens inside
     `mappedRows.flatMap`, so a query whose rows were already read fails with 
`IllegalStateException`
     instead of returning them whenever `closeAfterTimeout` wins the race.
   
   ### Modification
   
   * `withConnection`: schedule the timeout task before `beginTransaction()`, 
and close the
     connection if `beginTransaction()` fails (no transaction was started, so 
there is nothing to
     roll back).
   * `withAutoCommitConnection`: close the connection if `setAutoCommit(true)` 
fails.
   * Add a private `closeConnection` helper that applies the existing 
`ignoreConnectionClosedException`
     catcher, and use it for every close in `select`, 
`withAutoCommitConnection`, `commitAndClose` and
     `rollbackAndClose`.
   
   No API or binary shape change — only method bodies plus one private method.
   
   ### Result
   
   A connection is released back to the pool whichever of `beginTransaction`, 
`setAutoCommit` or the
   caller's function fails, and a `select` that raced the close watchdog 
returns its rows instead of
   failing with `IllegalStateException`.
   
   ### Tests
   
   - `sbt "core/testOnly 
org.apache.pekko.persistence.r2dbc.internal.R2dbcExecutorConnectionReleaseSpec"`
 - 3 succeeded
   - Same spec against the unpatched `R2dbcExecutor` - 3 failed, confirming the 
tests are directional:
     `closeCount` stayed at `0` for the two leaks, and the select failed with
     `IllegalStateException: Connection is closed` instead of returning its 
rows.
   - `sbt "core/mimaReportBinaryIssues"` - success (scoped to the only module 
touched)
   - `sbt "++3.3.8 core/Test/compile"` - success
   - `scalafmt --non-interactive` on both changed files - no further changes
   - The DB-backed `R2dbcExecutorSpec` and the rest of the suite were not run; 
no PostgreSQL or
     YugabyteDB instance was available in this environment. The new spec uses a 
fake
     `ConnectionFactory` and needs no database.
   
   ### References
   
   None - connection leak found while auditing resource handling in the main 
sources.
   


-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to