#2134 fetcher.thread.timeout: cancel the okhttp call, bounded helper pool for other protocols - #2135
Draft
GGraziadei wants to merge 1 commit into
Draft
#2134 fetcher.thread.timeout: cancel the okhttp call, bounded helper pool for other protocols#2135GGraziadei wants to merge 1 commit into
GGraziadei wants to merge 1 commit into
Conversation
…elper pool for other protocols The bolt-level fetch timeout ran the protocol call on a single-thread executor owned by each FetcherThread and abandoned it with future.cancel(true) on expiry. okhttp does not honour the interrupt while connecting or reading, so the helper stayed blocked on the socket and the FetcherThread queued behind it at its next fetch: a host that dribbles bytes could take a thread out of service for as long as http.timeout. It also doubled the thread count of the bolt whenever the option was on, and the robots.txt lookup was not covered by the timeout at all. okhttp: fetcher.thread.timeout is applied as a per-call deadline (Call.timeout()), enforced by okio's shared watchdog: on expiry the call is cancelled, the socket closed and the fetching thread gets an InterruptedIOException at once, classified as "Socket timeout fetching". The deadline is clamped to topology.message.timeout.secs so that it can never loosen the client-level callTimeout. With http.content.partial.as.trimmed the content received before the deadline is kept and flagged as trimmed for "time", as it already was for the call timeout. Protocol gains a default supportsFetchTimeout() (false); okhttp returns true when configured, DelegatorProtocol only when every delegate does. Bolts: the timeout machinery moves to a new package-private FetchTimeoutHelpers owned by FetcherBolt and SimpleFetcherBolt. Its call() runs both the robots.txt lookup and the fetch on the calling thread when the protocol enforces the timeout itself, otherwise on a helper thread from one bounded pool per bolt (fetcher.thread.timeout.helpers, default 2 x fetcher.threads.number, 2 for SimpleFetcherBolt, threads created on demand and released after a minute idle). A deadline throws a typed TimeoutException; a full pool rejects at once with SaturatedException, reported as FETCH_ERROR "No fetch helper available". With the default protocol no helper thread is ever created. New fetchhelpers gauge and fetch.timeout / fetch.helper.rejected counters. The fetcher.thread.timeout key is defined in Constants; the public alias FetcherBolt.FETCH_TIMEOUT_PARAM_KEY (apache#1861) is removed. Documentation for the parameter, which was missing, is added to configuration.adoc. Tests: HttpProtocolFetchTimeoutTest (cancellation at the deadline, clamp to the message timeout, partial content), FetchTimeoutHelpersTest (every branch of call()), fetcher bolt tests with a protocol that hangs and ignores interruption (stuck fetch not blocking the following ones, bounded pool with rejection, hanging robots.txt reported at the timeout), slow robots.txt bounded with okhttp, no helper threads with okhttp, delegator capability. Fixes apache#2134.
GGraziadei
marked this pull request as draft
September 6, 2026 22:26
Member
Author
|
Moving on DRAFT |
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.
Fixes #2134.
fetcher.thread.timeoutran the protocol call on a single-thread executor owned by eachFetcherThreadand abandoned it withfuture.cancel(true)when the deadline passed. okhttp does not honour the interrupt while connecting or reading, so the helper stayed blocked on the socket and theFetcherThreadqueued behind it at its next fetch: one host dribbling bytes could take a thread out of service for as long ashttp.timeout. The option also doubled the bolt's thread count, and the robots.txt lookup was not covered by the timeout at all.Change
okhttp protocol
fetcher.thread.timeoutis applied as a per-call deadline withCall.timeout(), enforced by okio's shared watchdog thread: on expiry the call is cancelled, the socket closed and the fetching thread gets anInterruptedIOExceptionimmediately, classified as "Socket timeout fetching" as before.topology.message.timeout.secs, so that it can never loosen the client-levelcallTimeoutderived from it (a warning is logged).http.content.partial.as.trimmedthe content received before the deadline is kept and flagged as trimmed for "time", which is what that option already did for the call timeout. Documented.Protocolgainsdefault boolean supportsFetchTimeout()(false), backward compatible for external and user protocols. okhttp returns true when the deadline is configured;DelegatorProtocolonly when every delegate does, since the bolt cannot know in advance which delegate a URL is routed to.Fetcher bolts
FetchTimeoutHelpers, owned byFetcherBoltandSimpleFetcherBolt. Itscall()runs both the robots.txt lookup and the fetch: on the calling thread when the protocol enforces the timeout itself, otherwise on a helper thread from one bounded pool per bolt (fetcher.thread.timeout.helpers, default 2 ×fetcher.threads.number, 2 forSimpleFetcherBolt; threads created on demand, released after a minute idle).TimeoutException; a full pool rejects at once withSaturatedException, reported asFETCH_ERRORwithfetch.exception"No fetch helper available". Helpers are shared by all hosts, so a host that never answers can make fetches of other hosts fail this way until its helpers time out; stated in the docs.fetchhelpersgauge andfetch.timeout/fetch.helper.rejectedcounters.fetcher.thread.timeoutis now defined inConstants; the public aliasFetcherBolt.FETCH_TIMEOUT_PARAM_KEY(Add bolt-level timeout for fetcher threads #1861) is removed. The configuration key is unchanged.configuration.adocdocumentsfetcher.thread.timeout, which was missing, and the new helpers key.Behaviour
fetcher.thread.timeoutsetFetcherThreadException classification now also maps
InterruptedIOException(okhttp's own timeouts) to "Socket timeout fetching" regardless of the option; previously such failures carried the exception class name infetch.exception.Tests
HttpProtocolFetchTimeoutTest: a 5s response is cancelled at the 1s deadline; a 60s deadline is clamped to a 1s message timeout; partial content kept or failed depending onhttp.content.partial.as.trimmed.FetchTimeoutHelpersTest: every branch ofcall()(off, self-timing protocol, helper thread, exception propagation, typed timeout with interrupt, saturation, bound from config, shutdown).FETCH_ERROR(before: all three timed out); bounded pool with one rejection; hanging robots.txt reported at the timeout (before: the thread blocked for good).FetcherBoltTest: no helper threads with okhttp; slow robots.txt bounded by the deadline with okhttp.testThreadTimeoutpasses unchanged through the okhttp path.For all changes
#XXXXwhereXXXXis the issue number you are trying to resolve?mvn git-code-format:format-code -Dgcf.globPattern="**/*" -Dskip.format.code=false?For code changes
mvn clean verify?