Skip to content

fix(server): back off, retry, and de-race the EVM indexer fetch loop#311

Merged
puneet2019 merged 1 commit into
mainfrom
refactor/adopt-cosmos-evm-indexer-service
Jul 6, 2026
Merged

fix(server): back off, retry, and de-race the EVM indexer fetch loop#311
puneet2019 merged 1 commit into
mainfrom
refactor/adopt-cosmos-evm-indexer-service

Conversation

@puneet2019

@puneet2019 puneet2019 commented Jul 6, 2026

Copy link
Copy Markdown
Contributor

Repurposed after review. This PR originally adopted cosmos/evm's EVMIndexerService; @phenix3443 caught that upstream's version latches the fetch error and stalls forever (still true on their main). The service now stays in-tree, on upstream's structure, with every known defect fixed.

The three defects (all in the same ~40-line loop)

Defect Where it lives Symptom
Busy-loop on fetch error moca's pre-fix copy hammers a failing Block/BlockResults fetch with no backoff (CPU burn; can occur from drive latency between a block existing and its results being saved)
Latched stall (the review find) upstream cosmos/evm v0.6.0 and main blockErr gates the loop but is never cleared — after the first transient fetch error the service only sleeps; indexing stops until restart
Data race on the latest-height variable both written by the new-block subscription goroutine, read by the indexing loop, unsynchronized — go test -race fails on both prior versions (surfaced while writing the regression test)

Fix (server/indexer_service.go, +39/−13 vs main)

Upstream's structure + backoff, plus blockErr = nil after the wait (so the loop actually retries), plus latestBlock as an atomic.Int64.

Regression test (server/indexer_service_test.go)

Mock RPC client scripts a transient BlockResults failure (fails twice, then succeeds); new-block signals drive the loop with no real-time waits. Verified to fail on each historical defect:

  • latched-stall version → test times out (indexed so far: map[] attempts=1)
  • busy-loop version → exceeds the bounded fetch-attempt count
  • unsynchronized version → -race failure

and passes on the fixed code (go test ./server/... -race clean).

Upstream

The latch + race are live in cosmos/evm main. Happy to send the completing fix upstream (same pattern as cosmos/evm#1214); until then the in-tree copy is the correct choice — adopting upstream would trade a busy-loop for a stall.

🤖 Generated with Claude Code

@puneet2019 puneet2019 requested a review from a team July 6, 2026 06:46
@puneet2019 puneet2019 force-pushed the refactor/adopt-cosmos-evm-indexer-service branch from 168ce9f to ac48db7 Compare July 6, 2026 06:47
@puneet2019 puneet2019 force-pushed the refactor/adopt-cosmos-evm-indexer-service branch from ac48db7 to ef7dbf0 Compare July 6, 2026 06:57
@puneet2019 puneet2019 requested a review from phenix3443 July 6, 2026 07:04

@phenix3443 phenix3443 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I found one blocking issue.

Adopting cosmos/evm's EVMIndexerService in the pinned upstream version introduces a stall-once-failed behavior: after the first transient Block / BlockResults fetch error, the latched blockErr prevents the service from ever retrying indexing work, so the EVM tx indexer can stop progressing until the node restarts. I left the detail inline on server/start.go where the upstream service is wired in.

Comment thread server/start.go Outdated
// cosmos/evm's indexer service (identical ctor; upstream also waits
// before retrying when a Block/BlockResults fetch errors, instead of
// busy-looping the failing fetch).
indexerService := cosmosevmserver.NewEVMIndexerService(idxer, clientCtx.Client.(rpcclient.Client))

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Switching to cosmos/evm's NewEVMIndexerService pulls in a regression from the pinned upstream v0.6.0: in server/indexer_service.go, blockErr is set on a transient Block / BlockResults fetch failure and then used as a permanent gate (if latestBlock <= lastBlock || blockErr != nil), but it is never cleared before retry. After the first fetch error, the service only sleeps and never re-enters the indexing loop, so EVM tx indexing can stall until process restart. The previous in-tree copy busy-looped, which was ugly but at least self-recovered once the storage race cleared.

This PR set out to adopt cosmos/evm's EVMIndexerService for its
wait-before-retry on Block/BlockResults fetch errors; review (phenix) caught
that upstream's version (still on their main) latches the fetch error and
never clears it — after the first transient failure the loop only sleeps and
indexing stalls until restart. The old in-tree copy had the opposite defect:
it busy-looped the failing fetch with no backoff. Writing the regression test
also surfaced a third defect present in both: the latest-height variable is
written by the new-block subscription goroutine and read by the indexing loop
unsynchronized (go test -race fails on both prior versions).

Keep the service in-tree on upstream's structure with all three fixed:
back off on fetch errors (upstream's improvement), clear the error before
retrying (the missing line), and make latestBlock an atomic.

The regression test drives the loop with a scripted RPC client and fails on
each historical defect: it times out on the latched-stall version, exceeds
the bounded fetch-attempt count on the busy-loop version, and trips -race on
the unsynchronized version.

Signed-off-by: puneetmahajan <59960662+puneet2019@users.noreply.github.com>
@puneet2019 puneet2019 force-pushed the refactor/adopt-cosmos-evm-indexer-service branch from ef7dbf0 to 66c5e48 Compare July 6, 2026 08:51
@github-actions github-actions Bot added the tests label Jul 6, 2026
@puneet2019 puneet2019 changed the title refactor(server): use cosmos/evm's EVMIndexerService instead of the in-tree copy fix(server): back off, retry, and de-race the EVM indexer fetch loop Jul 6, 2026
@puneet2019 puneet2019 dismissed phenix3443’s stale review July 6, 2026 09:46

re-added with test and patch

@puneet2019 puneet2019 requested a review from phenix3443 July 6, 2026 09:46
@puneet2019 puneet2019 merged commit 5f0ee78 into main Jul 6, 2026
27 checks passed
@puneet2019 puneet2019 deleted the refactor/adopt-cosmos-evm-indexer-service branch July 6, 2026 09:59
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants