Skip to content

Make the testVM pause gate resumable - #488

Draft
samliok wants to merge 3 commits into
mainfrom
fix/testvm-resumable-pause
Draft

Make the testVM pause gate resumable#488
samliok wants to merge 3 commits into
mainfrom
fix/testvm-resumable-pause

Conversation

@samliok

@samliok samliok commented Aug 4, 2026

Copy link
Copy Markdown
Collaborator

What was wrong
testVM.pause/resume only flipped an atomic.Bool (instance_test.go:686-687). BuildBlock and WaitForPendingBlock read that flag once on entry and then blocked on ctx.Done() and nothing else, so resume() could not release a caller already parked in the gate.

Consequence
Flakes TestInstanceRestartAcrossEpochs. The node is restarted while the VM is paused, then resumed. If the restarted node reaches WaitForPendingBlock before resume() runs, it parks indefinitely: the context is context.WithCancel(e.finishCtx) from monitorProgress (simplex/epoch.go:2752) and is only cancelled on a round transition, which cannot happen while that round is waiting for a block to build. The 20s require.Eventually in waitForNumBlocks then fails with "Condition never satisfied". This also violated the VM.WaitForPendingBlock contract in config.go:53-55, which says it returns when the VM signals a block should be built.

Fix
pause() creates a channel and resume() closes it; both gated methods select on that channel alongside ctx.Done(). While paused no block is produced and context cancellation is still honoured, so the existing "let the caller impatient build time out" behaviour is unchanged. Test-only, no production code touched.

Verification
New TestTestVMResumeWakesPausedCallers fails before the fix ("WaitForPendingBlock did not return after resume"), passes after.
Causal link to the flake: inserting a 300ms sleep before the vm.resume() call made TestInstanceRestartAcrossEpochs fail deterministically in ~21s with the identical assertion, and that same sleep passes with the fix applied. Both tests pass 3x under -race with the sleep removed.

Found while debugging CI failure https://github.com/ava-labs/Simplex/actions/runs/30949229624/job/92126838329 (--- FAIL: TestInstanceRestartAcrossEpochs (21.10s)).

pause/resume flipped an atomic that BuildBlock and WaitForPendingBlock
read once on entry before blocking on ctx.Done() alone, so resume could
not release a caller already parked in the gate.

pause now creates a channel that resume closes, and both methods select
on it alongside ctx.Done().
@samliok

samliok commented Aug 4, 2026

Copy link
Copy Markdown
Collaborator Author

@yacovm i asked claude to take a look at a flake for one of the instance tests it came up with some nonsense solution with this pr. could you take a look at a better solution when you get the chance? i'm debugging other flakes atm 😅

samliok added 2 commits August 4, 2026 17:52
…its a block

createFinalizedBlockVerificationTask indexed only the block it verified, so a
finalization stored while its sequence was ahead of the storage was never
revisited once replication caught up. The node then held a finalized block it
could not commit, and any peer that answered with that sequence drove
processFinalizedBlock into storeFinalization on a round that already had a
finalization, whose error reaches HandleMessage.

Index from the round instead, matching persistFinalization.
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