{{ message }}
Wait for a sibling HTTP/2 connection without blocking the caller thread#2227
Open
pavel-ptashyts wants to merge 2 commits into
Open
Wait for a sibling HTTP/2 connection without blocking the caller thread#2227pavel-ptashyts wants to merge 2 commits into
pavel-ptashyts wants to merge 2 commits into
Conversation
When the per-host connection cap is saturated and HTTP/2 is enabled, a request that fails to acquire a permit tries to multiplex onto a sibling connection another request is establishing to the same origin (stream reuse needs no permit). Off the event loop, waitForHttp2Connection did this by Thread.sleep(10)-polling the H2 registry until connectTimeout (5s default) elapsed — parking the caller thread (the synchronous part of execute()) for up to the full timeout and burning CPU. Under a bounded caller thread pool, a burst of over-cap requests to a new H2 origin could exhaust the pool. Replace the busy-poll with an event-driven, non-blocking deferral: register a one-shot waiter keyed by the request's partition key and return the pending future immediately. registerHttp2Connection wakes matching waiters, which resume the send via sendRequestWithOpenChannel. A connectTimeout deadline on the Netty timer fails the request with the original permit exception if no connection arrives; the client-close path fails pending waiters (their request-timeout backstop is not scheduled yet). A once-only CAS makes the registration, deadline, and close paths mutually exclusive, and the waiter rechecks the registry after registering to close the poll-vs-register lost-wakeup race. On the event loop (a redirect / 401 / 407 retry) the single immediate poll is kept and we give up if it misses — a wait there could self-deadlock, since the connection is being established on that same loop. The ROUND_ROBIN AsyncHttpClient#2214 limitation is unchanged (per-IP keying) but no longer occupies the caller thread. Adds ChannelManagerHttp2WaiterTest covering wake-on-registration, key isolation, waiter removal, and fail-on-close. Existing HTTP/2 regression tests (conformance, multiplexing, stream-orphan, streaming-body flow-control) pass unchanged.
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.

When the per-host connection cap is saturated and HTTP/2 is enabled, a request that fails to acquire a permit tries to multiplex onto a sibling connection another request is establishing to the same origin (stream reuse needs no permit). Off the event loop, waitForHttp2Connection did this by Thread.sleep(10)-polling the H2 registry until connectTimeout (5s default) elapsed — parking the caller thread (the synchronous part of execute()) for up to the full timeout and burning CPU. Under a bounded caller thread pool, a burst of over-cap requests to a new H2 origin could exhaust the pool.
Replace the busy-poll with an event-driven, non-blocking deferral: register a one-shot waiter keyed by the request's partition key and return the pending future immediately. registerHttp2Connection wakes matching waiters, which resume the send via sendRequestWithOpenChannel. A connectTimeout deadline on the Netty timer fails the request with the original permit exception if no connection arrives; the client-close path fails pending waiters (their request-timeout backstop is not scheduled yet). A once-only CAS makes the registration, deadline, and close paths mutually exclusive, and the waiter rechecks the registry after registering to close the poll-vs-register lost-wakeup race.
On the event loop (a redirect / 401 / 407 retry) the single immediate poll is kept and we give up if it misses — a wait there could self-deadlock, since the connection is being established on that same loop. The ROUND_ROBIN #2214 limitation is unchanged (per-IP keying) but no longer occupies the caller thread.
Adds ChannelManagerHttp2WaiterTest covering wake-on-registration, key isolation, waiter removal, and fail-on-close. Existing HTTP/2 regression tests (conformance, multiplexing, stream-orphan, streaming-body flow-control) pass unchanged.