Skip to content

execution/stagedsync: extract helpers so robustness tests drive production#21046

Merged
mh0lt merged 1 commit into
fix/parallel-apply-loop-partial-batch-no-spurious-flagfrom
fix/parallel-exec-loop-helpers-followup
May 7, 2026
Merged

execution/stagedsync: extract helpers so robustness tests drive production#21046
mh0lt merged 1 commit into
fix/parallel-apply-loop-partial-batch-no-spurious-flagfrom
fix/parallel-exec-loop-helpers-followup

Conversation

@mh0lt

@mh0lt mh0lt commented May 7, 2026

Copy link
Copy Markdown
Contributor

Follow-up to #21039 addressing Copilot's review feedback that the new robustness tests were modelling the exec-loop and apply-loop decision trees in local closures — which would let the model drift from production silently.

This PR extracts three pure helpers from the production code and rewires the tests to drive them directly. No production behaviour change — the helpers wrap the exact same logic that was inline before.

Helpers extracted

Helper Production call site What it pins
closeApplyChannels (pe method) execLoop deferred close commitResults-then-applyResults order
execLoopShouldExit (pure) execLoop per-blockResult branch sizeLimit > maxBlockNum > Exhausted > StopAfterBlock precedence
shouldMarkExhaustedAtBlock (pure) executeBlocks per-cycle limit gate initial-cycle gates + blockLimit + maxBlockNum guard

closeApplyChannels returns the actual close-order as a string slice — the previous test was racy because observer goroutines could wake up out of order even when the helper closed the channels in the right order; the slice removes that race entirely.

Tests rewired

  • TestApplyLoopChannelCloseOrder — now calls pe.closeApplyChannels() on a real parallelExecutor with mock channels and asserts on the returned close-order slice. Also covers double-close recovery and the post-close nil-out.
  • TestCloseApplyChannelsDoubleCloseRecovers — verifies the recover-on-double-close path doesn't count pre-closed channels as freshly closed.
  • TestExecLoopShouldExitPriority — table-driven coverage of each branch, including cross-condition cases (e.g. maxBlockNum AND Exhausted both set on the final block — maxBlockNum must win for the clean reachedMaxBlock path).
  • TestShouldMarkExhaustedAtBlock — table-driven coverage of the initial-cycle gates (`lastExecutedStep`/`lastFrozenStep`/`DiscardCommitment`), the `blockLimit==0` disabled case, the span < limit case, and the `maxBlockNum` exact-hit no-mark case.

The previous `TestExecLoopHonorsBlockResultExhausted` and `TestExecLoopExhaustedOnlySetOnFinalBlock` tests are removed — both are subsumed by the new `TestExecLoopShouldExitPriority` and `TestShouldMarkExhaustedAtBlock` cases that drive production code.

Test plan

  • All four new tests pass: `TestApplyLoopChannelCloseOrder`, `TestCloseApplyChannelsDoubleCloseRecovers`, `TestExecLoopShouldExitPriority`, `TestShouldMarkExhaustedAtBlock`
  • Existing tests in the same file (`TestApplyLoopMissingBlocks`, `TestExecLoopExitCheck`, `TestApplyLoopRootResultsCloseDoesNotRace`, etc.) still pass
  • `make lint` clean
  • No production behaviour change (`go build ./...` clean)

Stack

Stacked on #21039 (the apply-loop fix). Both should land together — this PR alone doesn't make sense without #21039's helper-using tests as context.

…ction

Addresses Copilot's review feedback on #21039: the original tests
modeled the exec-loop and apply-loop decision trees in local closures,
which would let the model drift from production silently. This
follow-up extracts three pure helpers from the production code and
rewires the tests to drive them directly.

Helpers extracted:

* `closeApplyChannels` (pe method) — pins commitResults-then-applyResults
  close ordering. Returns the close sequence as a string slice so tests
  can verify the order deterministically without racing on observer-
  goroutine wakeups (the previous test had a real flake here).

* `execLoopShouldExit` (pure func) — encodes the per-blockResult exit
  precedence: sizeLimit > maxBlockNum > Exhausted > StopAfterBlock.
  Production exec-loop now switches on the returned decision; tests
  pin each precedence rule with table-driven cases.

* `shouldMarkExhaustedAtBlock` (pure func) — encodes executeBlocks's
  per-cycle block-limit gate (initial-cycle protections + blockLimit
  span check + maxBlockNum guard). Replaces the old "dispatch
  convention" model test that didn't drive any production code.

Tests updated:

* `TestApplyLoopChannelCloseOrder` — calls `pe.closeApplyChannels()`
  on a real `parallelExecutor` with mock channels and asserts on the
  returned close-order slice. Also covers double-close recovery and
  the post-close nil-out.
* `TestExecLoopShouldExitPriority` — table-driven coverage of the
  exit-priority decisions including the cross-condition cases (e.g.
  maxBlockNum AND Exhausted both set on the final block — maxBlockNum
  must win).
* `TestShouldMarkExhaustedAtBlock` — table-driven coverage of the
  initial-cycle gates, blockLimit==0 disabled case, span < limit
  case, and the maxBlockNum exact-hit no-mark case.

No production behaviour change — pure refactor + test rewire.
@mh0lt mh0lt requested a review from yperbasis as a code owner May 7, 2026 17:58
@mh0lt mh0lt merged commit 52dffb3 into fix/parallel-apply-loop-partial-batch-no-spurious-flag May 7, 2026
1 check passed
@mh0lt mh0lt deleted the fix/parallel-exec-loop-helpers-followup branch May 7, 2026 20:26
@yperbasis

Copy link
Copy Markdown
Member

@mh0lt this PR didn't target main. Was that on purpose?

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.

2 participants