mirror of
https://github.com/vercel/workflow.git
synced 2026-09-14 19:59:43 +08:00
03455a2979
* Carry immutable run identity on step-dispatch messages; drop the blocking runs.get from the consumer prologue Closes #3456. Every queued step execution paid a runs.get round trip before its step_started claim — one RTT per branch on the TTLS-critical path, and under a 256-branch fan-out burst the read amplification drove that read to p90 ~5.1s (durabench parallel sweeps), smearing branch starts. The dispatch sites (node dispatch loop, delayed retries, the suspension handler's resilient publish, and the quickjs engine's queueStepMessage) now stamp WorkflowInvokePayload.runContext with the fields the consumer actually needs — deploymentId, specVersion, startedAt, rootRunId — all immutable for the life of a run and known from the run row the producer already holds. A consumer that receives it skips the run fetch: the run-status early exit is enforced by the step_started claim itself (RunExpired → gone, terminal step → skipped), guardDeployment takes the carried identity, and only the fan-out's LAST completer fetches the full run row, lazily, for its inline replay — once per fan-out instead of once per branch. The deployment-mismatch re-route now also preserves stepInput/runContext on the re-enqueued payload. Messages without runContext (older producers) keep the legacy prologue; messages are deployment-pinned, so mixed handling within one run cannot occur. * Address review: terminal-only lazy status gate, terminal-run start fence in local worlds, prologue telemetry, last-completer coverage - The last completer's lazy runs.get result is now gated on isTerminalWorkflowRunStatus (with a debug log): a stale 'pending' read — a run with completed steps has necessarily started — no longer silently abandons the fan-out's continuation; it falls through to the inline replay, whose next entity write is fenced server-side if the run truly ended meanwhile. - world-local / world-postgres now reject step_started on terminal runs even when the step row still reads 'running' (a redelivered start a previous delivery claimed): starting work on a finished run is never valid, and previously the body re-ran with its outcome unconsumable. In-flight steps still write their terminal events unchanged. This closes the adapter gap behind the fetch-free prologue's reliance on the step_started claim as the run-liveness check, and the prologue comment now states the contract precisely. - workflow.step.dispatch_prologue span attribute ('run_context' | 'runs_get') makes fetch-free adoption and the saved round trip observable during version-skew windows. - Restated why the eager redelivery re-ensure survives on the fetch-free path (no run fetch to overlap; still cheaper than the in-band recovery's failed-start round trip). - New two-phase fan-out coverage: a real replay emits the queued step message (asserting the stamped runContext), then its redelivery runs as the LAST completer — zero reads before the step, exactly one lazy runs.get, run completed; plus the stale-'pending' fall-through and the genuinely-terminal skip.