Skip to content

Refresh the actor when AssignWorkerStep's write hits a version conflict. - #535

Merged
Zoe Zhao (zoez7) merged 1 commit into
agent-substrate:mainfrom
HavenXia:assign-retry-fix
Jul 28, 2026
Merged

Refresh the actor when AssignWorkerStep's write hits a version conflict.#535
Zoe Zhao (zoez7) merged 1 commit into
agent-substrate:mainfrom
HavenXia:assign-retry-fix

Conversation

@HavenXia

@HavenXia Haven Xia (HavenXia) commented Jul 25, 2026

Copy link
Copy Markdown
Collaborator

Refresh the actor when AssignWorkerStep's write hits a version conflict so that the workflow won't kept retry with a stale version actor and never succeed.

AI assisted on testing.

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

Comment thread cmd/ateapi/internal/controlapi/workflow_resume.go
// retry applies on top of the concurrent write instead of replaying the same
// stale version until the backoff is exhausted; an actor that left the
// resumable states aborts the retry instead of being resurrected to RESUMING.
func TestAssignWorkerStep_ConflictRefreshesActor(t *testing.T) {

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.

For readability of tests, can we use table-driven tests, and run them in parallel if possible?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

updated

updatedActor, err := s.store.UpdateActor(ctx, state.Actor, state.Actor.GetMetadata().GetVersion())
if err != nil {
return err
if !errors.Is(err, store.ErrPersistenceRetry) {

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.

This isn't necessarily something we need to solve in this PR, but I wonder if a ErrVersionConflct is in order for this scenario. Refetching the actor on EVERY error here doesn't seem quite right to me

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

We are not refetching the actor on every error, current ErrPersistenceRetry means right "version mismatch", code.

…ct, otherwise the retry

keeps using the stale version and never succeeds.
switch fresh.GetStatus() {
case ateapipb.Actor_STATUS_SUSPENDED, ateapipb.Actor_STATUS_PAUSED:
slog.InfoContext(ctx, "Retrying assignment due to actor version conflict", slog.String("actor", input.Atespace+"/"+input.ActorName))
state.Actor = fresh

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.

Just a note - the correctness here relies on the fact that no other step before AssignWorkStep modifies state.Actor.

@zoez7
Zoe Zhao (zoez7) merged commit bb9d9a1 into agent-substrate:main Jul 28, 2026
11 checks passed
@EItanya

Copy link
Copy Markdown
Collaborator

I’m ok that this merged, but honestly I don’t think this answers my question #535 (comment)

It checks a negative case on an error because that function happens to only use one other one, which is making too many assumptions about the behavior of the underlying function. The errors should describe the behavior.

This comment is also fairly scary #535 (comment)

@HavenXia

Copy link
Copy Markdown
Collaborator Author

It checks a negative case on an error because that function happens to only use one other one, which is making too many assumptions about the behavior of the underlying function. The errors should describe the behavior.

Understood, to avoid we fold other errors into ErrPersistenceRetry I will rename this specfic path error to reflect the version mismatch ErrVersionConflict.

This comment is also fairly scary #535 (comment)

Fair - it's only safe because today no step before AssignWorker touches state.Actor and the engine retries only the failing step. Nothing enforces that. We actually have #554 created to track it, adding to #554 for vis.

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.

4 participants