Repository navigation
Commit 97dccc9
[world] Add optional
* feat(world-postgres): add optional executor invocation for hooks
Co-Authored-By: shalabhc <shalabh.chaturvedi@vercel.com>
* perf(world-postgres): notify invocation input and result waiters
Co-Authored-By: shalabhc <shalabh.chaturvedi@vercel.com>
* fix(world-postgres): verify serialized executor deliveries
Co-Authored-By: shalabhc <shalabh.chaturvedi@vercel.com>
* refactor(world): return invocation results and harden Postgres delivery
Co-Authored-By: shalabhc <shalabh.chaturvedi@vercel.com>
* chore: reconcile main before signed merge
Co-Authored-By: shalabhc <shalabh.chaturvedi@vercel.com>
* fix(world-postgres): replay drained inputs and return invocation errors
Co-Authored-By: shalabhc <shalabh.chaturvedi@vercel.com>
* Fix: Transient/infra errors thrown during hook-resume invocation are captured and stored as a permanent, non-retryable outcome instead of being retried, which can leave a `hook_received` write uncommitted and suspend the workflow forever.
This commit fixes the issue reported at packages/world-postgres/src/queue.ts:322
## The bug
Commit `f944ec9` reworked the executor `invoke` path in `packages/world-postgres/src/queue.ts`:
```ts
const outcome = await captureInvocationOutcome(() => handler(message, metadata));
await invocations.respondOutcome(input.runId, input.requestId, outcome);
return; // delivery acked (HTTP 200), no retry
```
`captureInvocationOutcome` (`packages/errors/src/invocation.ts`) caught **every** error unconditionally and returned `{ ok: false, error }`. `respondOutcome` then persisted it (`result_version = 1`, `responded_at = now()`) and the handler returned normally, so Graphile acked the job — **no redelivery**.
For an `invoke` message the wrapped handler routes only to `handleInvocation` (`packages/core/src/runtime/invocations.ts`), which performs pure world I/O: `world.hooks.get`, `world.runs.get`, and the durable `world.events.create` that commits the `hook_received` event resuming the workflow. `f944ec9` also changed `handleInvocation` to **throw** on every failure (it previously returned `{status:'rejected'}` for hook-gone).
### Concrete failure mode
1. `resumeHook` invokes with a stable `requestId`.
2. The executor runs `handleInvocation`; `world.events.create` fails with a **transient** error (DB connection reset, deadlock, `40001` serialization failure — surfaced as a raw `DrizzleQueryError`/pg error, not a `WorkflowError`).
3. `captureInvocationOutcome` catches it → `{ ok:false, error }`.
4. `respondOutcome` stores the error permanently (`responded_at` set) and commits.
5. Handler returns → HTTP 200 → Graphile acks → **no retry**.
6. The `hook_received` event was never committed, so the run stays suspended.
7. `world.invoke()`'s waiter reads the responded row and (via `unwrapInvocationOutcome`) throws the rehydrated error to the caller immediately.
**Before** `f944ec9`, a thrown error propagated out of the queue handler into `createQueueHandler`'s retry logic (Graphile redelivery), so a transient blip was retried transparently until the resume committed. The new code converts that into a permanent stored failure.
The intended contract (per `packages/world-postgres/test/invoke.test.ts` — *"returns persisted $name outcomes instead of timing out"*, which only exercises **known** `WorkflowWorldError`/`HookNotFoundError`/`WorkflowRunNotFoundError`/`RunExpiredError`/`EntityConflictError`) was to persist **terminal/deterministic** business errors so callers get the right error class instead of a 30s timeout — not to persist transient/unknown failures.
## The fix
Distinguish terminal errors (capture + store) from transient/unknown errors (re-throw so the delivery layer retries):
* Added `isTerminalInvocationError` in `packages/errors/src/invocation.ts`. An error is terminal only when it is a class the `@workflow/errors` package owns (checked by `name` against the module registry, matching the existing `deserializeWorkflowError` approach) **and** it does not look transient — i.e. it has no `retryAfter` and no `status` of `>= 500` / `408` / `425` / `429`. Unknown/infra failures (raw `Error`, `DrizzleQueryError`, thrown non-Errors) and transient Workflow errors are therefore **not** terminal.
* Gave `captureInvocationOutcome` an optional `shouldCapture` predicate (defaulting to capture-all, preserving its generic transport contract and existing unit tests). When it returns `false`, the error is re-thrown.
* Both `invoke`-path call sites in `packages/world-postgres/src/queue.ts` now pass `isTerminalInvocationError`, so transient failures propagate to `createQueueHandler`'s Graphile retry (as before `f944ec9`) while terminal errors are still persisted (keeping the `invoke.test.ts` `deliveries === 1` behavior).
The known terminal cases thrown by `handleInvocation` remain captured: `INVALID_INPUT` (400), `HookNotFoundError`, `INVOCATION_DATA_EXPIRED` (410), `WorkflowRunNotFoundError`, `RunExpiredError`, and the deterministic `EntityConflictError` from correlated-event dedup. Added unit tests covering both the terminal and transient classifications. Type-checking passes for `@workflow/errors` and `@workflow/world-postgres`, and the `invocation.test.ts` suite passes.
Co-authored-by: Vercel <vercel[bot]@users.noreply.github.com>
Co-authored-by: VaguelySerious <mittgfu@gmail.com>
* chore: reconcile main and invocation retry coverage
Co-Authored-By: shalabhc <shalabh.chaturvedi@vercel.com>
* style(errors): format invocation error fixture
Co-Authored-By: shalabhc <shalabh.chaturvedi@vercel.com>
* docs: consolidate invocation changeset and trim Vercel notes
Co-Authored-By: shalabhc <shalabh.chaturvedi@vercel.com>
* docs(world): define invoke single-runner guarantee
Co-Authored-By: shalabhc <shalabh.chaturvedi@vercel.com>
* docs: apply reviewed invoke documentation wording
Co-Authored-By: shalabhc <shalabh.chaturvedi@vercel.com>
---------
Co-authored-by: vercel[bot] <35613825+vercel[bot]@users.noreply.github.com>
Co-authored-by: Vercel <vercel[bot]@users.noreply.github.com>
Co-authored-by: VaguelySerious <mittgfu@gmail.com>invoke method (hook payloads only for now) (#4168)1 parent f1f5b7d commit 97dccc9
45 files changed
Lines changed: 4558 additions & 45 deletions
File tree
- .changeset
- docs/content/worlds/v5
- packages
- core
- src
- runtime
- errors
- src
- world-local
- src
- world-postgres
- src
- drizzle
- migrations
- meta
- test
- world-sim/src
- world-vercel/src
- world
- src
Some content is hidden
Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
239 | 239 | | |
240 | 240 | | |
241 | 241 | | |
| 242 | + | |
| 243 | + | |
| 244 | + | |
242 | 245 | | |
243 | 246 | | |
244 | | - | |
| 247 | + | |
245 | 248 | | |
246 | 249 | | |
247 | 250 | | |
248 | 251 | | |
| 252 | + | |
| 253 | + | |
| 254 | + | |
| 255 | + | |
| 256 | + | |
| 257 | + | |
| 258 | + | |
| 259 | + | |
| 260 | + | |
| 261 | + | |
| 262 | + | |
| 263 | + | |
| 264 | + | |
| 265 | + | |
| 266 | + | |
| 267 | + | |
| 268 | + | |
| 269 | + | |
| 270 | + | |
| 271 | + | |
| 272 | + | |
| 273 | + | |
| 274 | + | |
| 275 | + | |
| 276 | + | |
| 277 | + | |
| 278 | + | |
| 279 | + | |
| 280 | + | |
| 281 | + | |
| 282 | + | |
| 283 | + | |
| 284 | + | |
| 285 | + | |
| 286 | + | |
| 287 | + | |
249 | 288 | | |
250 | 289 | | |
251 | 290 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
80 | 80 | | |
81 | 81 | | |
82 | 82 | | |
| 83 | + | |
| 84 | + | |
| 85 | + | |
| 86 | + | |
| 87 | + | |
| 88 | + | |
| 89 | + | |
| 90 | + | |
| 91 | + | |
| 92 | + | |
| 93 | + | |
| 94 | + | |
| 95 | + | |
| 96 | + | |
| 97 | + | |
| 98 | + | |
| 99 | + | |
| 100 | + | |
| 101 | + | |
| 102 | + | |
| 103 | + | |
| 104 | + | |
| 105 | + | |
| 106 | + | |
| 107 | + | |
| 108 | + | |
| 109 | + | |
| 110 | + | |
| 111 | + | |
| 112 | + | |
| 113 | + | |
| 114 | + | |
| 115 | + | |
| 116 | + | |
| 117 | + | |
| 118 | + | |
| 119 | + | |
| 120 | + | |
| 121 | + | |
| 122 | + | |
| 123 | + | |
| 124 | + | |
| 125 | + | |
| 126 | + | |
| 127 | + | |
| 128 | + | |
| 129 | + | |
| 130 | + | |
| 131 | + | |
| 132 | + | |
| 133 | + | |
| 134 | + | |
| 135 | + | |
| 136 | + | |
83 | 137 | | |
84 | 138 | | |
85 | 139 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
8 | 8 | | |
9 | 9 | | |
10 | 10 | | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
| 15 | + | |
| 16 | + | |
| 17 | + | |
| 18 | + | |
| 19 | + | |
| 20 | + | |
| 21 | + | |
| 22 | + | |
| 23 | + | |
| 24 | + | |
| 25 | + | |
| 26 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
91 | 91 | | |
92 | 92 | | |
93 | 93 | | |
| 94 | + | |
94 | 95 | | |
95 | 96 | | |
96 | 97 | | |
| |||
120 | 121 | | |
121 | 122 | | |
122 | 123 | | |
123 | | - | |
| 124 | + | |
124 | 125 | | |
125 | 126 | | |
126 | 127 | | |
| |||
584 | 585 | | |
585 | 586 | | |
586 | 587 | | |
| 588 | + | |
587 | 589 | | |
588 | 590 | | |
589 | 591 | | |
| |||
594 | 596 | | |
595 | 597 | | |
596 | 598 | | |
| 599 | + | |
597 | 600 | | |
598 | 601 | | |
599 | 602 | | |
| |||
607 | 610 | | |
608 | 611 | | |
609 | 612 | | |
610 | | - | |
| 613 | + | |
| 614 | + | |
| 615 | + | |
| 616 | + | |
| 617 | + | |
611 | 618 | | |
612 | 619 | | |
613 | 620 | | |
| |||
715 | 722 | | |
716 | 723 | | |
717 | 724 | | |
718 | | - | |
| 725 | + | |
719 | 726 | | |
720 | 727 | | |
721 | | - | |
| 728 | + | |
722 | 729 | | |
723 | 730 | | |
724 | 731 | | |
| |||
1965 | 1972 | | |
1966 | 1973 | | |
1967 | 1974 | | |
| 1975 | + | |
| 1976 | + | |
| 1977 | + | |
| 1978 | + | |
| 1979 | + | |
| 1980 | + | |
| 1981 | + | |
| 1982 | + | |
| 1983 | + | |
| 1984 | + | |
| 1985 | + | |
| 1986 | + | |
| 1987 | + | |
| 1988 | + | |
| 1989 | + | |
| 1990 | + | |
| 1991 | + | |
| 1992 | + | |
1968 | 1993 | | |
1969 | 1994 | | |
1970 | 1995 | | |
| |||
2897 | 2922 | | |
2898 | 2923 | | |
2899 | 2924 | | |
| 2925 | + | |
2900 | 2926 | | |
2901 | 2927 | | |
2902 | 2928 | | |
| |||
3654 | 3680 | | |
3655 | 3681 | | |
3656 | 3682 | | |
| 3683 | + | |
3657 | 3684 | | |
3658 | 3685 | | |
3659 | 3686 | | |
| |||
4334 | 4361 | | |
4335 | 4362 | | |
4336 | 4363 | | |
| 4364 | + | |
| 4365 | + | |
| 4366 | + | |
| 4367 | + | |
| 4368 | + | |
| 4369 | + | |
| 4370 | + | |
| 4371 | + | |
| 4372 | + | |
4337 | 4373 | | |
4338 | 4374 | | |
4339 | 4375 | | |
| |||
5229 | 5265 | | |
5230 | 5266 | | |
5231 | 5267 | | |
5232 | | - | |
| 5268 | + | |
5233 | 5269 | | |
5234 | 5270 | | |
5235 | 5271 | | |
| |||
0 commit comments