Skip to content

Rename SnapshotsConfig to SnapshotConfig everywhere in the codebase - #1791

Merged
Julian Gutierrez Oschmann (juli4n) merged 1 commit into
agent-substrate:mainfrom
laoj2:api-cleanups-plural
Sep 23, 2026
Merged

Julian Gutierrez Oschmann (juli4n) merged 1 commit into
agent-substrate:mainfrom
laoj2:api-cleanups-plural

Conversation

@laoj2

Copy link
Copy Markdown
Collaborator

related to -> #1378

  • Tests pass
  • Appropriate changes to documentation are included in the PR

@juli4n

Copy link
Copy Markdown
Collaborator

Needs rebase.

Merged via the queue into agent-substrate:main with commit fb4b315 Sep 23, 2026
11 checks passed
@laoj2
Luiz Oliveira (laoj2) deleted the api-cleanups-plural branch September 23, 2026 15:26
@bowei Bowei Du (bowei) added area/api User-facing API changes area/api-machinery area/storage kind/cleanup Small fixes that are not bugs, for example a typo in a code comment labels Sep 29, 2026
Timo Derstappen (teemow) added a commit to giantswarm/substrate that referenced this pull request Oct 12, 2026
…kground and resume it on any worker

A pause checkpoints the workload onto its worker's node and nowhere else:
the node holds the only copy of the actor's state, and the record pins the
actor to that node (LocalSnapshotInfo.node_vms_with_local_snapshots →
Constraints.RequiredNodes). A node that goes away — preempted, scaled down,
rolled — takes the state along while the record still says PAUSED there. The
next resume then finds no worker that applies, the scheduler reports the
locality miss as ResourceExhausted "no free workers available" (the message of
a full pool), the router parks the request for its whole budget and every
retry repeats this; a suspend dials the atelet on the dead node and stays
retryable forever, so the actor cannot be suspended or deleted either.
Actors parked on a question for a person sit like this for hours.

Pauses now become durable shortly after they complete. After FinalizePaused
the control plane queues the actor with a background uploader (two workers
per replica, rate-limited requeues, a resync every minute that also finds
pauses from before an upgrade or a restart). The upload goes through the
atelet on the snapshot's node, the path a suspend of a paused actor already
uses, with two differences: the destination is named after the local
snapshot, so a retry re-sends the same request and overwrites the same
objects, and the atelet keeps the local copy (UploadPausedCheckpointRequest
.keep_local) — the actor stays PAUSED and its node remains the fast path. On
success the copy is recorded as the actor's external snapshot with the local
snapshot it came from (ExternalSnapshot.source_local_snapshot_name), under a
version precondition: no lease is held while the checkpoint streams, a
resume that arrives meanwhile is not delayed, and an upload the actor has
outrun is deleted again. The external snapshot the copy replaces is released
once the record points at the copy.

That marker is what the rest keys on. A paused actor's external snapshot is
its durable copy only while it names the local snapshot the actor holds;
the snapshot of an earlier suspend, or the upload of an earlier pause, is
older state and never stands in for the pause. While no copy exists the
node stays a hard constraint, exactly as before. Once one exists:

  - Placement prefers the snapshot's nodes (Constraints.PreferredNodes,
    ranked ahead of every other eligible worker, excluding none) instead of
    requiring them. Restore takes the local snapshot when the chosen worker
    is on such a node and the durable copy — with the scope the pause
    captured, memory included — anywhere else. A paused actor placed off its
    node with no copy is refused before any atelet is dialed rather than
    restored from an older snapshot.
  - A suspend commits the copy as captured: nothing is uploaded, no
    destination minted, the node not dialed except to drop the local copy
    best-effort (the new PruneLocalCheckpoints RPC, bounded to ten seconds).
    Narrowing the copy to the template's commit scope would need the node
    the actor may be leaving.
  - Deleting a paused actor collects the copy with the actor's prefix, as
    for a suspended actor, and asks the node to drop the local copy where an
    atelet answers.

The upload uses the scope the pause captured, not the commit scope: the
copy must restore exactly as the local snapshot would. Tests cover the
marker, the resync selection and its grace, the constraint switch, the
record commit with the replaced snapshot released, the stale upload
discarded and the concurrent replica's record left alone, the suspend
shortcut at mark and upload, the placement guard, the scheduler's preference
and its fallbacks, and — end to end against the fake atelet — the upload
after a pause, the resume onto another node restoring the copy after the
snapshot's node lost its worker, the resume that still prefers the node, the
suspend that uploads nothing, and the delete that releases both copies.

Signed-off-by: Timo Derstappen <[email protected]>
Re-pin note (v0.3.0-alpha1): ExternalSnapshot.source_local_snapshot_name
is field 4 (upstream took 3 for actor_template_uid); LocalSnapshotInfo is
LocalSnapshot (agent-substrate#1856) and SnapshotsConfig is SnapshotConfig (agent-substrate#1791); the
background upload no longer reads a crash directive from atelet (the
ateerrors taxonomy is gone, agent-substrate#1817): an upload atelet refuses is retried
by the resync and the actor's next resume answers with the cause; the
node-gone refusal is a plain DataLoss status. Upstream deleted the
python stubs, so none are regenerated.

(cherry picked from commit 31ae728)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area/api User-facing API changes area/api-machinery area/storage kind/cleanup Small fixes that are not bugs, for example a typo in a code comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants