Skip to content

service-automation: two concurrent resumes of one run on two replicas can both advance it — the idempotency guard is per-process #14333

Description

@claude

Found while fixing #13617, filed rather than folded in — a residual that fix deliberately does
not close.

What

#13617 fixed the SEQUENTIAL failure: a replica resuming from a run-state snapshot it had gone
stale on. The resume path now reads the shared sys_automation_run row.

It does not close the CONCURRENT one. AutomationEngine.resumeInternal guards against a
duplicate resume with this.resuming, an in-process Set. Across replicas there is no such
guard: two decisions for the same run arriving at the same moment on two replicas can both read
the same fresh row, both pass their own idempotency check, both call forgetSuspendedRun, and
both traverse forward — running the downstream side effects twice.

Why this is separate from #13617

The reported repro there is strictly sequential (human approvals seconds apart, one decision at
a time), and its ~60ms timing is the resume rebuilding the node it had just left, not two
concurrent approvals. So the authoritative read is the whole fix for that card, and this is a
different failure with a different remedy.

Why it needs a decision, not just a patch

Closing it needs a compare-and-set on the run’s advance so a loser learns it lost, and the two
obvious places to put it both widen a contract:

  • an optimistic-version column on sys_automation_run, checked on the consume — a schema change;
  • a conditional delete on the SuspendedRunStore interface (delete only if still parked at node N), which every implementation then owes.

Either is a contract question rather than an implementation detail, which is why this is a card
and not a follow-up commit. Worth sizing against real exposure first: it needs two decisions on
one run inside one resume window, so a flow with a single approver per level is not obviously
reachable, while parallel/any-of approvers and automated approve calls are.

Generated by Claude Code


Generated by Claude Code

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

Type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions