Repository navigation
fix(checkpoint): reply idle to a prepare whose checkpoint ended while it waited - #4660
Merged
Merged
Conversation
… it waited A prepare that waits for the participant to become ready can wake after its checkpoint has ended. For example, its lease expires and admission reopens, then the controller prepares a second checkpoint. When the first prepare woke, it replied with the participant's current phase, which by then belonged to the second checkpoint. So the first prepare could report `prepared` for a checkpoint that no longer existed. The participant's state stayed safe. The lease expiry had already fenced the first checkpoint's ID, so a commit for it is refused as `stale_checkpoint`. Only the reply was misleading, and a controller reading it could believe its own checkpoint was prepared. A prepare whose checkpoint is no longer the open one now replies `idle`, with the current readiness report. This matches the reply of a prepare that a resume interrupts, which already reported `idle`. A controller treats any phase other than `prepared` as not prepared. Once a resume or a lease expiry has ended the checkpoint, a later prepare or commit for it is refused as `stale_checkpoint`. A prepare that wakes after a concurrent prepare of the same checkpoint finished still reports that checkpoint's phase. Signed-off-by: Ananth Subramaniam <ansubramania@nvidia.com>
This was referenced Oct 16, 2026
Contributor
|
🔄 Merge queue validation started! You can track the progress here: https://github.com/NVIDIA-NeMo/Gym/actions/runs/38833982337 |
This was referenced Oct 19, 2026
This branch was previously deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What changed and why
A participant's
preparewaits for the participant to become ready before it replies.While it waits, its checkpoint can end.
For example, the controller goes quiet, the checkpoint's lease expires, and the participant reopens admission.
The controller can then prepare a second checkpoint, which reaches
preparedbefore the first prepare wakes.When the first prepare woke,
ParticipantControlPlane.prepareinnemo_gym/_checkpoint/control.pyreplied withself.phase, the phase of whichever checkpoint was open by then.In the example above, the first prepare reported
preparedfor a checkpoint that had already ended.The participant's state stayed safe.
The lease expiry had fenced the first checkpoint's ID, so a commit for it is refused as
stale_checkpoint.Only the reply was wrong, and a controller reading it could believe its own checkpoint was prepared.
A prepare whose checkpoint is no longer the open one now replies
idle, together with the current readiness report.This follows the existing reply for a prepare that a resume interrupts, which already reported
idle.The controller in
nemo_gym/_checkpoint/coordination.pytreats any phase other thanpreparedas not prepared.Once a resume or lease expiry has ended the checkpoint, a later prepare or commit for it is refused as
stale_checkpoint.A
stale_checkpointrefusal was the other candidate reply.It was not chosen because the reply to a prepare interrupted by a resume, which is the same situation, is already
idle, and a unit test pins that reply.Using one reply for both keeps the contract the same for every way a checkpoint can end while a prepare waits.
A prepare that wakes after a concurrent prepare of the same checkpoint finished it still reports that checkpoint's phase, because that phase belongs to its own checkpoint.
Issue
None needed.
This is a small fix for a race found by a test in the checkpoint stack's fault tests (#4197).
Validation
A new unit test in
tests/unit_tests/test_checkpoint_control.pyholds the first prepare at a gate while it waits for readiness.While it is held, the lease expires, admission reopens, and a second checkpoint is prepared.
The test then releases the first prepare and checks three things: the second prepare reports
prepared, the first prepare reportsidlerather than the second checkpoint's phase, and a commit for the first checkpoint is refused asstale_checkpoint.The test uses events rather than fixed sleeps, apart from waiting for the lease to expire.
On
mainwithout the fix, it fails because the first prepare reportsprepared.Commands, run with
PYTHONPATHset to the worktree:Rollout evidence
N/A.
The change affects only the control-plane reply to a prepare whose checkpoint ended while it waited, and a deterministic unit test covers that case.
No environment, agent, or verifier behavior changes.
Compatibility
The reply to
/ng-control/v1/checkpoint/preparechanges only in this race: it reportsidlewhere it used to report another checkpoint's phase.The reply shape is unchanged.
A controller that checked for
preparednow correctly sees its checkpoint as not prepared.