Skip to content

test(server): de-flake the indexer-service retry test#325

Merged
puneet2019 merged 2 commits into
mainfrom
fix/deflake-indexer-service-test
Jul 9, 2026
Merged

test(server): de-flake the indexer-service retry test#325
puneet2019 merged 2 commits into
mainfrom
fix/deflake-indexer-service-test

Conversation

@puneet2019

Copy link
Copy Markdown
Contributor

Fixes the test-unit-cover flake that hit #323 (TestIndexerServiceRetriesAfterFetchError, 10s deadline).

Root cause (my test, my bug)

The scenario scripted two fetch failures → recovery needed two wake-up tokens. But the service's newBlockSignal has capacity 1 and the subscription goroutine coalesces close headers into one token. Under the -race scheduler, the unlucky interleaving strands the second retry on the 60s NewBlockWaitTimeout → the test's 10s deadline fires.

Fix — deterministic two-phase design (test-only; service untouched)

Phase Discriminates How
1: quiescence busy-loop (pre-#311 copy) one failure, no signal sent — correct code must park; the busy-loop recovered without any wake-up and now fails an explicit check (replaces the fragile attempt-count bound)
2: single-token recovery latch (upstream cosmos/evm) one signal = one retry token → must index heights 1–2; the latched version stays parked and trips the deadline

Recovery now needs exactly one token → immune to coalescing under any interleaving.

Verification

  • Fixed code: 30 consecutive runs under -race — all pass.
  • Latch variant (blockErr = nil removed): fails with the stall signature ✓
  • Busy-loop variant (error gate removed): fails phase 1 ("indexed without a wake-up") ✓
  • go test ./server/... -race green; server/indexer_service.go byte-untouched.

🤖 Generated with Claude Code

@puneet2019 puneet2019 requested a review from a team July 9, 2026 07:16
TestIndexerServiceRetriesAfterFetchError scripted two fetch failures, so
recovery needed two wake-up tokens — but the service's new-block signal
channel has capacity 1 and the subscription goroutine coalesces close
headers into a single token. Under the -race scheduler that interleaving
stranded the second retry on the 60s NewBlockWaitTimeout and tripped the
test's 10s deadline (seen on #323's test-unit-cover).

Restructure to be deterministic under any interleaving:
- Phase 1 (busy-loop discriminator): one failure, NO signal sent — the
  correct loop must park; the pre-fix busy-loop recovered without a wake-up
  and now fails an explicit quiescence check instead of a fragile
  attempt-count bound.
- Phase 2 (latch discriminator + recovery): a single signal is a single
  retry token; the latched pre-fix version stays parked and trips the
  deadline.

Verified: 30 consecutive runs under -race; the latch and busy-loop variants
of the service each still fail the test; service code untouched.

Signed-off-by: puneetmahajan <59960662+puneet2019@users.noreply.github.com>
Signed-off-by: puneetmahajan <59960662+puneet2019@users.noreply.github.com>
@puneet2019 puneet2019 merged commit 7efecf2 into main Jul 9, 2026
27 checks passed
@puneet2019 puneet2019 deleted the fix/deflake-indexer-service-test branch July 9, 2026 15:50
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