Skip to content

Local snapshot node pinning breaks worker fungibility and causes unrecoverable resume errors if the free workers are on a different node #589

Open

Description

@laoj2

Summary

Local snapshots are tied to the specific nodes where the actor was running when paused. However, workers that belong to the same worker pool can be spread across different nodes in the cluster. As a result, the hard dependency between paused actors (local snapshots) and specific nodes cannot always be satisfied when no workers are available on that node.

This issue is broader than autoscaling, but it completely breaks autoscaling operations. We need to remove or mitigate this hard node dependency when there are no workers available for that cluster/node.

More Details

  1. Worker Pool Spread vs. Pinned Local Snapshots

    • In a multi-node cluster, pods in a WorkerPool are distributed across various nodes.
    • When an actor is paused with a local snapshot, its metadata records the specific node VM where the local snapshot resides
    • Because any subsequent resume requires a worker pod on that exact node, workers across the pool are no longer fungible for that actor. If all worker pods on that node are busy or none exist, the actor cannot resume—even if there are plenty of free workers on other nodes in the same worker pool.
  2. Problems for Autoscaling

(see #198 (comment))

  • Scale-Down Strands Paused Actors: When a worker pod or node is removed during scale-down (HPA, kubectl scale, etc), a paused actor's local snapshot remains pinned to a node that may no longer have any free worker pods (or any worker pods at all).
  • Scale-Up Gives No Locality Guarantee: If paused actors with local snapshots cannot resume because no worker pods are available on their pinned node, scaling up the worker pool may not unblock them (if the new workers land on a different node)

Activity

  1. laoj2 commented on Jul 29, 2026

    @laoj2
    CollaboratorAuthor
  2. aditya-shantanu commented on Oct 21, 2026

    @aditya-shantanu
    Collaborator

    A drain-shaped instance of this, with an e2e test on a private branch: draining_test.go, TestResumeAfterWorkerDrained. Two workers on different nodes, an actor paused on worker A, DrainWorker on A, then ResumeActor. The resume answers ResourceExhausted: no worker has room for the actor for about 10 s while worker B is ACTIVE and empty, until the drained worker's detach has moved the local snapshot off the node; then it succeeds on B. The test passes today because it retries the retriable codes, but a client that treats ResourceExhausted as "cluster full" (the usual reading) gets the wrong signal: the capacity is there, the snapshot is just pinned to a node whose only worker is draining.

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    Projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions