Commit f282750
authored
test: deflake epochs_optimistic_proving reorg-during-proving gate (#24308)
## Problem
`e2e_epochs/epochs_optimistic_proving.parallel.test.ts` › "handles a
reorg arriving while the top of the epoch is proving" flakes (e.g. CI
runs `a8d5f346e269c668`, `5039f193fa054832`), timing out at:
```
TimeoutError: Timeout awaiting prover-node sees the prune and recreates session with fewer provers
```
## Root cause
The test installs a `beforeTopTreeProve` gate meant to pause top-tree
proving so the L1 reorg lands at a deterministic point (session parked
mid-proof, all sub-trees done). The gate never actually engaged:
- The hook fires from `TopTreeJob.run()` at the `beforeProve` boundary
(`top-tree-job.ts:198`), which runs *after* `EpochSession` has already
flipped the session state from `awaiting-checkpoints` to `awaiting-root`
(`epoch-session.ts:426-431`).
- The gate predicate looked for a job in `awaiting-checkpoints`, so at
hook time it matched nothing and returned early — never blocking.
(`Top-tree proving gated` appears 0 times in both the failed and passed
CI logs.)
With the gate disabled, the reorg raced real, pipelined sub-tree proving
(`top-tree-orchestrator.ts:85-87`), and the outcome depended on epoch
size:
- **Small gated epoch** (epoch 0, 2 checkpoints, near-instant proving):
a sub-tree was still mid-execution when the prune removed its block →
`world-state ... Unable to get meta data for block 2` → the
`EpochSession` caught the error and went terminal `failed`
(`epoch-session.ts:211-218`) → the prune-reconcile
(`recreateInvalidSessions`, `session-manager.ts:266-269`) found it
already terminal and **deleted it without recreating** → the test's wait
for a trimmed session timed out.
- **Large gated epoch** (4 checkpoints): all sub-trees finished before
the prune, so the session was still non-terminal (`awaiting-root`) and
the prune took the clean cancel-then-recreate path — the test passed.
The flake is the small-epoch case, which is exactly the `2 → 1` survivor
transition the test is meant to cover.
## Fix (test only)
`yarn-project/end-to-end/src/e2e_epochs/epochs_optimistic_proving.parallel.test.ts`:
- Query the session for a job in `awaiting-root` (the state it is
actually in when `beforeProve` fires), keeping the `>= 2` checkpoint
condition.
- Resolve a `gateEntered` signal from *inside* the hook, right before
awaiting the gate, so the gate genuinely blocks and the test learns the
gated epoch only once the session is parked.
- Fire the reorg only after `await executeTimeout(() => gateEntered,
...)`, instead of acting on a transient `awaiting-checkpoints`
observation.
This makes the gate do what its comment always claimed:
deterministically park the session at the top-tree boundary (sub-trees
proven, root prove not yet started) before the reorg. That removes the
sub-tree-vs-prune race — when the reorg fires the session is
non-terminal, so the prune always takes the
cancel-and-recreate-with-survivors path the test verifies. No behavioral
assertion is relaxed; the `>= 2` gating and the final "proven up to the
surviving checkpoint" assertions are unchanged. Not a skip, not a
`.test_patterns.yml` entry.
## Verification
- `yarn build`: exit 0; `yarn lint end-to-end`: clean.
- Local run (`ANVIL_PORT=8600`) passed and reproduced the exact
previously-failing condition — it gated epoch 0 with 2 checkpoints, the
gate engaged this time (`Top-tree proving gated for epoch 0`, absent in
both CI logs), and the recreate path ran (`Prover-node trimmed in-flight
session: 2 → 1 tracked checkpoints`), proving up to the surviving
checkpoint.
## Also in this PR
- Refactored the two proving-gate deferred promises to
`promiseWithResolvers` from `@aztec/foundation/promise`, replacing `new
Promise(resolve => { outerVar = resolve })` with escaped `let`
placeholders.
- Added a TypeScript style note to `yarn-project/CLAUDE.md` preferring
`promiseWithResolvers` for promises settled from outside the executor.
## Note
This flake incidentally exposed a separate product edge, tracked
separately and intentionally not bundled into this test fix: if a reorg
prunes a block out from under an in-flight sub-tree, the `EpochSession`
can go terminal `failed`, and `recreateInvalidSessions` drops terminal
sessions without recreating them (unlike the non-terminal
cancel→recreate path).
This is **not a liveness risk**: the network self-heals (a different
prover node proves the epoch, and publishing dedups against the proven
chain), and the affected node itself usually recovers on the next
checkpoint event for the epoch (a path not gated by `lastTickEpoch`) or
on a process restart. It is durably stuck on a single node only in the
narrow case where no further checkpoint event ever arrives for the
epoch, leaving recovery to the periodic tick — which `lastTickEpoch`
blocks. The likely fix is to classify a prune-induced sub-tree read
fault as a cancellation so the session recreates via the existing tested
path, while keeping the `lastTickEpoch` anti-resubmission guard intact.1 parent 731985c commit f282750
2 files changed
Lines changed: 34 additions & 26 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
198 | 198 | | |
199 | 199 | | |
200 | 200 | | |
| 201 | + | |
| 202 | + | |
201 | 203 | | |
202 | 204 | | |
203 | 205 | | |
| |||
Lines changed: 32 additions & 26 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
2 | 2 | | |
3 | 3 | | |
4 | 4 | | |
| 5 | + | |
5 | 6 | | |
| 7 | + | |
6 | 8 | | |
7 | 9 | | |
8 | 10 | | |
| |||
669 | 671 | | |
670 | 672 | | |
671 | 673 | | |
672 | | - | |
| 674 | + | |
| 675 | + | |
673 | 676 | | |
674 | 677 | | |
675 | 678 | | |
676 | | - | |
677 | | - | |
678 | | - | |
679 | | - | |
680 | | - | |
681 | | - | |
682 | | - | |
683 | | - | |
684 | | - | |
685 | | - | |
| 679 | + | |
| 680 | + | |
| 681 | + | |
| 682 | + | |
| 683 | + | |
| 684 | + | |
| 685 | + | |
| 686 | + | |
| 687 | + | |
| 688 | + | |
| 689 | + | |
| 690 | + | |
| 691 | + | |
| 692 | + | |
686 | 693 | | |
687 | | - | |
| 694 | + | |
688 | 695 | | |
689 | 696 | | |
690 | 697 | | |
691 | | - | |
| 698 | + | |
| 699 | + | |
692 | 700 | | |
693 | 701 | | |
694 | | - | |
695 | | - | |
| 702 | + | |
| 703 | + | |
| 704 | + | |
696 | 705 | | |
697 | 706 | | |
698 | 707 | | |
699 | 708 | | |
700 | | - | |
701 | | - | |
702 | | - | |
703 | | - | |
704 | | - | |
705 | | - | |
| 709 | + | |
| 710 | + | |
| 711 | + | |
| 712 | + | |
| 713 | + | |
| 714 | + | |
| 715 | + | |
706 | 716 | | |
707 | | - | |
708 | | - | |
709 | 717 | | |
710 | | - | |
711 | | - | |
712 | 718 | | |
713 | 719 | | |
714 | 720 | | |
| |||
771 | 777 | | |
772 | 778 | | |
773 | 779 | | |
774 | | - | |
| 780 | + | |
775 | 781 | | |
776 | 782 | | |
777 | 783 | | |
| |||
0 commit comments