Skip to content

Stabilize Issue #38 CI races and sync preflight - #47

Merged
vmarkushin merged 10 commits into
mainfrom
codex/issue-38-minimal
Sep 7, 2026
Merged

Stabilize Issue #38 CI races and sync preflight#47
vmarkushin merged 10 commits into
mainfrom
codex/issue-38-minimal

Conversation

@vmarkushin

Copy link
Copy Markdown

Closes #38.

This is PR 1 extracted from #45. It contains only the root-cause fixes needed for the three reported Issue #38 failures, plus one tightly scoped Docker test reliability fix discovered while reproducing them.

What this fixes

  • Makes the optimistic-sync test deterministic by keeping automatic sequencing off during exact-number setup, then enabling it for the consolidation phase.
  • Coalesces a manual BuildBlock command with an in-flight payload job instead of replacing that job.
  • Drains immediately ready test events, diagnoses impossible exact-number waits, and bounds multi-event waits.
  • Removes remote RPC I/O from remote-source construction. A node now starts while its remote is down, then discovers its resume point on the first successful poll.
  • Adds a dead-port-to-live-proxy regression test and strengthens restart/resume assertions.
  • Adds a cheap sync-workflow secret preflight and treats whitespace-only ALCHEMY_KEY as missing.
  • Keeps the currently un-runnable external sync workflow manual until Re-point test_should_consolidate_to_block_15k at DogeOS chikyu infrastructure #43 provides compatible Chikyu infrastructure.
  • Lets Alloy estimate L1 fees in Docker helpers and uses --no-fail-fast so one flaky integration test cannot erase evidence from the rest of its lane.

Scope

This PR intentionally excludes the review-tail changes from #45: cancellation/rewind and CAS logic, fatal divergence policy, finalized-marker replay, L2 sync latching, broader remote transport/config validation, credential redaction, genesis/startup forkchoice work, DB restore classification, FCU policy, and soak automation. Those changes have been separated into local follow-up branches for individual review. The original #45 remains open as the preserved reference.

Known pre-existing behavior is not widened here: a cancelled remote build can still leave an outcome wait unbounded, and an unavailable remote can still log once per poll interval. Both are deferred rather than mixed into this fix.

Verification

  • cargo +nightly-2026-01-05 fmt --all -- --check
  • cargo +nightly-2026-01-05 clippy --workspace --lib --examples --tests --benches --all-features -- -D warnings
  • Three consecutive runs of the focused optimistic-sync/coalescing tests
  • Three consecutive runs of all remote-source tests, including startup while unavailable and restart/resume
  • cargo +nightly-2026-01-05 check --workspace --all-features --all-targets --locked --quiet on every prepared split branch
  • Final clean-context Codex review at xhigh: CLEAN

No breaking configuration changes are included in this PR.

…#38, defect 3)

- Fail on missing/empty ALCHEMY_KEY in a 5-minute preflight job before the
  ~20 minute release build (GitHub passes a missing secret as an empty
  string, which also slipped past the test's env::var skip-guard).
- Gate the workflow to workflow_dispatch: since the Tsuki migration the 15k
  consolidation test uses DOGEOS_CHIKYU, whose genesis lacks
  scroll.l1Config.startL1Block, so NodeConfig::from_chainspec fails before
  any networking and no secret can make the workflow green. A follow-up
  issue re-points the test at chikyu infrastructure before re-enabling push.
…(issue #38)

test_should_consolidate_after_optimistic_sync armed the sequencer's 20ms
auto-build timer while driving 200 manual build_block() calls asserting
exact block numbers. A manual BuildBlock silently replaces an in-flight
auto job (payload building = 40ms > the inter-iteration gap), which
normally hides the timer; on a slow runner an auto job completed inside
the gap, claimed the expected number, its event was consumed by an
interleaved waiter, and the exact-number wait timed out after 30s —
the CI failure in issue #38 (defect 1).

The timer now stays off during the exact-numbered setup loop and is
enabled via enable_automatic_sequencing() for the sync/consolidation
phase, whose one-shot gossip waits rely on its continuous block stream
(removing it entirely made those waits flaky the other way — verified
locally). Assertions are unchanged.

Also in this file:
- treat an empty ALCHEMY_KEY as unset in the 15k consolidation test's
  skip-guard (GitHub passes a missing secret as an empty string;
  issue #38, defect 3), and
- bound wait_n_events with a 60s timeout so an unmet expectation fails
  with a diagnosis instead of hanging the binary for the whole job
  timeout (test_chain_orchestrator_l1_reorg was observed doing exactly
  that on a pre-existing, unrelated race).

Verified: rollup-node::sync suite 14/14 locally; the fixed test passed
3/3 standalone plus the full-suite run.
…act-number waits (issue #38)

The event waiter consumed at most one event per 10ms sleep (~100
events/s). Under load that let the broadcast event channel (capacity
5000, silently lossy on lag via reth's EventStream) back up and drop
events, turning a backlog into a lost-event hang. The waiter now drains
every immediately-available event before sleeping.

block_sequenced(target) additionally fails fast with a diagnosis when
it observes a BlockSequenced above the target: numbers are monotone per
node, so the awaited event can no longer arrive and a silent 30s
timeout would just hide the story.
… payload job (issue #38)

A manual BuildBlock command called start_payload_building directly and
silently replaced any in-flight (typically timer-triggered) payload
job, discarding its engine work and making block numbering
timing-dependent whenever the build timer and manual triggers race —
the mechanism behind the optimistic-sync test flake. The command now
coalesces with the in-flight job; callers wait on
BlockSequenced/BlockBuildingSkipped, which that job emits.

A black-box red test for the old behavior is not possible in isolation
(a replaced job emits nothing, so replace and coalesce look identical
without the timer); the added test pins the coalescing contract:
two rapid build_block() calls yield exactly one block and contiguous
numbering.

Note: an A/B experiment (guard present 1/3 vs absent 2/6 failures)
confirmed the pre-existing test_chain_orchestrator_l1_reorg flake is
unrelated to this change — it panics in reth's nippy-jar static-file
reader after the L1-reorg unwind (tracked separately).
… remote down (issue #38)

Adds a remote_source_url() override to the test fixture builder and a
test that points the remote-source node at a dead port, asserts the
node still launches, then brings the port up via a TCP proxy to the
sequencer RPC and asserts the node imports and builds on top.

Red until the next commit: RemoteBlockSourceAddOn::new() currently
probes the remote during launch_add_ons, and a connection-refused
error (which alloy's default retry policy does not retry) aborts the
entire node — the docker_test_remote_block_source_basic startup race
from issue #38 (defect 2).
…nch (issue #38)

RemoteBlockSourceAddOn::new() called get_block_number() plus the
common-ancestor walk inside launch_add_ons. alloy's default retry
policy retries only rate-limit/temporarily-unavailable responses —
connection-refused is not retried — so a remote that wasn't up yet
(the sequencer container racing the remote-source container in
docker-compose.remote-source.yml, which orders neither) aborted the
whole node and its own RPC on 8546 never came up.

The resume point (last_imported_block) is now Option<u64>, determined
on the first successful poll inside the run loop, where errors are
already logged and retried at poll cadence. Construction performs no
remote I/O, so node launch never depends on remote availability;
restart-time resume detection is unchanged (still exercised by
test_remote_block_source_resumes_from_correct_head).

Fixes docker_test_remote_block_source_basic and the same one-shot race
previously seen in docker_test_remote_block_source_recovery.
…o-fail-fast lanes

Discovered while collecting repeated-green evidence for issue #38: a
rerun of the docker lane failed in
docker_test_heterogeneous_client_sync_and_sequencer_handoff with anvil
rejecting L1 txs ('max fee per gas less than block base fee'). The L1
messenger/gateway helpers pinned gas_price at 0.1 gwei, so whenever the
continuous senders pushed the EIP-1559 base fee above that, every
subsequent L1 message failed. Fees are now estimated per-send.

Both integration lanes also run with --no-fail-fast so a single flaky
test cannot cancel the rest of the lane's evidence (the failing rerun
cancelled the remote-source pair before it ran).
@codspeed-hq

codspeed-hq Bot commented Sep 4, 2026

Copy link
Copy Markdown

Merging this PR will not alter performance

✅ 2 untouched benchmarks


Comparing codex/issue-38-minimal (195514c) with main (d3260af)

Open in CodSpeed

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.

Stabilize integration CI startup/event races and fix sync preflight

1 participant