Reclaim actor node state when a worker pod goes away - #1706
Draft
Sairaj Pokale (sairajp-rewind) wants to merge 3 commits into
Draft
Sairaj Pokale (sairajp-rewind) wants to merge 3 commits into
Sairaj Pokale (sairajp-rewind) wants to merge 3 commits into
Conversation
A deleted worker pod is deregistered but its actors are never terminated, so actors/<uid> is orphaned permanently (measured: 55.22 GB over two runs, nothing reclaimed by scale-to-zero, --delete-all, or a reinstall). The worker-delete workflow now sends atelet's existing Terminate before releasing each bound actor: it is the last moment anything knows which node holds the state. It dials by node, since the pod it would dial through is what just disappeared, and is best-effort so an unreachable node cannot wedge deregistration. Terminate failed early for the same reason, tearing the workload down before reaching the directories. A gone ateom (missing socket, no sandbox record, UNAVAILABLE) now skips that step and still reclaims. A reachable ateom that rejects the call still fails: that sandbox is live. Actors holding a local pause snapshot are skipped -- Terminate prunes local checkpoints, and that deletion is unrecoverable.
Evictions, crashes and uninstalls strand actors/<uid> forever: a UID is never revisited and nothing walks the tree. Arm B of the repro showed fix removed none. The sweep runs at startup and on a period (like imagegc.go), reconciling actors/ against the actors the control plane places on this node. Kept rather than swept: everything, if the live set cannot be read in full; actors this atelet is hosting; dirs younger than --actor-gc-min-age; and dirs holding a local pause snapshot, which a PAUSED actor has with no worker assignment to appear in the live set under. Deletion renames out of the UID namespace first, so a crash mid-delete leaves debris the next pass finishes.
--delete-all tears down the control plane but never touches the hostPath it wrote to, so the data outlives the software: 55.22 GB across 3 nodes survived it, a reinstall, and fresh atelets starting on top of it. delete_ate_system now runs a short-lived DaemonSet that empties /var/lib/ateom-gvisor on every node, after the atelet DaemonSet is gone so nothing is writing to it. Readiness is the wipe having finished, so the rollout completing means every node is clean. --keep-node-state opts out.
Collaborator
Author
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.
Fixes #1688
Nothing removes
actors/<actor-UID>/once the worker pod hosting the actor is gone. The directory holds the actor's durable-dir contents, checkpoint/restore state and bundle overlay, so it is as large as the workload made it and it persists for the life of the node. This adds reclaim on the delete path, a sweep for the cases no delete path visits, and a wipe on uninstall.Three commits, reviewable in order:
ateapi,atelet: reclaim actor node state when a worker's pod is goneatelet: sweep orphaned actor state directorieshack: wipe the node state hostPath on uninstall1. Reclaim on the worker-delete path
WorkerWorkflow.releaseBoundActornow callsTerminatebefore releasing each bound actor — the last moment anything still knows which node holds the state. It dials by node (DialForAteletOnNode), since the pod it would otherwise dial through is what just disappeared, and is best-effort: an unreachable node must not wedge worker deregistration.Terminateneeded fixing too. It tore the workload down first and returned early on failure, never reaching the unmounts or the directory removal. A gone ateom (missing socket, no sandbox record,UNAVAILABLE) now skips that step and still reclaims. A reachable ateom that rejects the call still fails — that sandbox is live.2. Sweep as a backstop
Evictions, node crashes and uninstalls leave directories no delete path will ever visit.
cmd/atelet/actorgc.goruns at startup and on a period, like the existingimagegc.go, and comparesactors/against the actors the control plane says belong on this node.It does not delete:
--actor-gc-min-age;It renames a directory before deleting it, so a crash mid-delete leaves debris the next pass cleans up instead of a half-empty directory that still looks live.
Flags, all in
cmd/atelet/actorgc.go:--actor-gc-period(default 5m; 0 disables the periodic pass, startup still runs),--actor-gc-min-age(default 10m),--actor-gc-dry-run(default false).3. Wipe on uninstall
--delete-alltore down the control plane but never touched the hostPath it wrote to, so the data outlived the software — and fresh atelets would start on top of it.delete_ate_systemnow runs a short-lived DaemonSet that empties the state directory on every node, sequenced after the atelet DaemonSet is gone so nothing is writing to it. Readiness is the wipe having finished, so a completed rollout means every node is clean.--keep-node-stateopts out.Verification
3-node cluster, durable-dir workload: 15 concurrent clients each writing a 1 GiB file into its durable dir and suspending/resuming for 10 minutes.
+0.00 GB, 12 dirs remain--delete-all+0.00 GB, 16 dirs remainOn the delete path, 12 reclaim calls produced 12 matching
Terminates that took the gone-ateom branch — a per-node match against the directories present (5/5/2). The sweep's first pass ran before ateapi was serving and correctly reclaimed nothing (reading the live actor set failed; skipping this pass).Reproduced and fixed on both sandbox classes; they share
ateompath.BasePath, so the layout and the leak are identical.Relation to #1677 / #1678
Same pattern, different directory. #1678 removes
ateoms/<pod-UID>/on graceful ateom shutdown; this isactors/<actor-UID>/, written by atelet, and the exits that strand it are exactly the ungraceful ones a defer does not cover.The sweep here and the janitor proposed in #1677 landed on the same shape — both modeled on
imagegc.go, node-scoped, fail-closed, min-age. If both land, worth deciding whether they should be one loop.Not covered
The graceful path. Reclaiming during
Checkpointwould keep steady state near zero and shrink the sweep's caseload; that is a separate change.Terminateidempotency generally. Only one case improves here:Terminatewith no live sandbox now succeeds instead of erroring.