fix(firehose): add configurable idle timeout to the block stream receive loop - #6710
Open
SnowingFox wants to merge 1 commit into
Open
fix(firehose): add configurable idle timeout to the block stream receive loop#6710SnowingFox wants to merge 1 commit into
SnowingFox wants to merge 1 commit into
Conversation
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.
Problem
A subgraph fed by a firehose block stream can stop indexing indefinitely and silently if the firehose upstream keeps the HTTP/2 stream open but stops sending frames (no message, no error, no end-of-stream). The subgraph stays
health: healthy, is not paused, sets nofatalError, emits zero logs, and itslatestBlockis frozen until a manual restart/reassignment opens a fresh stream.Root cause
graph/src/blockchain/firehose_block_stream.rswraps only the initialstream_blocksestablishment intokio::time::timeout(120s, ...), but the receive loop (for await response in stream) has no per-message/idle timeout.last_response_timeis only fed to a metric and never used to trip a timeout. If the upstream holds the stream open and sends nothing,for awaitnever yields, and the hang propagates up the whole consumer chain (BufferedBlockStream, the runner'sblock_stream.next().await) with no error and no log.HTTP/2 keepalive pings are intentionally disabled in
graph/src/firehose/endpoints.rs(the code comment explains cloud load balancers drop connections that frequently send pings), andtcp_keepalive(15s)only detects a fully dead peer, not a half-open / app-hung upstream — the common case when the upstream is mid-restart behind a proxy/LB.Fix
Wrap each wait for the next stream message in a configurable idle timeout. When the timeout elapses, the stream is dropped and re-established with backoff (the reconnect path already exists), instead of hanging forever.
next_with_idle_timeout(stream, idle)helper: bounds eachstream.next()withtokio::time::timeout; returnsErr(())when the idle deadline elapses before the next message.GRAPH_FIREHOSE_STREAM_IDLE_TIMEOUT_SECS: when set to a positive number of seconds, the receive loop drops the stream and reconnects if no message arrives within that window. Disabled by default (missing /0/ unparseable) to preserve current behavior.Test
Adds
graph/tests/firehose_idle_timeout_tests.rs:idle_timeout_from_env_parsing— missing /0/ unparseable env value disables the timeout; a positive value enables it.stream_idle_timeout_returns_item_within_deadline— an item that arrives within the deadline is delivered.stream_idle_timeout_breaks_stalled_stream— a stream that sends nothing within the idle timeout returnsErr(())instead of hanging (the issue's exact failure mode).stream_idle_timeout_returns_none_when_stream_ends— natural end-of-stream is still observed.stream_idle_timeout_disabled_waits_for_item— with no idle timeout the wait is unconditional (backward compatible).The stalled-stream test does not build against the pre-fix code (the helper functions do not exist) and passes on the fixed code.
Notes
This is the topology-independent primary fix recommended in the issue (option 1). Re-enabling HTTP/2 keepalive (option 2) is left as an operator opt-in and out of scope here.
Fixes #6689