Repository navigation
fix(scheduler): tolerate backwards clock steps in worker liveness refresh - #2900
Conversation
…resh A backwards step on the scheduler's wall clock (NTP, VM time sync) made the liveness refresh error on the "stale" timestamp, and that error aborted processing of the message that proved liveness -- including a finished ExecuteResult, stranding the operation in Executing. Keep the newer timestamp with a warning instead, and make the refresh best-effort so a result the worker already produced always reaches update_action. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
…rt liveness Two adversarial tests for the safety argument behind the clock-step fix: - late_result_from_evicted_worker_cannot_touch_reassigned_attempt: W1 is dispatched an operation, evicted, and the operation is reassigned to W2; W1's late result is refused and the client sees only W2's result. The fences are the worker-map and running_action_infos checks in ApiWorkerScheduler::update_action. - evicted_worker_late_result_never_reaches_state_manager_test: exercises touch_liveness_best_effort swallowing a non-clock failure (worker evicted) and proves the swallowed refresh does not widen write authority: the stale ExecuteResult never reaches the state manager. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
|
Red-team pass on this PR (adversarial review of the best-effort liveness change, specifically whether it widens what a stale/evicted worker can do): Stale-worker verdict: the W1 → eviction → reassignment → late-W1 attack is fenced, and none of the fences involve liveness. The chain:
Clock verdict: the fix handles backward steps (equal timestamps were already accepted; Test gap found and closed: with the fix in place, the original clock-step test no longer exercises a failing
Both pass locally alongside the existing suites (2/2 and 16/16). |
|
I expected a panic and didn't get one. The agent is smarter. I think this looks good pending @rmlarsen's test. |
… only caller Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
|
/build-image nativelink |
|
/build-image nativelink-worker-init |
|
Image built and pushed! |
|
Image built and pushed! |
What and why
A backwards step on the scheduler's wall clock (NTP step, VM time sync) makes
refresh_lifetimeerror on the "stale" timestamp, and that error aborts processing of the very message that proved the worker alive. When that message is a finishedExecuteResultorExecuteComplete, the result the worker already produced is discarded and the operation is stranded inExecutingwith nothing left to finish it.Two changes:
api_worker_scheduler: a liveness refresh with an older timestamp keeps the newer one and logs a warning instead of erroring — the worker is no less alive because the scheduler's clock stepped.worker_api_server: the liveness refresh on result/accept/decline/complete messages is best-effort (touch_liveness_best_effort), so a result the worker already produced always reachesupdate_actioneven if the refresh fails for some other reason (e.g. the worker was just evicted).How was this verified?
nativelink-scheduler/tests/liveness_clock_step_test.rs: steps the mock clock backwards between two messages from the same worker and asserts the refresh succeeds, the newer timestamp is kept, and the action completes normally.worker_api_server_test.rs(execution_response_lands_when_clock_steps_backwards_test): the scheduler'snow_fnsteps back 2s before the worker'sExecuteResultarrives; asserts the result still reachesupdate_actionand the worker keeps its registration. Both exercise the exact path that errored before the change.Executinguntil timeout.Risk
Low and contained to the worker-liveness path. The behavior change: a backwards clock step (previously an error that could cost a worker its message) is tolerated; the keepalive-gap metric uses
saturating_sub, so a step contributes 0 rather than garbage. The scheduler-side execution timeout still runs off the retained (newer) timestamp, so no deadline gets extended.AI assistance
An agent (Claude) drafted the fix and tests from a deployment incident I triaged; I reviewed the diff and the test assertions.
🤖 Generated with Claude Code