Skip to content

Prefer workers with cached actor images during scheduling #276

Description

@han2ni3bal-pixel

Description

Atelet maintains a node-local image cache, but the scheduler does not know which images are cached on each node. As a result, an actor may be assigned to a cold node and pull its images again, even when another eligible node already has them cached.

Image preparation happens synchronously during ResumeActor, so unnecessary pulls increase actor startup latency and registry traffic.

Proposal

  • Let atelet report its cached image digests to the control plane.
  • Store cache information by node.
  • After applying existing constraints, prefer available workers whose node has all required actor images cached.
  • Fall back to the current random selection when no cache hit is available.

Local snapshot availability remains a hard constraint. Image cache affinity is applied only among nodes that can restore the selected snapshot.

A general scoring framework is not required: use cache-hit preference with fallback.

Expected Outcome

Reduce unnecessary image pulls and improve Actor Resume latency and consistency.

Related Issues

This proposal complements those efforts by making worker selection aware of node-local image cache availability.

Activity

  1. han2ni3bal-pixel commented on Jun 26, 2026

    @han2ni3bal-pixel
    ContributorAuthor

    Following up on Julian Gutierrez Oschmann (@juli4n)'s comment on PR #298 — I agree we should agree on a unified approach for node-locality before landing more one-off scheduling features.

    Where we are today

    Only one node-locality mechanism is merged: when an actor is paused with a local snapshot, NodeVmsWithLocalSnapshots on the Actor record is a hard constraint during ResumeActor — workers must be on one of those nodes.

    #276 / PR #298 proposes a second path (not merged yet): atelet periodically reports OCI image digests, control plane stores them per node with a TTL, and the scheduler prefers workers whose node already has the required images, falling back to random selection.

    The concern isn't that we already run two models in production — it's that merging #298 as a standalone RPC and store key sets a pattern that #228 (rootfs overlay), #135 (hot snapshot cache), and future volume locality will likely copy unless we align now.

    Proposed direction

    Split two concerns:

    1. Actor-bound, durable, hard constraints — e.g. pause local snapshot nodes. Stays on the Actor record.
    2. Node-bound, TTL-backed, soft preferences — e.g. warm images, extracted rootfs, resident snapshots. One shared node inventory reported periodically by atelet and stored per node. Stale or wrong data should only hurt latency, not correctness.
      Keep the Prefer workers with cached actor images during scheduling #276 scheduling model: fixed layers (hard filter → ordered soft preferences → random fallback), not a general scoring framework.

    Soft preference order (when we add them): hot snapshot on node first, then rootfs overlay (#228), then image tar cache (#276). Pause local snapshots remain hard-only via the Actor record — don't move them into node inventory.

    Rough plan

    Phase 0 (this issue): agree on hard vs soft split, node inventory as the long-term envelope, and preference ordering. Don't ossify ReportNodeImageCache as the permanent API.
    Phase 1: implement #276 on NodeInventory directly (rebase PR #298 onto that, rather than merge then rename).
    Phase 2: add rootfs overlay digests from #228.
    Phase 3: add resident snapshot prefixes for Suspend hot-restore (#135 light).
    Phase 4 (later): secondary indexes / #135 ListOptions only if query patterns prove we need them.
    Happy to help turn Phase 0 into a short design note here if that's useful.

  2. vicentefb commented on Oct 30, 2026

    @vicentefb
    Collaborator

    adding a measurement that argues for this.

    I ran an RL trainer shape on Substrate: 32 images with 16 rollouts each per step, 60 steps, 1,920 distinct images, 783 nodes. Every rollout of an image landed on a different node, and every node pulled the image.

    Measured
    Cold pulls per actor 0.96
    Pulls for 1,920 images 29,301 (about 32 per step would do)
    Image unpack per actor 28 s, 97% of the start time
    Step time ~2 min, against 15 to 30 s when the images are already on the nodes

    One more thing: a restore from a golden snapshot onto a node without the image costs the same as booting the image, because the golden download and the gVisor restore together are about 0.3 s. So the preference you describe is also what makes golden snapshots pay off.

  3. tomergee commented on Nov 2, 2026

    @tomergee
    Collaborator

    Another consumer, and two asks for the unified node-locality approach this thread wants.

    In ai-on-gke/sandboxenv-rl we are planning a pre-flight that fills node image caches before an RL job (ai-on-gke/sandboxenv-rl#205): with placement image-blind, the first boot of each image pays the unpack on whatever node it lands on, and #3481's numbers (unpack 1 ms with the layers on the node, 9–50 s without, ~97 % of a cold first command) are what the job sees. The workaround available on main is client-side: WorkerPools as placement domains, each with its own label, and Actor.worker_selector per actor to keep an image's actors inside the domain that holds it. A scheduler preference like PR #298 would retire that.

    Until it lands, a client needs two things the API does not give today:

    1. A way to place on a node without one WorkerPool per node: either mirror a configurable set of node labels (kubernetes.io/hostname, zone, node pool) into Worker.labels at registration, which worker_selector can then name, or a per-actor node affinity composed into the RequiredNodes the scheduler already has for local snapshots.
    2. Visibility of which images a node holds. Prefer workers with cached actor images during scheduling #298 already has atelet report cached digests to ate-api; exposing that report (a gauge with the digest per node, or a field on Worker.status) lets a client verify a warm node and choose where to send an image's actors, and it is also how an operator would see a pre-warm took.

    Happy to write either up in more detail if it helps the unified design.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions