mirror of
https://github.com/vercel/workflow.git
synced 2026-09-14 19:59:43 +08:00
9177ba83d3
* [core] Enforce maxRetries for steps that time out A step that is hard-killed by the platform function timeout writes no step_failed/step_retrying, so `step.error` stays null and the error-based max-retries guards never fire. Each redelivery re-runs step_started (incrementing the attempt), so a timing-out step retried without bound instead of stopping at maxRetries. Enforce the retry ceiling BEFORE running the body, via a new `authoritativeAttempt` param on executeStep: - Inline (combined handler): count the step_started events already in the event log for the step (+1 for this attempt). The log is authoritative because the optimistic-start path synthesizes step.attempt = 1. - Background (queue-dispatched): the queue delivery count (metadata.attempt), which increments on the visibility-timeout redelivery a timed-out step produces. When the attempt exceeds maxRetries + 1 the step is failed without starting another attempt. Thrown-error exhaustion is unchanged — it still terminates via the post-body guard one attempt earlier, with the thrown error as cause. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * Apply suggestions from code review Co-authored-by: Peter Wielander <mittgfu@gmail.com> Signed-off-by: Peter Wielander <mittgfu@gmail.com> * [core] Verify step_started count before failing a backgrounded step `metadata.attempt` (queue delivery count) also advances for redeliveries that never run the step body (ThrottleError, TooEarlyError, other pre-body failures), so trusting it directly could fail a step as "exceeded max retries" before the body ever ran. Use the delivery count only as a fast gate: while it is at or under the ceiling the step can't be exhausted, so proceed without touching the log. Only once it crosses the ceiling, load the full event log and derive the authoritative attempt from the recorded step_started count (which only real attempts write) — excluding throttle/too-early redeliveries. The load also primes the replay's cachedEvents/eventsCursor. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * [core] Skip the event-log scan for brand-new inline steps Deriving an inline step's attempt number by scanning the cumulative event log for step_started events ran for every inline execution, which is O(n²) across a long sequential workflow. A lazy inline step is brand-new by construction (it only enters the batch with no step_created yet), so it has zero prior starts and is always attempt 1 — no scan needed. Reserve the scan for owned-recovery re-runs (this message re-executing a step it crashed/timed out on), which are uncommon and few per batch. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> --------- Signed-off-by: Peter Wielander <mittgfu@gmail.com> Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
102 B
102 B
@workflow/core
| @workflow/core |
|---|
| patch |
Enforce maxRetries for inline and backgrounded steps that time out