Skip to content

ateapi: return ResourceExhausted when no worker is free - #908

Open
ILLUM1N0X wants to merge 1 commit into
agent-substrate:mainfrom
ILLUM1N0X:resume-resource-exhausted
Open

ateapi: return ResourceExhausted when no worker is free#908
ILLUM1N0X wants to merge 1 commit into
agent-substrate:mainfrom
ILLUM1N0X:resume-resource-exhausted

Conversation

@ILLUM1N0X

@ILLUM1N0X ILLUM1N0X commented Aug 13, 2026

Copy link
Copy Markdown
Collaborator

ResumeActor returned FailedPrecondition both when the fleet had no free worker and when the actor was not in a resumable state. Only the former is fixable by suspending the actor to drop its node pinning, and a client driving that recovery could not tell the two apart.

assignWorkerAttempt() now returns ResourceExhausted for scheduling.ErrNoCapacity. FailedPrecondition was the de-facto capacity signal, so three router consumers move with it:

  • retryable(): ResourceExhausted is parked, so saturation behaves as before. FailedPrecondition stays retryable on purpose. It no longer carries saturation, but it does cover states a concurrent operation can move the actor out of.
  • mapResumeError(): ResourceExhausted now preserves the gRPC description, keeping saturation at 503 as today. Its former 429 "rate limited" branch was written speculatively as nothing in substrate produced the code until now, and the fleet being full is not the caller sending too many requests.
  • classifyOutcome(): no_capacity moves to ResourceExhausted, and FailedPrecondition gets its own label so the capacity bucket stops collecting non-capacity errors.

TestResumeActor_RelocatesAfterSuspendFromPaused covers the recovery flow this signal exists for: a PAUSED actor pinned to a full node cannot resume, and after a suspend clears the pinning it schedules onto a worker on another node.

Fixes #660

  • make test clean (except the pre-existing macOS-only internal/atunnel failures, untouched by this PR)
  • make fmt clean
  • go vet ./cmd/... clean

ResumeActor returned FailedPrecondition both when the fleet had no free
worker and when the actor was not in a resumable state. Only the former
is fixable by suspending the actor to drop its node pinning, and a
client driving that recovery could not tell the two apart.

`assignWorkerAttempt()` now returns `ResourceExhausted` for
`scheduling.ErrNoCapacity`. `FailedPrecondition` was the de-facto
capacity signal, so three router consumers move with it:

- `retryable()`: `ResourceExhausted` is parked, so saturation behaves as
  before. `FailedPrecondition` stays retryable on purpose. It no longer
  carries saturation, but it does cover states a concurrent operation
  can move the actor out of.
- `mapResumeError()`: `ResourceExhausted` now preserves the gRPC
  description, keeping saturation at 503 as today. Its former 429 "rate
  limited" branch was written speculatively; nothing in substrate
  produced the code until now, and the fleet being full is not the
  caller sending too many requests.
- `classifyOutcome()`: `no_capacity` moves to `ResourceExhausted`, and
  `FailedPrecondition` gets its own label so the capacity bucket stops
  collecting non-capacity errors.

`TestResumeActor_RelocatesAfterSuspendFromPaused` covers the recovery
flow this signal exists for: a PAUSED actor pinned to a full node cannot
resume, and after a suspend clears the pinning it schedules onto a
worker on another node.

Part of agent-substrate#660.
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.

Handling Resume from Pause failures

1 participant