Skip to content

feat(interop): make autorelay retries + backoff configurable, fix dead retry path - #421

Open
abhicris wants to merge 1 commit into
ethereum-optimism:mainfrom
abhicris:kcolb/autorelay-retries-config
Open

feat(interop): make autorelay retries + backoff configurable, fix dead retry path#421
abhicris wants to merge 1 commit into
ethereum-optimism:mainfrom
abhicris:kcolb/autorelay-retries-config

Conversation

@abhicris

@abhicris abhicris commented May 1, 2026

Copy link
Copy Markdown

Summary

relayMessageWithRetry was always called with maxRetries=1 at both call sites in interop/relayer.go (today's lines 121, 188), so the exponential backoff inside the retry loop was dead code and any single transient destination-chain RPC failure dropped a cross-chain message permanently. This PR makes the retry behavior real and configurable.

Changes

  • Two new CLI flags (also envvars):
    • --interop.autorelay.retries (default 3) — max relay attempts per message before giving up. Set to 1 to reproduce the prior no-retry behavior.
    • --interop.autorelay.retry.backoff.max (default 8s) — caps the 1<<attempt exponential backoff so high retry counts don't trigger unbounded sleeps.
  • NewL2ToL2MessageRelayer(logger, maxRetries, retryBackoffMax) signature now accepts the new parameters; orchestrator passes them through from CLIConfig.
  • Both call sites (relayMessageWithRetry(..., 1)) now pass r.maxRetries.
  • relayMessageWithRetry caps each backoff at retryBackoffMax (when > 0).

The defaults (3 attempts, 8s cap) are conservative — users get improved liveness against transient RPC blips with no surprise; downstream tests that depend on the no-retry behavior can opt out.

Why

Refs #291 ("Supersim autorelay flakes"). The Promise#dispatchCallbacks and waiting-pool retry paths both relied on relayMessageWithRetry to absorb transient errors, but the hardcoded 1 meant the retry loop never iterated. Local repro: any transient eth_sendTransaction failure (port contention, mempool full, restarted Anvil) would drop the cross-chain message and leak the waiting-pool entry.

Compatibility

  • NewL2ToL2MessageRelayer is internal — only orchestrator.NewOrchestrator calls it, updated in this PR. No exported API removed.
  • Default behavior is now "retry up to 3 times with capped backoff" instead of "no retries". Anyone explicitly relying on no-retry semantics can pass --interop.autorelay.retries=1.
  • maxRetries <= 0 is normalized to 1 to keep the loop body running at least once.

Test plan

  • go build ./...
  • go vet ./...
  • go test -run TestL2ToL2Message -count 1 ./interop/... (passes; interop package OK in 0.985s)
  • CI

This PR intentionally stays mechanical (config + dead-code revival) and does not also try to fix the InvalidTimestamp race in #291 — that's a separate concern that should ride a dedicated PR so the two changes can be evaluated independently.

…d retry path

relayMessageWithRetry was always called with maxRetries=1 at both call sites
in interop/relayer.go (lines 121, 188), so the exponential backoff in the
retry loop was dead code and any single transient destination-chain RPC
failure dropped a cross-chain message permanently.

This patch:
- Adds --interop.autorelay.retries (default 3) and --interop.autorelay.retry.backoff.max
  (default 8s) flags, plumbed through CLIConfig.
- Threads them into NewL2ToL2MessageRelayer(logger, maxRetries, backoffMax)
  so the indexer-driven and waiting-pool relay paths both use r.maxRetries.
- Caps the time.Sleep(1<<attempt) backoff at retryBackoffMax to prevent
  unbounded sleep on long retry counts.
- Defaults are conservative (3 attempts, 8s cap) so existing users see
  improved liveness on transient RPC blips without behavioral surprise.
  Setting --interop.autorelay.retries=1 reproduces the prior no-retry behavior.

Refs ethereum-optimism#291 (autorelay flake).
@abhicris
abhicris requested a review from a team as a code owner May 1, 2026 03:12
@abhicris
abhicris requested a review from tremarkley May 1, 2026 03:12
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.

1 participant