Skip to content

Restore eth_getLogs range reduction after alloy migration - #6729

Open
datanexus-vincent wants to merge 3 commits into
graphprotocol:masterfrom
datanexus-vincent:fix/eth-getlogs-range-reduction
Open

datanexus-vincent wants to merge 3 commits into
graphprotocol:masterfrom
datanexus-vincent:fix/eth-getlogs-range-reduction

Conversation

@datanexus-vincent

@datanexus-vincent datanexus-vincent commented Oct 5, 2026 •

Copy link
Copy Markdown

Problem

log_stream shrinks the eth_getLogs block range when a provider rejects a request as too heavy. It detected that by substring-matching the rendered error against a list that included ServerError(-32005) and ServerError(-32000). Those are rust-web3's Debug rendering of the error code.

Since the alloy migration in v0.42.0 the error is an alloy RpcError, which renders as error code -32005: ... or ErrorResp(ErrorPayload { code: -32005, .. }). The code fingerprints never match. A provider with a range or result cap below GRAPH_ETHEREUM_MAX_BLOCK_RANGE_SIZE now gets ten identical retries, Unexpected RPC error, and a block stream restart, and the subgraph never advances. Besu with --rpc-max-logs-range is the simplest reproduction.

Change

The substring list is replaced by a structural check of the alloy error. A known cap message means the request is over the provider's cap: it is not retried, and log_stream shrinks the range at once. Everything else is retried and then surfaced, exactly as before, and the polling block stream lowers its own range in response, so a cap under an unlisted message still converges the way it does on master.

Caps are matched by message, not code. geth and reth report a cap under -32602 and Alchemy under -32600, which they also use for genuinely malformed requests, so the code alone cannot be trusted. The old list's code-based entries (-32005, -32000) are gone for the same reason: providers also use them for rate limits and transient failures, and shrinking on those sends more requests at a provider that is already refusing them.

Cap messages are also read from HTTP 400, 403, 413 and 503 bodies, and from 200 bodies alloy failed to deserialize. graph-node's HTTP transport turns any non-2xx response into an HttpError carrying the raw body, so a provider that pairs its cap with an error status was otherwise invisible. Ankr does this with a 413, GetBlock with a 403.

Covered, each with its verbatim message in the tests: geth, reth, Besu, Alchemy, Infura, QuickNode, Ankr, zkSync Era, Monad, Dwellir, GetBlock. Other providers can be added without a release through a new GRAPH_RPC_ETH_GET_LOGS_ERRORS variable, a ;-separated list of message fragments, mirroring GRAPH_RPC_ETH_CALL_ERRORS.

Behaviour

Error Before this PR After this PR
Known cap message, any code or status Retried 10x, surfaced, stream restarts; indexing loops Range shrinks immediately
Cap message only in an HTTP error body or an undeserializable body Same as above Range shrinks immediately
Cap under an unlisted message Same as above Unchanged: retried, surfaced, polling stream lowers its range and backs off
-32000, -32005 or bare 503 without a cap message Retried, surfaced Unchanged. Pre-alloy these shrank the range; that is not restored
Client-side timeout Retried, surfaced Unchanged

The reduction policy itself, dividing the step by ten down to a single block, is untouched.

Testing

Unit tests cover every message fragment in isolation, the verbatim provider messages under their real codes, codes that must not match on their own, cap messages inside HTTP and undeserializable bodies, and statuses whose bodies must not be read.

cargo test -p graph-chain-ethereum, cargo clippy -p graph-chain-ethereum --all-targets and cargo check -p graph-chain-ethereum --release pass. The provider messages come from client sources and provider documentation, not from live requests.

@datanexus-vincent
datanexus-vincent marked this pull request as ready for review October 8, 2026 16:14

@dimitrovmaksim dimitrovmaksim left a comment •

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

From what I see in the PR, the TooHeavy path looks good. I'm not sure about the MaybeTooHeavy one. Details are in the inline comments

-EDIT-

Also please follow the commit convention
https://github.com/graphprotocol/graph-node/blob/master/CONTRIBUTING.md#commit-messages-and-pull-requests
https://github.com/graphprotocol/graph-node/blob/master/CLAUDE.md#commit-convention

Comment thread chain/ethereum/src/ethereum_adapter.rs Outdated
Comment thread chain/ethereum/src/ethereum_adapter.rs Outdated
…tion

log_stream shrinks the block range when a provider rejects eth_getLogs
as too heavy. It recognised that by substring-matching the rendered
error, including "ServerError(-32005)" and "ServerError(-32000)", which
are rust-web3's Debug rendering. Since the alloy migration in v0.42.0
those strings never appear, so a provider enforcing a range or result
cap got ten identical retries, "Unexpected RPC error", and a block
stream restart loop instead of a smaller range.

Match the alloy error structurally instead. A known cap message shrinks
the range at once, without retrying. Caps are matched by message
because most providers report them under a generic code such as
-32602. Everything else is retried and surfaced as before, and the
polling block stream lowers its own range in response, so an unknown
cap still converges.
Some providers answer a cap with an error status and the reason in the
body, e.g. Ankr with a 413 and GetBlock with a 403. The HTTP transport
turns any non-2xx response into an HttpError carrying the raw body, and
a 200 whose error object alloy cannot parse becomes a DeserError, so in
both cases the message was invisible to the cap check. Read it out of
the body on 400, 403, 413 and 503. Only the message is consulted, never
the code, since a code in an error body is as likely a rate limit.
A `;`-separated list of extra eth_getLogs error messages to treat as a
block-range or result cap, mirroring GRAPH_RPC_ETH_CALL_ERRORS, so
operators can cover providers that are not hardcoded.
@datanexus-vincent
datanexus-vincent force-pushed the fix/eth-getlogs-range-reduction branch 2 times, most recently from 75504e2 to 6860d56 Compare October 14, 2026 18:24

This branch has not been deployed

No deployments
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