Skip to content

ateapi: backfill unset worker state during pod sync - #599

Open
Mesut Oezdil (mesutoezdil) wants to merge 5 commits into
agent-substrate:mainfrom
mesutoezdil:fix/worker-state-backfill
Open

ateapi: backfill unset worker state during pod sync#599
Mesut Oezdil (mesutoezdil) wants to merge 5 commits into
agent-substrate:mainfrom
mesutoezdil:fix/worker-state-backfill

Conversation

@mesutoezdil

@mesutoezdil Mesut Oezdil (mesutoezdil) commented Jul 29, 2026

Copy link
Copy Markdown
Contributor

Follow-up to #517.

#517 added a scheduler filter that skips any worker whose state is not STATE_ACTIVE:

if worker.GetState() != ateapipb.Worker_STATE_ACTIVE {
return false
}

But the syncer writes State only when it creates a worker record. The update path in syncWorkerToStore diffs Ip, SandboxClass and Labels and never touches State, and no other code path writes State onto an existing record.

The store is Redis backed, so worker records outlive an ate-api-server rollout, and worker pods are not recreated by that rollout either. Every worker record written before the State field existed therefore stays at STATE_UNSPECIFIED and is skipped by the scheduler for the whole life of its pod. On an existing cluster upgrading past #517, actor placement fails with ErrNoCapacity until each worker pod is deleted and recreated. The startup sync does not help, because the field diff computes changed == false and never writes.

The scheduler now skips any worker whose state is not STATE_ACTIVE, but the
syncer only sets State when it creates a worker record. The update path diffs
Ip, SandboxClass and Labels and never touches State, and the Redis store
outlives ate-api-server while worker pods are not recreated on upgrade. Every
worker record written before the State field existed therefore stays at
STATE_UNSPECIFIED and is permanently unschedulable, so actor placement fails
with ErrNoCapacity until each worker pod is deleted and recreated.

Backfill the unset state to STATE_ACTIVE when syncing a running pod, which
converges legacy records on the first startup sync. Only the unset state is
backfilled, so a DRAINING worker whose pod name is reused is not resurrected.
ValidateWorker already documents STATE_UNSPECIFIED as tolerated for backward
compatibility, and workflow_resume rejects only DRAINING, so the scheduler's
strict check stays as is.
Comment thread cmd/ateapi/internal/controlapi/syncer.go Outdated
Blindly promoting STATE_UNSPECIFIED to STATE_ACTIVE could resurrect a
worker whose state was unset by an unrelated bug, not just a legacy
pre-field record. Backward-incompatible changes are fine at this
stage, so handle stale legacy records operationally instead.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants