workflow 5.0.0-beta.41 → 5.0.0-beta.42
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/docs/api-reference/workflow-errors/precondition-failed-error.mdx +2 -2
- package/docs/api-reference/workflow-runtime/world/analytics.mdx +4 -0
- package/docs/api-reference/workflow-runtime/world/storage.mdx +12 -1
- package/docs/configuration/runtime-tuning.mdx +17 -11
- package/package.json +11 -11
|
@@ -8,9 +8,9 @@ related:
|
|
|
8
8
|
- /docs/api-reference/workflow-errors/entity-conflict-error
|
|
9
9
|
---
|
|
10
10
|
|
|
11
|
-
`PreconditionFailedError` is thrown by world implementations when an event creation is rejected because the client's event-log snapshot is stale
|
|
11
|
+
`PreconditionFailedError` is thrown by world implementations when an event creation is rejected because the client's event-log snapshot is stale: the log already held more events than the position the creation named. It corresponds to HTTP 412 Precondition Failed semantics.
|
|
12
12
|
|
|
13
|
-
|
|
13
|
+
No world in this repository throws it. A stale replay does not need to be refused: its log is a prefix rather than a prefix with a hole in it, replay is deterministic on a prefix, and the write it makes next comes back carrying the events it was pushed past (see [Stale reads](/docs/configuration/runtime-tuning#stale-reads-and-why-nothing-has-to-be-rejected)). The error and the runtime's handling of it remain for a world that would rather refuse than report — one that allocates positions somewhere other than the commit, and so cannot report a gap reliably. Event creations that carry no position are never rejected with it.
|
|
14
14
|
|
|
15
15
|
A world rejects only on evidence and accepts the creation whenever it cannot decide, so this error always means the snapshot really was stale — but not receiving it does not prove the snapshot was current.
|
|
16
16
|
|
|
@@ -22,6 +22,10 @@ keywords:
|
|
|
22
22
|
|
|
23
23
|
`world.analytics` is an optional, read-only namespace for observability surfaces — dashboards, CLIs, and admin tools that list large numbers of runs without touching payload data.
|
|
24
24
|
|
|
25
|
+
For observability and inspection listings, prefer this namespace over
|
|
26
|
+
[`world.runs.list()`](/docs/api-reference/workflow-runtime/world/storage#runslist).
|
|
27
|
+
The storage API remains available for operational and payload-bearing reads.
|
|
28
|
+
|
|
25
29
|
It differs from [Storage](/docs/api-reference/workflow-runtime/world/storage) in two ways:
|
|
26
30
|
|
|
27
31
|
- **Metadata only.** Results never include run input/output, step data, or hook tokens. There is no `resolveData` option.
|
|
@@ -133,7 +133,10 @@ const result = await world.events.listByCorrelationId({ // [!code highlight]
|
|
|
133
133
|
|
|
134
134
|
## world.runs
|
|
135
135
|
|
|
136
|
-
Materialized from run events. Use it
|
|
136
|
+
Materialized from run events. Use it for canonical operational reads, including
|
|
137
|
+
reads that require workflow input or output data. For observability dashboards,
|
|
138
|
+
inspection tools, and historical listings, use
|
|
139
|
+
[`world.analytics.runs.list()`](/docs/api-reference/workflow-runtime/world/analytics#runslist).
|
|
137
140
|
|
|
138
141
|
### runs.get()
|
|
139
142
|
|
|
@@ -163,6 +166,14 @@ const result = await world.runs.list({ // [!code highlight]
|
|
|
163
166
|
|
|
164
167
|
**Returns:** `{ data: WorkflowRun[], cursor?: string }`
|
|
165
168
|
|
|
169
|
+
<Callout type="warn">
|
|
170
|
+
Observability and inspection usage of `world.runs.list()` is deprecated. Use
|
|
171
|
+
[`world.analytics.runs.list()`](/docs/api-reference/workflow-runtime/world/analytics#runslist)
|
|
172
|
+
for metadata-only, plan-aware queries backed by the observability pipeline.
|
|
173
|
+
`world.runs.list()` remains supported for operational and payload-bearing
|
|
174
|
+
reads.
|
|
175
|
+
</Callout>
|
|
176
|
+
|
|
166
177
|
### Cancelling Runs
|
|
167
178
|
|
|
168
179
|
To cancel a run, create a `run_cancelled` event via `world.events.create()` (see [world.events](#worldevents) above), or use the CLI or Web UI helpers.
|
|
@@ -71,17 +71,23 @@ For example, a workflow can run a 10-minute inline step even with `WORKFLOW_REPL
|
|
|
71
71
|
- Only applies to Worlds with atomic, immutable deployments (the Vercel World). A run whose pinned deployment cannot be reached at all fails immediately regardless of this value.
|
|
72
72
|
- Transient or unknown queue publishing failures use normal queue redelivery and do not consume this budget.
|
|
73
73
|
|
|
74
|
-
### `
|
|
74
|
+
### `WORKFLOW_RESILIENT_STEP_DISPATCH`
|
|
75
75
|
|
|
76
|
-
- Default:
|
|
77
|
-
-
|
|
78
|
-
-
|
|
79
|
-
-
|
|
80
|
-
-
|
|
81
|
-
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
76
|
+
- Default: disabled
|
|
77
|
+
- When a suspension hands newly created steps to the queue, the runtime publishes each step's execution message in parallel with its `step_created` event write instead of sequencing them, cutting a round trip per dispatched step. The message also carries the serialized step input (`stepInput`), so a transient `step_created` write failure (429 / 5xx / transport) still executes the step — the queue consumer idempotently re-ensures the event before running it, converging with the producer's write on the step's correlation ID. This mirrors resilient start (`runInput`) and the lazy hook resume (`hookInput`).
|
|
78
|
+
- It is off by default because the publish races the create's verdict, and a create can come back refused: as a duplicate this replay should stop pursuing, or as a [stale write](#stale-reads-and-why-nothing-has-to-be-rejected) on a World that refuses rather than reports. Either way the message carrying the payload is already out, so the consumer can materialize a step whose create was refused, and nothing orders the verdict before the consumer's redelivery re-ensure. The sequential path is the only one that gives the message a happens-after edge over it.
|
|
79
|
+
- Even when enabled, the runtime falls back to the sequential create-then-publish dispatch when the step input is too large to inline on the queue message, or when the run's queue transport cannot carry binary payloads (pre-CBOR spec versions).
|
|
80
|
+
- Producer-side recoveries are reported on the suspension span as `workflow.step.resilient_dispatch_recovered`; a consumer that materialized the event reports `workflow.step.resilient_dispatch_materialized`.
|
|
81
|
+
- Set `1` to enable it.
|
|
82
|
+
|
|
83
|
+
### Stale reads, and why nothing has to be rejected
|
|
84
|
+
|
|
85
|
+
- Not a variable: this is how a replay working from an out-of-date event log stays correct, and why no World needs a precondition guard to make it so.
|
|
86
|
+
- Three properties do it together. A reader's log is always a **prefix** of the run's log, never a prefix with a hole in it — positions are allocated by the World at commit, so nothing lands behind a position a reader has already passed. Replay is **deterministic on a prefix**: the same prefix always yields the same decisions, so a shorter log does not mean a different run, only a run that has not caught up. And every write **reports what it missed**: a creation names the position it replayed from (`eventCount`), and the World returns the events occupying the positions it was pushed past. The replay merges those and continues, correcting itself on the write rather than on a read.
|
|
87
|
+
- So a stale replay costs a merge, not a rejection. None of the shipped Worlds refuses a write for being stale.
|
|
88
|
+
- A World *may* refuse instead, with 412 ([`PreconditionFailedError`](/docs/api-reference/workflow-errors/precondition-failed-error)) — appropriate when it allocates positions somewhere other than the commit and cannot report a gap reliably. The runtime handles that: it restarts the replay in the same invocation from a corrected event log, and falls back to a re-invocation with a fresh replay once the restart budget is spent. The rejected write is never retried as-is, because a replay working from a corrected log derives different events.
|
|
89
|
+
- A World that does refuse should only ever do so on evidence, and accept the write in every other case. A rejection then always means the position really was stale, while the absence of one proves nothing about currency.
|
|
90
|
+
- Two runtime behaviors follow from the properties above rather than from any fence. The per-step event-log delta optimization (consuming the delta returned by a step's terminal write instead of issuing an extra `events.list` per step) stays active while the run has an open hook: a `hook_received` missed by the delta window is observed one iteration later, and the next write brings it back. And while a hook is open, inline steps take the await-then-run path even when optimistic inline start is enabled — several invocations race for one step's claim there, and awaiting it means the body runs only for the writer that won.
|
|
85
91
|
|
|
86
92
|
### `WORKFLOW_SLOT_GAP_CHECK`
|
|
87
93
|
|
|
@@ -94,7 +100,7 @@ For example, a workflow can run a 10-minute inline step even with `WORKFLOW_REPL
|
|
|
94
100
|
### `WORKFLOW_PRECONDITION_MAX_INPROCESS_RESTARTS`
|
|
95
101
|
|
|
96
102
|
- Default: `3`
|
|
97
|
-
- How many times a single invocation restarts its replay in-process after
|
|
103
|
+
- How many times a single invocation restarts its replay in-process after an event creation is [rejected as stale](#stale-reads-and-why-nothing-has-to-be-rejected) before it falls back to a re-invocation. No shipped World rejects one, so this budget is reserved for a World that chooses to.
|
|
98
104
|
- A restart reloads the event log and rebuilds the workflow from scratch, so it costs a replay but no queue round trip. A World may attach the missing events to its rejection, in which case the first restart needs no event-log request at all.
|
|
99
105
|
|
|
100
106
|
### `WORKFLOW_PRECONDITION_MAX_REINVOCATIONS`
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "workflow",
|
|
3
|
-
"version": "5.0.0-beta.
|
|
3
|
+
"version": "5.0.0-beta.42",
|
|
4
4
|
"description": "Workflow SDK - Build durable, resilient, and observable workflows",
|
|
5
5
|
"main": "dist/typescript-plugin.cjs",
|
|
6
6
|
"type": "module",
|
|
@@ -59,18 +59,18 @@
|
|
|
59
59
|
},
|
|
60
60
|
"dependencies": {
|
|
61
61
|
"ms": "2.1.3",
|
|
62
|
-
"@workflow/astro": "5.0.0-beta.
|
|
63
|
-
"@workflow/cli": "5.0.0-beta.
|
|
64
|
-
"@workflow/core": "5.0.0-beta.
|
|
65
|
-
"@workflow/errors": "5.0.0-beta.
|
|
62
|
+
"@workflow/astro": "5.0.0-beta.42",
|
|
63
|
+
"@workflow/cli": "5.0.0-beta.42",
|
|
64
|
+
"@workflow/core": "5.0.0-beta.42",
|
|
65
|
+
"@workflow/errors": "5.0.0-beta.17",
|
|
66
66
|
"@workflow/typescript-plugin": "5.0.0-beta.5",
|
|
67
67
|
"@workflow/utils": "5.0.0-beta.8",
|
|
68
|
-
"@workflow/next": "5.0.0-beta.
|
|
69
|
-
"@workflow/nest": "5.0.0-beta.
|
|
70
|
-
"@workflow/nitro": "5.0.0-beta.
|
|
71
|
-
"@workflow/nuxt": "5.0.0-beta.
|
|
72
|
-
"@workflow/sveltekit": "5.0.0-beta.
|
|
73
|
-
"@workflow/rollup": "5.0.0-beta.
|
|
68
|
+
"@workflow/next": "5.0.0-beta.42",
|
|
69
|
+
"@workflow/nest": "5.0.0-beta.42",
|
|
70
|
+
"@workflow/nitro": "5.0.0-beta.42",
|
|
71
|
+
"@workflow/nuxt": "5.0.0-beta.42",
|
|
72
|
+
"@workflow/sveltekit": "5.0.0-beta.42",
|
|
73
|
+
"@workflow/rollup": "5.0.0-beta.42"
|
|
74
74
|
},
|
|
75
75
|
"devDependencies": {
|
|
76
76
|
"@types/ms": "2.1.0",
|