Skip to content

atenet: never cancel an in-flight resume at the park budget - #991

Open
Omer Yahud (omeryahud) wants to merge 1 commit into
agent-substrate:mainfrom
omeryahud:parking-grpc-cancellation-fix
Open

atenet: never cancel an in-flight resume at the park budget#991
Omer Yahud (omeryahud) wants to merge 1 commit into
agent-substrate:mainfrom
omeryahud:parking-grpc-cancellation-fix

Conversation

@omeryahud

@omeryahud Omer Yahud (omeryahud) commented Aug 17, 2026

Copy link
Copy Markdown
Collaborator

The park budget doubled as the ResumeActor RPC deadline, so a resume that outlived it was cancelled mid-restore. ateapi claims the worker and persists RESUMING before the restore begins, rolls back neither on cancellation, and nothing reclaims a RESUMING actor on a live worker - a budget cancel therefore discarded the restore and stranded the worker, which is the TestRequestParking/ParkThenServed flake (#675).

The budget now bounds the retry loop only: an attempt still in flight when it elapses runs to completion on a non-cancellable context (bounded by ateapi's own server-side RPC deadline, with Envoy's ext_proc timeout unchanged as the client-side ceiling), and its real result is classified - an overshooting restore is served late instead of failed, and a late retryable error still surfaces as the capacity
503.

The e2e window widens to admit a served-late overshoot, and the test now pins the invariants the flake exposed: actor B reaches RUNNING and a follow-up request is served warm.

Fixes #675

  • Tests pass
  • Appropriate changes to documentation are included in the PR

@omeryahud
Omer Yahud (omeryahud) marked this pull request as ready for review August 18, 2026 07:35
@omeryahud

Copy link
Copy Markdown
Collaborator Author

Zoe Zhao (@zoez7) yufan-su Hi guys, would appreciate your review here

@bowei Bowei Du (bowei) self-assigned this Aug 21, 2026

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The fix itself looks right, but the branch needs a rebase before it can merge — #1025 landed after it branched and renamed the proto surface the new tests use, so the PR no longer compiles against current main. Holding off on approval until that's done; happy to approve after the rebase.

What I verified:

  • context.WithoutCancel(bgCtx) for the attempt plus keeping bgCtx on wait.ExponentialBackoffWithContext gives exactly the intended split: the budget stops new attempts, the in-flight attempt runs to completion, and an overshooting success is honored by the backoff loop (condition returning true short-circuits the ctx check). The lastRetryErr != nil && (bgCtx.Err() != nil || wait.Interrupted(err)) classification correctly maps a late retryable failure to the capacity 503.
  • The "bounded by ateapi's own server-side RPC deadline" claim holds: MaxDeadlineUnaryInterceptor(maxRPCDeadline) in cmd/ateapi/main.go caps it at 10m.
  • go test -race ./cmd/atenet/internal/router/ingress/ passes on the branch as-is.

Rebase breakage (merge with main is textually clean but doesn't build):

  • resumer_test.go: the new tests use ateapipb.Actor_STATUS_RUNNING and a top-level WorkerAssignment on Actor; on main this is now Status: &ateapipb.ActorStatus{State: ateapipb.ActorState_ACTOR_STATE_RUNNING, WorkerAssignment: ...}.
  • internal/e2e/suites/parking/parking_test.go: the new follow-up assertion calls waitForActorStatus(..., ateapipb.Actor_STATUS_RUNNING); main renamed the helper to waitForActorState(..., ateapipb.ActorState_ACTOR_STATE_RUNNING).

One trade-off worth a sentence in the doc if you think it matters: attempts now have no client-side deadline at all, so a pathologically slow ResumeActor holds its parking-lot slot for up to ateapi's 10m ceiling rather than the budget. Seems acceptable given the alternative is a stranded worker, but it does change worst-case slot occupancy.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Inline notes for the rebase (details in my earlier review).

ctxErrAtReturn = ctx.Err()
mu.Unlock()
return &ateapipb.ResumeActorResponse{
Actor: &ateapipb.Actor{Metadata: &ateapipb.ResourceMetadata{Name: testActorName}, Status: ateapipb.Actor_STATUS_RUNNING, WorkerAssignment: &ateapipb.WorkerAssignment{WorkerPodIp: expectedIP}},

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

After #1025 this no longer compiles on main: Status is now &ateapipb.ActorStatus{State: ateapipb.ActorState_ACTOR_STATE_RUNNING, WorkerAssignment: ...}WorkerAssignment moved inside ActorStatus and the enum was renamed. The merge with main is textually clean, so CI won't flag it until the test workflows run against the merge commit.

// The flake's root cause stranded actors in RESUMING with the worker
// claimed (#675): pin that B really converges and a follow-up request
// is served warm — a stranded actor would 503 it.
waitForActorStatus(ctx, t, clients, actorB, ateapipb.Actor_STATUS_RUNNING)

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same #1025 rebase issue: main renamed this helper to waitForActorState and the enum to ateapipb.ActorState_ACTOR_STATE_RUNNING.

// the restore away and strands the worker (#675). An attempt still
// running when the budget elapses is waited for and its real result
// classified below; ateapi's own server-side RPC deadline bounds it.
attemptCtx := context.WithoutCancel(bgCtx)

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Worth a sentence in docs/request-parking.md: with no client-side deadline on the attempt, a pathologically slow ResumeActor now holds its parking-lot slot up to ateapi's 10m maxRPCDeadline instead of the budget. Acceptable vs. stranding a worker, but it changes worst-case slot occupancy.

@omeryahud

Copy link
Copy Markdown
Collaborator Author

Thanks Aditya Shantanu (@aditya-shantanu), I've rebased and addressed your doc comment

The park budget doubled as the ResumeActor RPC deadline, so a resume
that outlived it was cancelled mid-restore. ateapi claims the worker
and persists RESUMING before the restore begins, rolls back neither on
cancellation, and nothing reclaims a RESUMING actor on a live worker -
a budget cancel therefore discarded the restore and stranded the
worker, which is the TestRequestParking/ParkThenServed flake (agent-substrate#675).

The budget now bounds the retry loop only: an attempt still in flight
when it elapses runs to completion on a non-cancellable context
(bounded by ateapi's own server-side RPC deadline, with Envoy's
ext_proc timeout unchanged as the client-side ceiling), and its real
result is classified - an overshooting restore is served late instead
of failed, and a late retryable error still surfaces as the capacity
503. This also closes a mapping hole where budget expiry with zero
completed attempts escaped as a raw DeadlineExceeded and surfaced as a
504 (what actually failed BudgetExhaustion in run 30476804701): the
loop can no longer exit before its first attempt has completed.

The e2e window widens to admit a served-late overshoot, and the test
now pins the invariants the flake exposed: actor B reaches RUNNING and
a follow-up request is served warm.

Fixes agent-substrate#675
@omeryahud
Omer Yahud (omeryahud) force-pushed the parking-grpc-cancellation-fix branch from 66f3f30 to aa5324f Compare August 24, 2026 13:25
@haiyanmeng
haiyanmeng requested a review from yufan-su August 24, 2026 14:46
Aditya Shantanu (aditya-shantanu) added a commit to aditya-shantanu/substrate that referenced this pull request Aug 24, 2026
Adopted from agent-substrate#991 by @omeryahud to unblock CI velocity. Fixes the
TestRequestParking flake (agent-substrate#675): the park budget doubled as the
ResumeActor RPC deadline, cancelling restores mid-flight and
stranding RESUMING actors on live workers.

Co-authored-by: Omer Yahud <oyahud@nvidia.com>
@aditya-shantanu

Copy link
Copy Markdown
Collaborator

Heads-up: adopted this fix (unchanged, with Co-authored-by credit) into the consolidated flake-fix PR #1160 to get CI green faster. If maintainers prefer landing this PR individually, #1160 can drop the commit or be closed — whatever lands first wins.

Aditya Shantanu (aditya-shantanu) added a commit to aditya-shantanu/substrate that referenced this pull request Aug 24, 2026
SuspendActor returns before the suspend completes, and on the micro-VM
class the snapshot upload routinely outlives the 5s park budget under
CI contention — the router's budget-exhausted 503 is then correct
behavior, not a failure. Retry the request (bounded at 3 attempts):
each attempt parks anew and the completed suspend lets one resume the
actor. A stranded worker (agent-substrate#675's root cause) fails all attempts, so
the regression stays pinned.

This was the dominant historical failure mode of the agent-substrate#675 flake
(identical parking_test.go:113 503s in pre-PR runs 32305728993,
32397291519, 32487958077) — distinct from the mid-restore
cancellation that agent-substrate#991 fixes.
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.

E2E test flaky: TestRequestParking

3 participants