Skip to content

HTTPCLIENT-2437: LaxConnPool fails expired lease requests queued behind a live one - #738

Merged
ok2c merged 1 commit into
apache:masterfrom
shinseongsu:lax-pool-expire-pending
Nov 4, 2026
Merged

ok2c merged 1 commit into
apache:masterfrom
shinseongsu:lax-pool-expire-pending

Conversation

@shinseongsu

@shinseongsu shinseongsu commented Nov 4, 2026 •

Copy link
Copy Markdown
Contributor

Follow-up to #735 for HTTPCLIENT-2437.

LaxConnPool already completes the future of an expired request, but only when that request is at the head of the queue. PerRoutePool#servicePendingRequests stops at the first request it cannot serve, so when the head request has not expired yet, expired requests behind it are not failed by closeExpired() or closeIdle().

This shows up when lease requests on the same route use different connectionRequestTimeout values. With a pool of 1, request B (1 minute) and then request C (100 ms) waiting behind a leased connection, C is still pending after closeExpired(). StrictConnPool fails C in the same case, as processPendingRequests() walks the whole queue.

enumAvailable() now also calls validatePendingRequests(), which checks every pending request.

Is this approach OK with you? I kept the change in enumAvailable() so that only closeExpired() / closeIdle() change and the lease/release path stays as it is. The other option I considered is to let servicePendingRequests() keep scanning past a request it cannot serve and fail the expired ones there, which would also cover release, but it touches the hot path. Happy to change it if you prefer that.

Tests: the new TestPoolPendingRequestExpiry case fails on master for LAX only and passes with the change. httpcore5 verify: 2011 tests, 0 failures.

5.4.x has the same code. I have the same change on a 5.4.x branch, with the test using the real clock as in the #735 cherry-pick; it fails before the change and passes after (httpcore5 verify: 1264 tests). Should I open a separate PR for 5.4.x, or would you prefer to cherry-pick it?

…nd a live one

PerRoutePool#servicePendingRequests stops at the first request it
cannot serve. When that request has not expired yet, expired requests
queued behind it are not failed by closeExpired() or closeIdle() and
stay pending until they reach the head of the queue.

enumAvailable() now also runs validatePendingRequests(), which checks
every pending request, as StrictConnPool already does.
@ok2c
ok2c merged commit 57428d2 into apache:master Nov 4, 2026
12 checks passed
@ok2c

ok2c commented Nov 4, 2026

Copy link
Copy Markdown
Member

Cherry-picked to 5.4.x

@ok2c

ok2c commented Nov 4, 2026

Copy link
Copy Markdown
Member

Thank you @shinseongsu

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.

3 participants