Repository navigation
LDEV-6503 discard timed-out connections instead of returning them to the pool - #2853
Merged
Merged
Conversation
GianTschingt
force-pushed
the
LDEV-6503-pool-timeout-7.0
branch
from
October 3, 2026 07:07
7e98977 to
1913131
Compare
…the pool - DatasourceManagerImpl.releaseConnection() invalidates the connection in the pool (existing invalidate() helper from LDEV-6129) instead of release() when the thread is interrupted or the request timed out; a request timeout can stop the driver in the middle of reading a response and the next borrower would get that unread result - the pool accounting from LDEV-5966 is kept, the connection is destroyed through the pool - test/tickets/LDEV6503.cfc
GianTschingt
force-pushed
the
LDEV-6503-pool-timeout-7.0
branch
from
October 3, 2026 09:08
1913131 to
3490b5a
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Jira: https://luceeserver.atlassian.net/browse/LDEV-6503
Forum report: https://dev.lucee.org/t/17621
Problem
Since LDEV-5966 (
3417970d4, 6.2.5.6 / 7.0.2.10)DatasourceManagerImpl.releaseConnection()always returns the connection to the pool withrelease(). Before that, a connection used by a timed-out request was closed. The idea was that validation on the next borrow catches a bad connection. But withvalidate=false(the default)validateObject()only checksisClosed()and the idle/live timeout.On 6.2 and 7.0 a request timeout interrupts the thread and then calls
Thread.stop(). TheThreadDeathhits as soon as the driver's socket read returns, so the response is left half read on the connection. The connection goes back to the pool and the next request that borrows it gets the result of the timed-out query instead of its own. On a server-level datasource that can be another application or user.Fix
In
releaseConnection(), if the current thread is interrupted, or the request (ThreadLocalPageContext.get(pc)) has timed out (getTimeoutStackTrace() != null), the connection is invalidated in the pool with the existing privateinvalidate(dc)helper (LDEV-6129) instead ofrelease(). The connection is destroyed through commons-pool2 (invalidateObject()), so the pool counters stay right and the LDEV-5966 fix is kept. One change in one place:Query,QueryLazy,Insert/Update,StoredProc,DBInfoand the transactionend()path all release through this method.No classloader changes, no new exception wrapping, no duplicated code. It reuses
invalidate(),ThreadLocalPageContext.get()andPageContextImpl.getTimeoutStackTrace().Trade-off
cfthread action="terminate"(thereSystemUtil.stop()gets no PageContext, so no timeout stack trace is set).Thread.stop()is gone) the query may have finished cleanly before the release. The connection is still discarded, so it's one reconnect per timed-out request, like before 6.2.5.6.Test
test/tickets/LDEV6503.cfc(labelspostgres,datasource, skipped when no Postgres datasource is configured; CI has one). It uses a datasource withvalidate=false, recordspg_backend_pid(), then runs acfthreadwithrequesttimeout=1andpg_sleep(5)that returnsA_MARKER,A_ID. While that query runs, the test callsgetCFMLFactory().checkTimeout()itself: the controller only checks for request timeouts every 5 seconds, so otherwise it could act after the query has finished (then nothing was aborted and the connection is rightly reused). After that it asserts:PID), not the timed-out request's result (fails without the fix on Java < 20);Repro summary
The forum repro (request A in a
cfthreadwithrequesttimeout=3, then request B on the same datasource), PostgreSQL 17, bundled pgjdbc,validate=false, 6 runs each:A_MARKER,A_ID)Thread.stop()), but the same connection is reused after every timeoutvalidate=trueon the datasource avoided the swap in all runs.Local verification
The changed class was compiled from this branch (
javac --release 11) into the 7.1.1.15-RC jar. The branch base (7.12059469e) has the same core sources as that RC, only version and build files changed since.LDEV6503.cfcwas run with script-runner against a local PostgreSQL 17:Merge-up
The branch is based on the current 7.1 tip (
2059469e). It also applies cleanly to 8.0 (git apply --checkon the current tip92487b9e).Opened by Gian Tschingt, Lucee community assistant for Michael Offner.