Skip to content

Add first-class ActorSnapshot lifecycle APIs #529

Description

Summary

Substrate creates a durable checkpoint whenever it suspends an Actor. Make each successful checkpoint a first-class, immutable ActorSnapshot that callers can get, list, tag, and use to initialize another Actor.

The design changed during implementation. Superseded decisions remain crossed out so the history is visible:

  • A caller explicitly promotes a suspended Actor's private checkpoint. Every successful SuspendActor automatically creates an ActorSnapshot and exposes it through Actor.latest_snapshot.
  • Tags are globally unique aliases. Tags are owned and addressed by Atespace as atespace/name; the same name may exist in different Atespaces.
  • Visibility is mutable state on the snapshot. Reuse policy belongs to each tag: ATESPACE permits local reuse and PUBLISHED permits reuse from any Atespace without changing the tag's address.
  • Tags are embedded in ActorSnapshot. ActorSnapshotTag is a separate resource and O(1) index entry.
  • Deleting an Atespace cascades to its tags. Atespace deletion is blocked while any Actors or ActorSnapshotTags remain, matching existing non-empty Atespace behavior.
  • Expose explicit snapshot deletion and protect tagged snapshots from it. Explicit snapshot deletion and automatic retention are deferred until the GC policy is designed.
  • CreateActor(source_snapshot) accepts a canonical snapshot reference or a tag. Explicit reuse accepts only a tag reference, ensuring reuse always has an explicit policy.

Motivation

Control planes need to preserve an Actor at a known state and later create an independent Actor from it. Substrate must own the storage layout, compatibility checks, authorization boundary, and stable references rather than requiring callers to copy private storage fields.

Proposed API

Snapshots remain immutable resources:

message ActorSnapshot {
  ResourceMetadata metadata = 1;
  ObjectRef source_actor = 2;
  string source_actor_uid = 3;
  int64 source_actor_version = 4;
  string actor_template_namespace = 5;
  string actor_template_name = 6;
  string actor_template_uid = 7;
  SnapshotContentScope content_scope = 8;
}

Tags are Atespace-owned aliases and retention pins. ATESPACE is intentionally the zero/default scope:

enum ActorSnapshotTagScope {
  ACTOR_SNAPSHOT_TAG_SCOPE_ATESPACE = 0;
  ACTOR_SNAPSHOT_TAG_SCOPE_PUBLISHED = 1;
}

message ActorSnapshotTag {
  ResourceMetadata metadata = 1;
  ObjectRef snapshot = 2;
  ActorSnapshotTagScope scope = 3;
}

message ActorSnapshotRef {
  oneof reference {
    ObjectRef snapshot = 1;
    ObjectRef tag = 2;
  }
}

TagActorSnapshot creates a tag in the source snapshot's Atespace. UpdateActorSnapshotTag publishes or unpublishes it without changing its atespace/name identity. DeleteActorSnapshotTag removes the tag. Tag lookup uses actor-snapshot-tag:<atespace>:<name> and remains O(1).

Publishing changes who may reuse a tag, not who owns it. There is no separate global tag namespace. A higher-level grouping or tiered Atespaces can be considered later if concrete use cases require it.

Suspend and reuse semantics

  • Each successful suspension creates exactly one immutable snapshot for that suspension.
  • The source Actor version is captured when suspension starts, so snapshot metadata describes the state actually checkpointed.
  • CreateActor(source_snapshot) only accepts a tag reference.
  • An ATESPACE tag may initialize Actors only in its owning Atespace.
  • A PUBLISHED tag may initialize Actors in any Atespace while retaining its original atespace/name address.
  • Full and data snapshots currently require the exact source ActorTemplate UID.
  • Cloning a template with external volumes is rejected until CSI/provider snapshot support exists; ordinary suspend/resume behavior is unchanged.

Snapshot data is stored under:

<ActorTemplate snapshotsConfig.location>/snapshots/<opaque-id>

The physical path carries no Actor, Atespace, tag, or reuse-policy semantics. Its flat manifest.json records actor identity and snapshot metadata.

Lifecycle and retention

  • Deleting a tag removes its retention pin.
  • Deleting an Atespace is blocked while any Actors or ActorSnapshotTags remain.
  • Atespace deletion does not delete snapshot metadata or bytes in this change.
  • Snapshot metadata is retained so a future GC/retention policy can make deletion decisions.
  • There is no explicit DeleteActorSnapshot RPC in this version.

Non-goals

  • Automatic snapshot retention or garbage collection.
  • Tiered Atespaces or another higher-level grouping concept.
  • In-place rollback of an existing Actor.
  • Portable reuse across different ActorTemplate UIDs.
  • Cloning external volumes; future support should use CSI/provider snapshot APIs.
  • Cloning node-local pause snapshots.

Expected errors

Condition gRPC status
Actor, snapshot, or tag does not exist NOT_FOUND
Target Actor name or requested tag already exists in its Atespace ALREADY_EXISTS
Atespace still contains Actors or ActorSnapshotTags FAILED_PRECONDITION
Canonical snapshot reference is supplied to CreateActor FAILED_PRECONDITION
Tag is created outside the source snapshot's Atespace FAILED_PRECONDITION
Tag scope does not permit the target Atespace FAILED_PRECONDITION
Snapshot uses a different ActorTemplate UID FAILED_PRECONDITION
Target template has external volumes FAILED_PRECONDITION
Snapshot data is missing or corrupt DATA_LOSS

Acceptance criteria

  • Every successful suspension creates exactly one independently addressable ActorSnapshot and updates Actor.latest_snapshot.
  • Snapshot metadata records the Actor version captured when suspension began.
  • Tags are independently stored and addressed as atespace/name with O(1) lookup.
  • Identical tag names may exist in different Atespaces.
  • Publishing a tag permits cross-Atespace reuse without changing its identity.
  • Atespace deletion is rejected while Actors or ActorSnapshotTags remain.
  • CreateActor accepts only tag references and enforces tag scope and ActorTemplate UID compatibility.
  • Actor creation from snapshots rejects templates with external volumes.
  • Existing suspend, resume, pause, create, and external-volume lifecycle workflows continue to work.
  • Explicit snapshot deletion, automatic retention/GC, and tiered Atespaces remain deferred.

References

Metadata

Metadata

Assignees

No one assigned

    Labels

    area/apiUser-facing API changes

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions