mirror of
https://github.com/vercel/workflow.git
synced 2026-09-14 19:59:43 +08:00
e552dc47f9
The previous attempt (unconditional orphaned-step recovery scan,
reverted in d441126fe) re-queued every pending step_created on every
invocation. That violated the single-owner-per-step invariant: a
non-owner tick could re-dispatch a step another tick was already
running, producing a duplicate step_started and a
CORRUPTED_EVENT_LOG ("Unconsumed event in event log:
eventType=step_started"). 2/40 runs hit this in stress.
Safer approach: when handleSuspension abandons on a stale snapshot, it
returns the steps it ALREADY wrote a fenced step_created for (the ones
in createdStepCorrelationIds) as pendingSteps. Those writes succeeded
against a matching fence inside the atomic transaction, so they're
canonical and owned by exactly this tick. The runtime's
staleSnapshot branch dispatches just those owned steps (with
idempotencyKey: correlationId) before re-enqueuing, so:
- no orphaned step_created (the step that this tick created always gets
an owner to dispatch it), and
- no double-dispatch (only the single owning tick queues each step;
other ticks that abandon before writing the step_created never claim
ownership of it).
This pairs ownership with dispatch instead of blindly recovering, which
is what made the unconditional scan unsafe.
Tests: 1018 core tests pass.
@workflow/core
Core runtime package for Workflow SDK.