Skip to content

[BUG] Substrate workers never report the actor they are holding #2709

Description

@Charlesthebird

On the substrate page in the UI, the associated actors in the workers lists aren't updating when I send a message, even though the actor shows that it is scheduled on a worker pod on the actors list. It sounds like this is an issue with the worker -> actor mapping in the API response.


🤖 written by Claude (start)

🎯 Affected Service(s)

Controller Service

🚦 Impact/Severity

Minor inconvenience

🐛 Bug Description

GetSubstrateStatus never sets SubstrateWorker.actor_id, actor_template or actor_namespace, so every worker reports holding nothing and the page reads that as an idle pod. The actors half of the same response is correct; only the worker → actor direction is missing.

🔄 Steps To Reproduce

  1. On a cluster with ate-api configured, send a message so an actor is scheduled.
  2. Open the Substrate page. Actors names the worker pod; Workers shows idle for that same pod.

🔍 Additional Context

Introduced in #2697 (a9c209f6), the substrate v0.0.25 update. That release removed the singular assignment from WorkerStatus, so the PR correctly deleted the lines reading it — but nothing replaced them:

-	assignment := worker.GetStatus().GetAssignment()
-		ActorNamespace:  assignment.GetActorTemplateRef().GetAtespace(),
-		ActorTemplate:   assignment.GetActorTemplateRef().GetName(),
-		ActorID:         assignment.GetActor().GetName(),

Under v0.0.25 the binding lives only on the actor, as ActorStatus.worker_assignment, so the worker side has to be joined from the actor list — in the service, where both whole lists are in hand rather than in the UI, which only ever holds pages.

Worth settling: a v0.0.25 worker holds several actors and SubstrateWorker has room for one, so either the pick is made deterministic or system.proto gains a repeated field.


🧭 Fixing it

The binding lives only on the actor under v0.0.25 (ActorStatus.worker_assignment), so the worker side has to be joined from the actor list. That belongs in system.Service.GetSubstrateStatus, which holds both complete lists — done in the UI it would match only where two paged reads overlap, and the reads that reach a browser are pages.

Two decisions come with it, which is why this is not a UI change:

  1. A worker holds several actors, and SubstrateWorker has room for one. Adding actor_status beside actor_id/actor_template/actor_namespace deepens a model the API already contradicts: a pod running three actors cannot be represented, and actor_id: "" with actor_status: "Running" can be. A repeated placement message replacing the four singular fields matches the backend, and is cheap now — GetSubstrateStatus has exactly two consumers, this controller and the UI, with no Python or CLI client.
  2. ActorStatusLabel falls back to the wire constant for a state it has not been taught, and callers sort on what it sends while displaying what they sorted — so ACTOR_STATE_DELETING files itself under A and reads as "Deleting" beneath a heading that says sorted by status. Naming every value fixes the sort and the display together.

🚧 Blocked on this

The Substrate page shows the actor mix as a bar per card. The Workers card has no bar, because workerFromProto sets no actor fields at all — actor_id is empty on every cluster, so a bar there would read as an idle fleet while pods are running. Once this lands the workers bar can be drawn, coloured by what each pod is holding.


🤖 written by Claude (end)

Activity

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

    No type

    Projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions