Skip to content

Fix Postgres step lifecycle event ordering#2714

Draft
NathanColosimo wants to merge 2 commits into
mainfrom
nathanc/2331-step-update-pg-transaction
Draft

Fix Postgres step lifecycle event ordering#2714
NathanColosimo wants to merge 2 commits into
mainfrom
nathanc/2331-step-update-pg-transaction

Conversation

@NathanColosimo

Copy link
Copy Markdown
Contributor

Summary

Fixes #2331 by writing Postgres step lifecycle entity updates and their event-log entries in the same transaction, so the step row lock serializes concurrent lifecycle writers until the log entry is durable.

Also allocates Postgres-backed event ids inside the protected lifecycle path and re-enables the previously skipped Postgres e2e regression coverage.

Testing

  • node_modules/.bin/tsc --noEmit -p packages/world-postgres/tsconfig.json
  • node_modules/.bin/biome check --diagnostic-level=error packages/world-postgres/src/storage.ts packages/world-postgres/test/storage.test.ts packages/core/e2e/e2e.test.ts packages/world-postgres/src/drizzle/migrations/0015_add_workflow_events_order_sequence.sql packages/world-postgres/src/drizzle/migrations/meta/_journal.json .changeset/step-lifecycle-pg-transaction.md
  • ../../node_modules/.bin/vitest run test/storage.test.ts src/reenqueue.test.ts src/util.test.ts src/queue.test.ts
  • pnpm --filter @workflow/world-postgres build

@vercel

vercel Bot commented Jun 30, 2026

Copy link
Copy Markdown
Contributor

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
example-nextjs-workflow-turbopack Ready Ready Preview, Comment Jun 30, 2026 11:39pm
example-nextjs-workflow-webpack Ready Ready Preview, Comment Jun 30, 2026 11:39pm
example-workflow Ready Ready Preview, Comment Jun 30, 2026 11:39pm
workbench-astro-workflow Ready Ready Preview, Comment Jun 30, 2026 11:39pm
workbench-express-workflow Ready Ready Preview, Comment Jun 30, 2026 11:39pm
workbench-fastify-workflow Ready Ready Preview, Comment Jun 30, 2026 11:39pm
workbench-hono-workflow Ready Ready Preview, Comment Jun 30, 2026 11:39pm
workbench-nitro-workflow Ready Ready Preview, Comment Jun 30, 2026 11:39pm
workbench-nuxt-workflow Ready Ready Preview, Comment Jun 30, 2026 11:39pm
workbench-sveltekit-workflow Ready Ready Preview, Comment Jun 30, 2026 11:39pm
workbench-tanstack-start-workflow Ready Ready Preview, Comment Jun 30, 2026 11:39pm
workbench-vite-workflow Ready Ready Preview, Comment Jun 30, 2026 11:39pm
workflow-swc-playground Ready Ready Preview, Comment Jun 30, 2026 11:39pm
workflow-tarballs Ready Ready Preview, Comment Jun 30, 2026 11:39pm
workflow-web Ready Ready Preview, Comment Jun 30, 2026 11:39pm
1 Skipped Deployment
Project Deployment Actions Updated (UTC)
workflow-docs Skipped Skipped Jun 30, 2026 11:39pm

@changeset-bot

changeset-bot Bot commented Jun 30, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: 766e059

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 1 package
Name Type
@workflow/world-postgres Patch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@github-actions

github-actions Bot commented Jun 30, 2026

Copy link
Copy Markdown
Contributor

🧪 E2E Test Results

Some tests failed

Summary

Passed Failed Skipped Total
❌ ▲ Vercel Production 1123 319 230 1672
✅ 💻 Local Development 1605 0 219 1824
✅ 📦 Local Production 1605 0 219 1824
✅ 🐘 Local Postgres 1605 0 219 1824
✅ 🪟 Windows 152 0 0 152
❌ 📋 Other 857 30 177 1064
Total 6947 349 1064 8360

❌ Failed Tests

▲ Vercel Production (319 failed)

astro (22 failed):

  • AbortController abortListenerWorkflow: signal.addEventListener fires on the deserialized step signal
  • AbortController abortThrowIfAbortedMidFlightWorkflow: throwIfAborted in a polling loop bails when abort fires
  • AbortController abortDeterministicBranchFromStepWorkflow: branches stay consistent when abort comes from a step
  • AbortController abortHookOrderingWorkflow [listener-first-abort-first]: addEventListener → hook.then → abort() → resumeHook
  • AbortController abortHookOrderingWorkflow [listener-first-hook-first]: addEventListener → hook.then → resumeHook → abort()
  • AbortController abortHookOrderingWorkflow [hook-first-abort-first]: hook.then → addEventListener → abort() → resumeHook
  • AbortController abortHookOrderingWorkflow [hook-first-hook-first]: hook.then → addEventListener → resumeHook → abort()
  • importMetaUrlWorkflow - import.meta.url is available in step bundles | wrun_01KWDG0BAHB9V2GHMMQB34S96G | 🔍 observability
  • metadataFromHelperWorkflow - getWorkflowMetadata/getStepMetadata work from module-level helper (#1577) | wrun_01KWDG11H5ZEAD0ZA0NJMJHVEA | 🔍 observability
  • resilient start: addTenWorkflow completes when run_created returns 500 | wrun_01KWDG13VEH2WVRBX950R9MR39 | 🔍 observability
  • getterStepWorkflow - getter functions with "use step" directive | wrun_01KWDG16RVV5X2DZ6FKF4D2M2X | 🔍 observability
  • distributedAbortController - manual abort triggers signal | wrun_01KWDG0KW05EDD9WWC600EEP0J | 🔍 observability
  • distributedAbortController - TTL expiration triggers signal | wrun_01KWDG0JWC50WP936PFAWDH4PX | 🔍 observability
  • distributedAbortController - reconnect to existing controller | wrun_01KWDG0S9VK50K5NNJ3FXP3VYG | 🔍 observability
  • experimental_setAttributes start: initial attributes are seeded on run creation
  • experimental_setAttributes start: reserved-prefix initial attributes are seeded with allowReservedAttributes
  • experimental_setAttributes experimentalSetAttributesWorkflow: workflow-body calls append native attr_set events and merge correctly
  • experimental_setAttributes experimentalSetAttributesInsideStepWorkflow: step-body calls append attributed native events
  • experimental_setAttributes fire-and-forget: void experimental_setAttributes lands without awaiting
  • experimental_setAttributes Promise.all of disjoint-key writes: every key lands
  • experimental_setAttributes workflow throws after awaited setAttributes: attribute still persists on the failed run
  • experimental_setAttributes validation DX: invalid writes throw catchable FatalErrors naming rule and limit

example (22 failed):

  • AbortController abortListenerWorkflow: signal.addEventListener fires on the deserialized step signal
  • AbortController abortThrowIfAbortedMidFlightWorkflow: throwIfAborted in a polling loop bails when abort fires
  • AbortController abortDeterministicBranchFromStepWorkflow: branches stay consistent when abort comes from a step
  • AbortController abortHookOrderingWorkflow [listener-first-abort-first]: addEventListener → hook.then → abort() → resumeHook
  • AbortController abortHookOrderingWorkflow [listener-first-hook-first]: addEventListener → hook.then → resumeHook → abort()
  • AbortController abortHookOrderingWorkflow [hook-first-abort-first]: hook.then → addEventListener → abort() → resumeHook
  • AbortController abortHookOrderingWorkflow [hook-first-hook-first]: hook.then → addEventListener → resumeHook → abort()
  • importMetaUrlWorkflow - import.meta.url is available in step bundles | wrun_01KWDG0BAHB9V2GHMMQB34S96G | 🔍 observability
  • metadataFromHelperWorkflow - getWorkflowMetadata/getStepMetadata work from module-level helper (#1577) | wrun_01KWDG11H5ZEAD0ZA0NJMJHVEA | 🔍 observability
  • resilient start: addTenWorkflow completes when run_created returns 500 | wrun_01KWDG13VEH2WVRBX950R9MR39 | 🔍 observability
  • getterStepWorkflow - getter functions with "use step" directive | wrun_01KWDG16RVV5X2DZ6FKF4D2M2X | 🔍 observability
  • distributedAbortController - manual abort triggers signal | wrun_01KWDG0KW05EDD9WWC600EEP0J | 🔍 observability
  • distributedAbortController - TTL expiration triggers signal | wrun_01KWDG0JWC50WP936PFAWDH4PX | 🔍 observability
  • distributedAbortController - reconnect to existing controller | wrun_01KWDG0S9VK50K5NNJ3FXP3VYG | 🔍 observability
  • experimental_setAttributes start: initial attributes are seeded on run creation
  • experimental_setAttributes start: reserved-prefix initial attributes are seeded with allowReservedAttributes
  • experimental_setAttributes experimentalSetAttributesWorkflow: workflow-body calls append native attr_set events and merge correctly
  • experimental_setAttributes experimentalSetAttributesInsideStepWorkflow: step-body calls append attributed native events
  • experimental_setAttributes fire-and-forget: void experimental_setAttributes lands without awaiting
  • experimental_setAttributes Promise.all of disjoint-key writes: every key lands
  • experimental_setAttributes workflow throws after awaited setAttributes: attribute still persists on the failed run
  • experimental_setAttributes validation DX: invalid writes throw catchable FatalErrors naming rule and limit

express (17 failed):

  • AbortController abortHookOrderingWorkflow [hook-first-abort-first]: hook.then → addEventListener → abort() → resumeHook
  • AbortController abortHookOrderingWorkflow [hook-first-hook-first]: hook.then → addEventListener → resumeHook → abort()
  • importMetaUrlWorkflow - import.meta.url is available in step bundles | wrun_01KWDG0BAHB9V2GHMMQB34S96G | 🔍 observability
  • metadataFromHelperWorkflow - getWorkflowMetadata/getStepMetadata work from module-level helper (#1577) | wrun_01KWDG11H5ZEAD0ZA0NJMJHVEA | 🔍 observability
  • resilient start: addTenWorkflow completes when run_created returns 500 | wrun_01KWDG13VEH2WVRBX950R9MR39 | 🔍 observability
  • getterStepWorkflow - getter functions with "use step" directive | wrun_01KWDG16RVV5X2DZ6FKF4D2M2X | 🔍 observability
  • distributedAbortController - manual abort triggers signal | wrun_01KWDG0KW05EDD9WWC600EEP0J | 🔍 observability
  • distributedAbortController - TTL expiration triggers signal | wrun_01KWDG0JWC50WP936PFAWDH4PX | 🔍 observability
  • distributedAbortController - reconnect to existing controller | wrun_01KWDG0S9VK50K5NNJ3FXP3VYG | 🔍 observability
  • experimental_setAttributes start: initial attributes are seeded on run creation
  • experimental_setAttributes start: reserved-prefix initial attributes are seeded with allowReservedAttributes
  • experimental_setAttributes experimentalSetAttributesWorkflow: workflow-body calls append native attr_set events and merge correctly
  • experimental_setAttributes experimentalSetAttributesInsideStepWorkflow: step-body calls append attributed native events
  • experimental_setAttributes fire-and-forget: void experimental_setAttributes lands without awaiting
  • experimental_setAttributes Promise.all of disjoint-key writes: every key lands
  • experimental_setAttributes workflow throws after awaited setAttributes: attribute still persists on the failed run
  • experimental_setAttributes validation DX: invalid writes throw catchable FatalErrors naming rule and limit

fastify (36 failed):

  • AbortController abortTimeoutWorkflow: timeout cancels long-running step
  • AbortController abortFromStepWorkflow: step abort cancels an in-flight sibling step
  • AbortController abortAlreadyAbortedWorkflow: pre-aborted signal seen by step
  • AbortController abortReasonWorkflow: abort reason preserved across boundaries
  • AbortController abortAfterCompletionWorkflow: abort after step completes is a no-op
  • AbortController abortViaHookWorkflow: external hook triggers abort on in-flight step
  • AbortController abortExternalSignalWorkflow: signal passed as workflow input
  • AbortController abortExternalSignalInFlightWorkflow: external abort fires mid-flight, propagates to nested steps
  • AbortController abortAnyInWorkflowWorkflow: AbortSignal.any composes signals inside the workflow VM
  • AbortController abortAnyInStepWorkflow: AbortSignal.any inside a step composes deserialized signals
  • AbortController abortSurvivesReplayWorkflow: controller state consistent across replay
  • AbortController abortThrowIfAbortedWorkflow: throwIfAborted causes FatalError, no retries
  • AbortController abortReasonTypesWorkflow: various abort reason types propagate correctly
  • AbortController abortFetchUncaughtWorkflow: uncaught fetch AbortError is FatalError, no retries
  • AbortController abortFetchInFlightWorkflow: aborting cancels an in-flight fetch
  • AbortController abortVoidSleepTimeoutWorkflow: documented void sleep().then(abort) pattern works
  • AbortController abortDeterministicBranchWorkflow: if-check takes same path on first-run and replay
  • AbortController abortListenerWorkflow: signal.addEventListener fires on the deserialized step signal
  • AbortController abortThrowIfAbortedMidFlightWorkflow: throwIfAborted in a polling loop bails when abort fires
  • AbortController abortDeterministicBranchFromStepWorkflow: branches stay consistent when abort comes from a step
  • AbortController abortHookOrderingWorkflow [listener-first-abort-first]: addEventListener → hook.then → abort() → resumeHook
  • AbortController abortHookOrderingWorkflow [listener-first-hook-first]: addEventListener → hook.then → resumeHook → abort()
  • AbortController abortHookOrderingWorkflow [hook-first-abort-first]: hook.then → addEventListener → abort() → resumeHook
  • AbortController abortHookOrderingWorkflow [hook-first-hook-first]: hook.then → addEventListener → resumeHook → abort()
  • importMetaUrlWorkflow - import.meta.url is available in step bundles | wrun_01KWDG0BAHB9V2GHMMQB34S96G | 🔍 observability
  • metadataFromHelperWorkflow - getWorkflowMetadata/getStepMetadata work from module-level helper (#1577) | wrun_01KWDG11H5ZEAD0ZA0NJMJHVEA | 🔍 observability
  • resilient start: addTenWorkflow completes when run_created returns 500 | wrun_01KWDG13VEH2WVRBX950R9MR39 | 🔍 observability
  • getterStepWorkflow - getter functions with "use step" directive | wrun_01KWDG16RVV5X2DZ6FKF4D2M2X | 🔍 observability
  • distributedAbortController - TTL expiration triggers signal | wrun_01KWDG0JWC50WP936PFAWDH4PX | 🔍 observability
  • distributedAbortController - reconnect to existing controller | wrun_01KWDG0S9VK50K5NNJ3FXP3VYG | 🔍 observability
  • experimental_setAttributes start: initial attributes are seeded on run creation
  • experimental_setAttributes start: reserved-prefix initial attributes are seeded with allowReservedAttributes
  • experimental_setAttributes experimentalSetAttributesWorkflow: workflow-body calls append native attr_set events and merge correctly
  • experimental_setAttributes Promise.all of disjoint-key writes: every key lands
  • experimental_setAttributes workflow throws after awaited setAttributes: attribute still persists on the failed run
  • experimental_setAttributes validation DX: invalid writes throw catchable FatalErrors naming rule and limit

hono (28 failed):

  • AbortController abortAnyInWorkflowWorkflow: AbortSignal.any composes signals inside the workflow VM
  • AbortController abortAnyInStepWorkflow: AbortSignal.any inside a step composes deserialized signals
  • AbortController abortSurvivesReplayWorkflow: controller state consistent across replay
  • AbortController abortThrowIfAbortedWorkflow: throwIfAborted causes FatalError, no retries
  • AbortController abortFetchUncaughtWorkflow: uncaught fetch AbortError is FatalError, no retries
  • AbortController abortFetchInFlightWorkflow: aborting cancels an in-flight fetch
  • AbortController abortVoidSleepTimeoutWorkflow: documented void sleep().then(abort) pattern works
  • AbortController abortDeterministicBranchWorkflow: if-check takes same path on first-run and replay
  • AbortController abortListenerWorkflow: signal.addEventListener fires on the deserialized step signal
  • AbortController abortThrowIfAbortedMidFlightWorkflow: throwIfAborted in a polling loop bails when abort fires
  • AbortController abortDeterministicBranchFromStepWorkflow: branches stay consistent when abort comes from a step
  • AbortController abortHookOrderingWorkflow [listener-first-abort-first]: addEventListener → hook.then → abort() → resumeHook
  • AbortController abortHookOrderingWorkflow [listener-first-hook-first]: addEventListener → hook.then → resumeHook → abort()
  • AbortController abortHookOrderingWorkflow [hook-first-abort-first]: hook.then → addEventListener → abort() → resumeHook
  • AbortController abortHookOrderingWorkflow [hook-first-hook-first]: hook.then → addEventListener → resumeHook → abort()
  • importMetaUrlWorkflow - import.meta.url is available in step bundles | wrun_01KWDG0BAHB9V2GHMMQB34S96G | 🔍 observability
  • resilient start: addTenWorkflow completes when run_created returns 500 | wrun_01KWDG13VEH2WVRBX950R9MR39 | 🔍 observability
  • getterStepWorkflow - getter functions with "use step" directive | wrun_01KWDG16RVV5X2DZ6FKF4D2M2X | 🔍 observability
  • distributedAbortController - manual abort triggers signal | wrun_01KWDG0KW05EDD9WWC600EEP0J | 🔍 observability
  • distributedAbortController - TTL expiration triggers signal | wrun_01KWDG0JWC50WP936PFAWDH4PX | 🔍 observability
  • distributedAbortController - reconnect to existing controller | wrun_01KWDG0S9VK50K5NNJ3FXP3VYG | 🔍 observability
  • experimental_setAttributes start: initial attributes are seeded on run creation
  • experimental_setAttributes start: reserved-prefix initial attributes are seeded with allowReservedAttributes
  • experimental_setAttributes experimentalSetAttributesWorkflow: workflow-body calls append native attr_set events and merge correctly
  • experimental_setAttributes experimentalSetAttributesInsideStepWorkflow: step-body calls append attributed native events
  • experimental_setAttributes fire-and-forget: void experimental_setAttributes lands without awaiting
  • experimental_setAttributes workflow throws after awaited setAttributes: attribute still persists on the failed run
  • experimental_setAttributes validation DX: invalid writes throw catchable FatalErrors naming rule and limit

nextjs-turbopack (41 failed):

  • crossContextSerdeWorkflow - classes defined in step code are deserializable in workflow context | wrun_01KWDFXAVWFQSAHF61JJBNPHCQ | 🔍 observability
  • errorSubclassRoundTripWorkflow - first-class Error subclasses survive every serialization boundary | wrun_01KWDFXH4HTJD22FFWTNKRHNP4 | 🔍 observability
  • stepFunctionAsStartArgWorkflow - step function reference passed as start() argument | wrun_01KWDFXKKF6FYJHGEZ7JK080TJ | 🔍 observability
  • cancelRun - cancelling a running workflow | wrun_01KWDFXSMZ0AANDHWV95Q6SM5C | 🔍 observability
  • cancelRun via CLI - cancelling a running workflow | wrun_01KWDFXZ3TSRECMGTM4NZEV3BR | 🔍 observability
  • pages router promiseAllWorkflow via pages router
  • pages router sleepingWorkflow via pages router
  • hookWithSleepWorkflow - hook payloads delivered correctly with concurrent sleep | wrun_01KWDFY7RQXDZCYXGT5M3Y13VT | 🔍 observability
  • hookWithSleepFinalStepWorkflow - step only on final payload | wrun_01KWDFYNGXF6V0GSS2WR1MTYP3 | 🔍 observability
  • sleepInLoopWorkflow - sleep inside loop with steps actually delays each iteration | wrun_01KWDFYZCGYF3Z1HC6BN57QQZ0 | 🔍 observability
  • sleepWithSequentialStepsWorkflow - sequential steps work with concurrent sleep (control) | wrun_01KWDFZ94PC9FJY9M87P1YA4DM | 🔍 observability
  • AbortController abortTimeoutWorkflow: timeout cancels long-running step
  • AbortController abortParallelWorkflow: abort cancels all parallel steps
  • AbortController abortFromStepWorkflow: step abort cancels an in-flight sibling step
  • AbortController abortReasonWorkflow: abort reason preserved across boundaries
  • AbortController abortAfterCompletionWorkflow: abort after step completes is a no-op
  • AbortController abortViaHookWorkflow: external hook triggers abort on in-flight step
  • AbortController abortExternalSignalWorkflow: signal passed as workflow input
  • AbortController abortExternalSignalInFlightWorkflow: external abort fires mid-flight, propagates to nested steps
  • AbortController abortAnyInWorkflowWorkflow: AbortSignal.any composes signals inside the workflow VM
  • AbortController abortAnyInStepWorkflow: AbortSignal.any inside a step composes deserialized signals
  • AbortController abortSurvivesReplayWorkflow: controller state consistent across replay
  • AbortController abortThrowIfAbortedWorkflow: throwIfAborted causes FatalError, no retries
  • AbortController abortReasonTypesWorkflow: various abort reason types propagate correctly
  • AbortController abortFetchUncaughtWorkflow: uncaught fetch AbortError is FatalError, no retries
  • AbortController abortFetchInFlightWorkflow: aborting cancels an in-flight fetch
  • AbortController abortVoidSleepTimeoutWorkflow: documented void sleep().then(abort) pattern works
  • AbortController abortDeterministicBranchWorkflow: if-check takes same path on first-run and replay
  • AbortController abortThrowIfAbortedMidFlightWorkflow: throwIfAborted in a polling loop bails when abort fires
  • AbortController abortDeterministicBranchFromStepWorkflow: branches stay consistent when abort comes from a step
  • AbortController abortHookOrderingWorkflow [listener-first-abort-first]: addEventListener → hook.then → abort() → resumeHook
  • AbortController abortHookOrderingWorkflow [hook-first-abort-first]: hook.then → addEventListener → abort() → resumeHook
  • AbortController abortHookOrderingWorkflow [hook-first-hook-first]: hook.then → addEventListener → resumeHook → abort()
  • importMetaUrlWorkflow - import.meta.url is available in step bundles | wrun_01KWDG0BAHB9V2GHMMQB34S96G | 🔍 observability
  • distributedAbortController - manual abort triggers signal | wrun_01KWDG0KW05EDD9WWC600EEP0J | 🔍 observability
  • distributedAbortController - TTL expiration triggers signal | wrun_01KWDG0JWC50WP936PFAWDH4PX | 🔍 observability
  • distributedAbortController - reconnect to existing controller | wrun_01KWDG0S9VK50K5NNJ3FXP3VYG | 🔍 observability
  • experimental_setAttributes start: initial attributes are seeded on run creation
  • experimental_setAttributes start: reserved-prefix initial attributes are seeded with allowReservedAttributes
  • experimental_setAttributes experimentalSetAttributesWorkflow: workflow-body calls append native attr_set events and merge correctly
  • experimental_setAttributes fire-and-forget: void experimental_setAttributes lands without awaiting

nextjs-webpack (26 failed):

  • AbortController abortFetchUncaughtWorkflow: uncaught fetch AbortError is FatalError, no retries
  • AbortController abortFetchInFlightWorkflow: aborting cancels an in-flight fetch
  • AbortController abortVoidSleepTimeoutWorkflow: documented void sleep().then(abort) pattern works
  • AbortController abortDeterministicBranchWorkflow: if-check takes same path on first-run and replay
  • AbortController abortListenerWorkflow: signal.addEventListener fires on the deserialized step signal
  • AbortController abortThrowIfAbortedMidFlightWorkflow: throwIfAborted in a polling loop bails when abort fires
  • AbortController abortDeterministicBranchFromStepWorkflow: branches stay consistent when abort comes from a step
  • AbortController abortHookOrderingWorkflow [listener-first-abort-first]: addEventListener → hook.then → abort() → resumeHook
  • AbortController abortHookOrderingWorkflow [listener-first-hook-first]: addEventListener → hook.then → resumeHook → abort()
  • AbortController abortHookOrderingWorkflow [hook-first-abort-first]: hook.then → addEventListener → abort() → resumeHook
  • AbortController abortHookOrderingWorkflow [hook-first-hook-first]: hook.then → addEventListener → resumeHook → abort()
  • importMetaUrlWorkflow - import.meta.url is available in step bundles | wrun_01KWDG0BAHB9V2GHMMQB34S96G | 🔍 observability
  • metadataFromHelperWorkflow - getWorkflowMetadata/getStepMetadata work from module-level helper (#1577) | wrun_01KWDG11H5ZEAD0ZA0NJMJHVEA | 🔍 observability
  • resilient start: addTenWorkflow completes when run_created returns 500 | wrun_01KWDG13VEH2WVRBX950R9MR39 | 🔍 observability
  • getterStepWorkflow - getter functions with "use step" directive | wrun_01KWDG16RVV5X2DZ6FKF4D2M2X | 🔍 observability
  • distributedAbortController - manual abort triggers signal | wrun_01KWDG0KW05EDD9WWC600EEP0J | 🔍 observability
  • distributedAbortController - TTL expiration triggers signal | wrun_01KWDG0JWC50WP936PFAWDH4PX | 🔍 observability
  • distributedAbortController - reconnect to existing controller | wrun_01KWDG0S9VK50K5NNJ3FXP3VYG | 🔍 observability
  • experimental_setAttributes start: initial attributes are seeded on run creation
  • experimental_setAttributes start: reserved-prefix initial attributes are seeded with allowReservedAttributes
  • experimental_setAttributes experimentalSetAttributesWorkflow: workflow-body calls append native attr_set events and merge correctly
  • experimental_setAttributes experimentalSetAttributesInsideStepWorkflow: step-body calls append attributed native events
  • experimental_setAttributes fire-and-forget: void experimental_setAttributes lands without awaiting
  • experimental_setAttributes Promise.all of disjoint-key writes: every key lands
  • experimental_setAttributes workflow throws after awaited setAttributes: attribute still persists on the failed run
  • experimental_setAttributes validation DX: invalid writes throw catchable FatalErrors naming rule and limit

nitro (28 failed):

  • AbortController abortExternalSignalWorkflow: signal passed as workflow input
  • AbortController abortExternalSignalInFlightWorkflow: external abort fires mid-flight, propagates to nested steps
  • AbortController abortAnyInWorkflowWorkflow: AbortSignal.any composes signals inside the workflow VM
  • AbortController abortAnyInStepWorkflow: AbortSignal.any inside a step composes deserialized signals
  • AbortController abortSurvivesReplayWorkflow: controller state consistent across replay
  • AbortController abortReasonTypesWorkflow: various abort reason types propagate correctly
  • AbortController abortFetchUncaughtWorkflow: uncaught fetch AbortError is FatalError, no retries
  • AbortController abortFetchInFlightWorkflow: aborting cancels an in-flight fetch
  • AbortController abortVoidSleepTimeoutWorkflow: documented void sleep().then(abort) pattern works
  • AbortController abortDeterministicBranchWorkflow: if-check takes same path on first-run and replay
  • AbortController abortListenerWorkflow: signal.addEventListener fires on the deserialized step signal
  • AbortController abortThrowIfAbortedMidFlightWorkflow: throwIfAborted in a polling loop bails when abort fires
  • AbortController abortDeterministicBranchFromStepWorkflow: branches stay consistent when abort comes from a step
  • AbortController abortHookOrderingWorkflow [listener-first-abort-first]: addEventListener → hook.then → abort() → resumeHook
  • AbortController abortHookOrderingWorkflow [listener-first-hook-first]: addEventListener → hook.then → resumeHook → abort()
  • AbortController abortHookOrderingWorkflow [hook-first-abort-first]: hook.then → addEventListener → abort() → resumeHook
  • AbortController abortHookOrderingWorkflow [hook-first-hook-first]: hook.then → addEventListener → resumeHook → abort()
  • importMetaUrlWorkflow - import.meta.url is available in step bundles | wrun_01KWDG0BAHB9V2GHMMQB34S96G | 🔍 observability
  • metadataFromHelperWorkflow - getWorkflowMetadata/getStepMetadata work from module-level helper (#1577) | wrun_01KWDG11H5ZEAD0ZA0NJMJHVEA | 🔍 observability
  • resilient start: addTenWorkflow completes when run_created returns 500 | wrun_01KWDG13VEH2WVRBX950R9MR39 | 🔍 observability
  • getterStepWorkflow - getter functions with "use step" directive | wrun_01KWDG16RVV5X2DZ6FKF4D2M2X | 🔍 observability
  • distributedAbortController - manual abort triggers signal | wrun_01KWDG0KW05EDD9WWC600EEP0J | 🔍 observability
  • distributedAbortController - TTL expiration triggers signal | wrun_01KWDG0JWC50WP936PFAWDH4PX | 🔍 observability
  • distributedAbortController - reconnect to existing controller | wrun_01KWDG0S9VK50K5NNJ3FXP3VYG | 🔍 observability
  • experimental_setAttributes start: reserved-prefix initial attributes are seeded with allowReservedAttributes
  • experimental_setAttributes experimentalSetAttributesWorkflow: workflow-body calls append native attr_set events and merge correctly
  • experimental_setAttributes experimentalSetAttributesInsideStepWorkflow: step-body calls append attributed native events
  • experimental_setAttributes validation DX: invalid writes throw catchable FatalErrors naming rule and limit

nuxt (33 failed):

  • AbortController abortAfterCompletionWorkflow: abort after step completes is a no-op
  • AbortController abortViaHookWorkflow: external hook triggers abort on in-flight step
  • AbortController abortExternalSignalWorkflow: signal passed as workflow input
  • AbortController abortExternalSignalInFlightWorkflow: external abort fires mid-flight, propagates to nested steps
  • AbortController abortAnyInWorkflowWorkflow: AbortSignal.any composes signals inside the workflow VM
  • AbortController abortAnyInStepWorkflow: AbortSignal.any inside a step composes deserialized signals
  • AbortController abortThrowIfAbortedWorkflow: throwIfAborted causes FatalError, no retries
  • AbortController abortReasonTypesWorkflow: various abort reason types propagate correctly
  • AbortController abortFetchUncaughtWorkflow: uncaught fetch AbortError is FatalError, no retries
  • AbortController abortFetchInFlightWorkflow: aborting cancels an in-flight fetch
  • AbortController abortVoidSleepTimeoutWorkflow: documented void sleep().then(abort) pattern works
  • AbortController abortDeterministicBranchWorkflow: if-check takes same path on first-run and replay
  • AbortController abortListenerWorkflow: signal.addEventListener fires on the deserialized step signal
  • AbortController abortThrowIfAbortedMidFlightWorkflow: throwIfAborted in a polling loop bails when abort fires
  • AbortController abortDeterministicBranchFromStepWorkflow: branches stay consistent when abort comes from a step
  • AbortController abortHookOrderingWorkflow [listener-first-abort-first]: addEventListener → hook.then → abort() → resumeHook
  • AbortController abortHookOrderingWorkflow [listener-first-hook-first]: addEventListener → hook.then → resumeHook → abort()
  • AbortController abortHookOrderingWorkflow [hook-first-abort-first]: hook.then → addEventListener → abort() → resumeHook
  • AbortController abortHookOrderingWorkflow [hook-first-hook-first]: hook.then → addEventListener → resumeHook → abort()
  • importMetaUrlWorkflow - import.meta.url is available in step bundles | wrun_01KWDG0BAHB9V2GHMMQB34S96G | 🔍 observability
  • resilient start: addTenWorkflow completes when run_created returns 500 | wrun_01KWDG13VEH2WVRBX950R9MR39 | 🔍 observability
  • getterStepWorkflow - getter functions with "use step" directive | wrun_01KWDG16RVV5X2DZ6FKF4D2M2X | 🔍 observability
  • distributedAbortController - manual abort triggers signal | wrun_01KWDG0KW05EDD9WWC600EEP0J | 🔍 observability
  • distributedAbortController - TTL expiration triggers signal | wrun_01KWDG0JWC50WP936PFAWDH4PX | 🔍 observability
  • distributedAbortController - reconnect to existing controller | wrun_01KWDG0S9VK50K5NNJ3FXP3VYG | 🔍 observability
  • experimental_setAttributes start: initial attributes are seeded on run creation
  • experimental_setAttributes start: reserved-prefix initial attributes are seeded with allowReservedAttributes
  • experimental_setAttributes experimentalSetAttributesWorkflow: workflow-body calls append native attr_set events and merge correctly
  • experimental_setAttributes experimentalSetAttributesInsideStepWorkflow: step-body calls append attributed native events
  • experimental_setAttributes fire-and-forget: void experimental_setAttributes lands without awaiting
  • experimental_setAttributes Promise.all of disjoint-key writes: every key lands
  • experimental_setAttributes workflow throws after awaited setAttributes: attribute still persists on the failed run
  • experimental_setAttributes validation DX: invalid writes throw catchable FatalErrors naming rule and limit

sveltekit (32 failed):

  • AbortController abortAlreadyAbortedWorkflow: pre-aborted signal seen by step
  • AbortController abortReasonWorkflow: abort reason preserved across boundaries
  • AbortController abortAfterCompletionWorkflow: abort after step completes is a no-op
  • AbortController abortViaHookWorkflow: external hook triggers abort on in-flight step
  • AbortController abortExternalSignalWorkflow: signal passed as workflow input
  • AbortController abortAnyInWorkflowWorkflow: AbortSignal.any composes signals inside the workflow VM
  • AbortController abortAnyInStepWorkflow: AbortSignal.any inside a step composes deserialized signals
  • AbortController abortSurvivesReplayWorkflow: controller state consistent across replay
  • AbortController abortThrowIfAbortedWorkflow: throwIfAborted causes FatalError, no retries
  • AbortController abortReasonTypesWorkflow: various abort reason types propagate correctly
  • AbortController abortFetchUncaughtWorkflow: uncaught fetch AbortError is FatalError, no retries
  • AbortController abortFetchInFlightWorkflow: aborting cancels an in-flight fetch
  • AbortController abortVoidSleepTimeoutWorkflow: documented void sleep().then(abort) pattern works
  • AbortController abortListenerWorkflow: signal.addEventListener fires on the deserialized step signal
  • AbortController abortThrowIfAbortedMidFlightWorkflow: throwIfAborted in a polling loop bails when abort fires
  • AbortController abortDeterministicBranchFromStepWorkflow: branches stay consistent when abort comes from a step
  • AbortController abortHookOrderingWorkflow [listener-first-abort-first]: addEventListener → hook.then → abort() → resumeHook
  • AbortController abortHookOrderingWorkflow [listener-first-hook-first]: addEventListener → hook.then → resumeHook → abort()
  • AbortController abortHookOrderingWorkflow [hook-first-abort-first]: hook.then → addEventListener → abort() → resumeHook
  • AbortController abortHookOrderingWorkflow [hook-first-hook-first]: hook.then → addEventListener → resumeHook → abort()
  • importMetaUrlWorkflow - import.meta.url is available in step bundles | wrun_01KWDG0BAHB9V2GHMMQB34S96G | 🔍 observability
  • metadataFromHelperWorkflow - getWorkflowMetadata/getStepMetadata work from module-level helper (#1577) | wrun_01KWDG11H5ZEAD0ZA0NJMJHVEA | 🔍 observability
  • resilient start: addTenWorkflow completes when run_created returns 500 | wrun_01KWDG13VEH2WVRBX950R9MR39 | 🔍 observability
  • getterStepWorkflow - getter functions with "use step" directive | wrun_01KWDG16RVV5X2DZ6FKF4D2M2X | 🔍 observability
  • distributedAbortController - manual abort triggers signal | wrun_01KWDG0KW05EDD9WWC600EEP0J | 🔍 observability
  • experimental_setAttributes start: initial attributes are seeded on run creation
  • experimental_setAttributes start: reserved-prefix initial attributes are seeded with allowReservedAttributes
  • experimental_setAttributes experimentalSetAttributesWorkflow: workflow-body calls append native attr_set events and merge correctly
  • experimental_setAttributes experimentalSetAttributesInsideStepWorkflow: step-body calls append attributed native events
  • experimental_setAttributes fire-and-forget: void experimental_setAttributes lands without awaiting
  • experimental_setAttributes Promise.all of disjoint-key writes: every key lands
  • experimental_setAttributes validation DX: invalid writes throw catchable FatalErrors naming rule and limit

vite (34 failed):

  • AbortController abortParallelWorkflow: abort cancels all parallel steps
  • AbortController abortFromStepWorkflow: step abort cancels an in-flight sibling step
  • AbortController abortAlreadyAbortedWorkflow: pre-aborted signal seen by step
  • AbortController abortReasonWorkflow: abort reason preserved across boundaries
  • AbortController abortAfterCompletionWorkflow: abort after step completes is a no-op
  • AbortController abortViaHookWorkflow: external hook triggers abort on in-flight step
  • AbortController abortExternalSignalWorkflow: signal passed as workflow input
  • AbortController abortAnyInWorkflowWorkflow: AbortSignal.any composes signals inside the workflow VM
  • AbortController abortAnyInStepWorkflow: AbortSignal.any inside a step composes deserialized signals
  • AbortController abortSurvivesReplayWorkflow: controller state consistent across replay
  • AbortController abortThrowIfAbortedWorkflow: throwIfAborted causes FatalError, no retries
  • AbortController abortReasonTypesWorkflow: various abort reason types propagate correctly
  • AbortController abortFetchUncaughtWorkflow: uncaught fetch AbortError is FatalError, no retries
  • AbortController abortFetchInFlightWorkflow: aborting cancels an in-flight fetch
  • AbortController abortVoidSleepTimeoutWorkflow: documented void sleep().then(abort) pattern works
  • AbortController abortDeterministicBranchWorkflow: if-check takes same path on first-run and replay
  • AbortController abortListenerWorkflow: signal.addEventListener fires on the deserialized step signal
  • AbortController abortThrowIfAbortedMidFlightWorkflow: throwIfAborted in a polling loop bails when abort fires
  • AbortController abortDeterministicBranchFromStepWorkflow: branches stay consistent when abort comes from a step
  • AbortController abortHookOrderingWorkflow [listener-first-abort-first]: addEventListener → hook.then → abort() → resumeHook
  • AbortController abortHookOrderingWorkflow [listener-first-hook-first]: addEventListener → hook.then → resumeHook → abort()
  • AbortController abortHookOrderingWorkflow [hook-first-abort-first]: hook.then → addEventListener → abort() → resumeHook
  • AbortController abortHookOrderingWorkflow [hook-first-hook-first]: hook.then → addEventListener → resumeHook → abort()
  • importMetaUrlWorkflow - import.meta.url is available in step bundles | wrun_01KWDG0BAHB9V2GHMMQB34S96G | 🔍 observability
  • resilient start: addTenWorkflow completes when run_created returns 500 | wrun_01KWDG13VEH2WVRBX950R9MR39 | 🔍 observability
  • distributedAbortController - manual abort triggers signal | wrun_01KWDG0KW05EDD9WWC600EEP0J | 🔍 observability
  • distributedAbortController - TTL expiration triggers signal | wrun_01KWDG0JWC50WP936PFAWDH4PX | 🔍 observability
  • distributedAbortController - reconnect to existing controller | wrun_01KWDG0S9VK50K5NNJ3FXP3VYG | 🔍 observability
  • experimental_setAttributes start: initial attributes are seeded on run creation
  • experimental_setAttributes experimentalSetAttributesWorkflow: workflow-body calls append native attr_set events and merge correctly
  • experimental_setAttributes experimentalSetAttributesInsideStepWorkflow: step-body calls append attributed native events
  • experimental_setAttributes fire-and-forget: void experimental_setAttributes lands without awaiting
  • experimental_setAttributes Promise.all of disjoint-key writes: every key lands
  • experimental_setAttributes validation DX: invalid writes throw catchable FatalErrors naming rule and limit
📋 Other (30 failed)

e2e-vercel-prod-tanstack-start (30 failed):

  • AbortController abortViaHookWorkflow: external hook triggers abort on in-flight step
  • AbortController abortExternalSignalWorkflow: signal passed as workflow input
  • AbortController abortExternalSignalInFlightWorkflow: external abort fires mid-flight, propagates to nested steps
  • AbortController abortAnyInWorkflowWorkflow: AbortSignal.any composes signals inside the workflow VM
  • AbortController abortAnyInStepWorkflow: AbortSignal.any inside a step composes deserialized signals
  • AbortController abortSurvivesReplayWorkflow: controller state consistent across replay
  • AbortController abortThrowIfAbortedWorkflow: throwIfAborted causes FatalError, no retries
  • AbortController abortReasonTypesWorkflow: various abort reason types propagate correctly
  • AbortController abortFetchUncaughtWorkflow: uncaught fetch AbortError is FatalError, no retries
  • AbortController abortFetchInFlightWorkflow: aborting cancels an in-flight fetch
  • AbortController abortVoidSleepTimeoutWorkflow: documented void sleep().then(abort) pattern works
  • AbortController abortListenerWorkflow: signal.addEventListener fires on the deserialized step signal
  • AbortController abortThrowIfAbortedMidFlightWorkflow: throwIfAborted in a polling loop bails when abort fires
  • AbortController abortDeterministicBranchFromStepWorkflow: branches stay consistent when abort comes from a step
  • AbortController abortHookOrderingWorkflow [listener-first-abort-first]: addEventListener → hook.then → abort() → resumeHook
  • AbortController abortHookOrderingWorkflow [listener-first-hook-first]: addEventListener → hook.then → resumeHook → abort()
  • AbortController abortHookOrderingWorkflow [hook-first-abort-first]: hook.then → addEventListener → abort() → resumeHook
  • AbortController abortHookOrderingWorkflow [hook-first-hook-first]: hook.then → addEventListener → resumeHook → abort()
  • importMetaUrlWorkflow - import.meta.url is available in step bundles | wrun_01KWDG0BAHB9V2GHMMQB34S96G
  • metadataFromHelperWorkflow - getWorkflowMetadata/getStepMetadata work from module-level helper (#1577) | wrun_01KWDG11H5ZEAD0ZA0NJMJHVEA
  • resilient start: addTenWorkflow completes when run_created returns 500 | wrun_01KWDG13VEH2WVRBX950R9MR39
  • getterStepWorkflow - getter functions with "use step" directive | wrun_01KWDG16RVV5X2DZ6FKF4D2M2X
  • distributedAbortController - manual abort triggers signal | wrun_01KWDG0KW05EDD9WWC600EEP0J
  • distributedAbortController - TTL expiration triggers signal | wrun_01KWDG0JWC50WP936PFAWDH4PX
  • distributedAbortController - reconnect to existing controller | wrun_01KWDG0S9VK50K5NNJ3FXP3VYG
  • experimental_setAttributes start: initial attributes are seeded on run creation
  • experimental_setAttributes start: reserved-prefix initial attributes are seeded with allowReservedAttributes
  • experimental_setAttributes fire-and-forget: void experimental_setAttributes lands without awaiting
  • experimental_setAttributes workflow throws after awaited setAttributes: attribute still persists on the failed run
  • experimental_setAttributes validation DX: invalid writes throw catchable FatalErrors naming rule and limit

Details by Category

❌ ▲ Vercel Production
App Passed Failed Skipped
❌ astro 103 22 27
❌ example 103 22 27
❌ express 108 17 27
❌ fastify 89 36 27
❌ hono 97 28 27
❌ nextjs-turbopack 108 41 3
❌ nextjs-webpack 123 26 3
❌ nitro 97 28 27
❌ nuxt 92 33 27
❌ sveltekit 112 32 8
❌ vite 91 34 27
✅ 💻 Local Development
App Passed Failed Skipped
✅ astro-stable 127 0 25
✅ express-stable 127 0 25
✅ fastify-stable 127 0 25
✅ hono-stable 127 0 25
✅ nextjs-turbopack-canary 133 0 19
✅ nextjs-turbopack-stable 152 0 0
✅ nextjs-webpack-canary 133 0 19
✅ nextjs-webpack-stable 152 0 0
✅ nitro-stable 127 0 25
✅ nuxt-stable 127 0 25
✅ sveltekit-stable 146 0 6
✅ vite-stable 127 0 25
✅ 📦 Local Production
App Passed Failed Skipped
✅ astro-stable 127 0 25
✅ express-stable 127 0 25
✅ fastify-stable 127 0 25
✅ hono-stable 127 0 25
✅ nextjs-turbopack-canary 133 0 19
✅ nextjs-turbopack-stable 152 0 0
✅ nextjs-webpack-canary 133 0 19
✅ nextjs-webpack-stable 152 0 0
✅ nitro-stable 127 0 25
✅ nuxt-stable 127 0 25
✅ sveltekit-stable 146 0 6
✅ vite-stable 127 0 25
✅ 🐘 Local Postgres
App Passed Failed Skipped
✅ astro-stable 127 0 25
✅ express-stable 127 0 25
✅ fastify-stable 127 0 25
✅ hono-stable 127 0 25
✅ nextjs-turbopack-canary 133 0 19
✅ nextjs-turbopack-stable 152 0 0
✅ nextjs-webpack-canary 133 0 19
✅ nextjs-webpack-stable 152 0 0
✅ nitro-stable 127 0 25
✅ nuxt-stable 127 0 25
✅ sveltekit-stable 146 0 6
✅ vite-stable 127 0 25
✅ 🪟 Windows
App Passed Failed Skipped
✅ nextjs-turbopack 152 0 0
❌ 📋 Other
App Passed Failed Skipped
✅ e2e-local-dev-nest-stable 127 0 25
✅ e2e-local-dev-tanstack-start- 127 0 25
✅ e2e-local-postgres-nest-stable 127 0 25
✅ e2e-local-postgres-tanstack-start- 127 0 25
✅ e2e-local-prod-nest-stable 127 0 25
✅ e2e-local-prod-tanstack-start- 127 0 25
❌ e2e-vercel-prod-tanstack-start 95 30 27

📋 View full workflow run


Some E2E test jobs failed:

  • Vercel Prod: failure
  • Local Dev: success
  • Local Prod: success
  • Local Postgres: success
  • Windows: success

Check the workflow run for details.

@github-actions

github-actions Bot commented Jun 30, 2026

Copy link
Copy Markdown
Contributor

📊 Benchmark Results

📈 Comparing against baseline from main branch. Green 🟢 = faster, Red 🔺 = slower.

workflow with no steps

💻 Local Development

World Framework Workflow Time Wall Time Overhead Samples vs Fastest
💻 Local 🥇 Express 0.044s (-4.8%) 1.008s (~) 0.964s 10 1.00x
💻 Local Nitro 0.046s (+5.4% 🔺) 1.006s (~) 0.959s 10 1.06x
💻 Local Next.js (Turbopack) 0.050s (-7.1% 🟢) 1.007s (~) 0.957s 10 1.14x
🐘 Postgres Next.js (Turbopack) 0.060s (-45.5% 🟢) 1.014s (-2.5%) 0.954s 10 1.37x
🐘 Postgres Nitro 0.064s (+2.4%) 1.014s (~) 0.949s 10 1.46x
🐘 Postgres Express 0.066s (-9.2% 🟢) 1.013s (~) 0.946s 10 1.51x
workflow with 1 step

💻 Local Development

World Framework Workflow Time Wall Time Overhead Samples vs Fastest
💻 Local 🥇 Express 1.081s (~) 2.006s (~) 0.925s 10 1.00x
💻 Local Nitro 1.083s (~) 2.006s (~) 0.922s 10 1.00x
💻 Local Next.js (Turbopack) 1.090s (+0.9%) 2.007s (~) 0.917s 10 1.01x
🐘 Postgres Next.js (Turbopack) 1.091s (-1.0%) 2.010s (~) 0.919s 10 1.01x
🐘 Postgres Nitro 1.094s (~) 2.011s (~) 0.917s 10 1.01x
🐘 Postgres Express 1.099s (~) 2.011s (~) 0.912s 10 1.02x
workflow with 10 sequential steps

💻 Local Development

World Framework Workflow Time Wall Time Overhead Samples vs Fastest
💻 Local 🥇 Express 10.426s (-0.6%) 11.023s (~) 0.596s 3 1.00x
💻 Local Nitro 10.445s (~) 11.022s (~) 0.576s 3 1.00x
💻 Local Next.js (Turbopack) 10.461s (~) 11.022s (~) 0.561s 3 1.00x
🐘 Postgres Express 10.500s (~) 11.020s (~) 0.520s 3 1.01x
🐘 Postgres Next.js (Turbopack) 10.521s (+1.2%) 11.021s (~) 0.500s 3 1.01x
🐘 Postgres Nitro 10.562s (+1.0%) 11.019s (~) 0.457s 3 1.01x
workflow with 25 sequential steps

💻 Local Development

World Framework Workflow Time Wall Time Overhead Samples vs Fastest
💻 Local 🥇 Express 13.540s (-1.4%) 14.029s (~) 0.489s 5 1.00x
💻 Local Nitro 13.608s (~) 14.027s (~) 0.419s 5 1.01x
💻 Local Next.js (Turbopack) 13.664s (~) 14.026s (~) 0.362s 5 1.01x
🐘 Postgres Express 13.700s (+0.5%) 14.021s (~) 0.321s 5 1.01x
🐘 Postgres Nitro 13.744s (+0.5%) 14.022s (~) 0.278s 5 1.02x
🐘 Postgres Next.js (Turbopack) 13.748s (+0.7%) 14.021s (~) 0.273s 5 1.02x
workflow with 50 sequential steps

💻 Local Development

World Framework Workflow Time Wall Time Overhead Samples vs Fastest
💻 Local 🥇 Express 12.044s (-1.1%) 12.882s (-1.1%) 0.838s 7 1.00x
💻 Local Nitro 12.110s (~) 13.025s (~) 0.916s 7 1.01x
💻 Local Next.js (Turbopack) 12.174s (~) 13.024s (~) 0.850s 7 1.01x
🐘 Postgres Express 12.199s (-2.3%) 13.019s (~) 0.820s 7 1.01x
🐘 Postgres Next.js (Turbopack) 12.306s (-1.0%) 13.016s (+0.8%) 0.711s 7 1.02x
🐘 Postgres Nitro 12.504s (+2.5%) 13.020s (~) 0.517s 7 1.04x
Promise.all with 10 concurrent steps

💻 Local Development

World Framework Workflow Time Wall Time Overhead Samples vs Fastest
🐘 Postgres 🥇 Nitro 1.194s (-0.6%) 2.007s (~) 0.813s 15 1.00x
🐘 Postgres Next.js (Turbopack) 1.203s (-5.4% 🟢) 2.010s (-1.5%) 0.806s 15 1.01x
🐘 Postgres Express 1.215s (+2.7%) 2.008s (~) 0.793s 15 1.02x
💻 Local Express 1.360s (-2.3%) 2.006s (~) 0.646s 15 1.14x
💻 Local Next.js (Turbopack) 1.421s (~) 2.006s (~) 0.585s 15 1.19x
💻 Local Nitro 1.429s (+2.1%) 2.007s (~) 0.577s 15 1.20x
Promise.all with 25 concurrent steps

💻 Local Development

World Framework Workflow Time Wall Time Overhead Samples vs Fastest
🐘 Postgres 🥇 Nitro 1.354s (+2.4%) 2.295s (-18.8% 🟢) 0.941s 14 1.00x
🐘 Postgres Next.js (Turbopack) 1.355s (+1.8%) 2.677s (+8.0% 🔺) 1.323s 12 1.00x
🐘 Postgres Express 1.385s (+2.2%) 2.224s (-3.9%) 0.840s 14 1.02x
💻 Local Next.js (Turbopack) 2.243s (-18.2% 🟢) 2.918s (-6.2% 🟢) 0.674s 11 1.66x
💻 Local Nitro 2.356s (-6.5% 🟢) 3.009s (-3.2%) 0.653s 10 1.74x
💻 Local Express 2.650s (+2.6%) 3.109s (+3.3%) 0.458s 10 1.96x
Promise.all with 50 concurrent steps

💻 Local Development

World Framework Workflow Time Wall Time Overhead Samples vs Fastest
🐘 Postgres 🥇 Express 1.769s (+11.3% 🔺) 4.137s (+3.1%) 2.368s 8 1.00x
🐘 Postgres Nitro 1.782s (+12.2% 🔺) 4.871s (+21.4% 🔺) 3.089s 7 1.01x
🐘 Postgres Next.js (Turbopack) 4.090s (+124.7% 🔺) 7.521s (+53.8% 🔺) 3.431s 4 2.31x
💻 Local Nitro 4.602s (-25.2% 🟢) 5.012s (-26.5% 🟢) 0.410s 6 2.60x
💻 Local Express 4.762s (-28.5% 🟢) 5.179s (-30.2% 🟢) 0.418s 6 2.69x
💻 Local Next.js (Turbopack) 4.781s (-39.0% 🟢) 5.180s (-35.4% 🟢) 0.399s 6 2.70x
Promise.race with 10 concurrent steps

💻 Local Development

World Framework Workflow Time Wall Time Overhead Samples vs Fastest
🐘 Postgres 🥇 Nitro 1.207s (+3.0%) 2.009s (~) 0.802s 15 1.00x
🐘 Postgres Next.js (Turbopack) 1.208s (+2.1%) 2.009s (~) 0.801s 15 1.00x
🐘 Postgres Express 1.310s (+11.1% 🔺) 2.075s (+3.4%) 0.765s 15 1.09x
💻 Local Express 1.423s (-1.6%) 2.007s (~) 0.584s 15 1.18x
💻 Local Nitro 1.423s (+3.8%) 2.007s (~) 0.584s 15 1.18x
💻 Local Next.js (Turbopack) 1.456s (+1.8%) 2.006s (~) 0.551s 15 1.21x
Promise.race with 25 concurrent steps

💻 Local Development

World Framework Workflow Time Wall Time Overhead Samples vs Fastest
🐘 Postgres 🥇 Nitro 1.323s (+0.7%) 2.153s (-17.0% 🟢) 0.830s 14 1.00x
🐘 Postgres Next.js (Turbopack) 1.365s (-2.9%) 2.828s (+11.7% 🔺) 1.463s 11 1.03x
🐘 Postgres Express 1.399s (+7.0% 🔺) 2.077s (-15.9% 🟢) 0.678s 15 1.06x
💻 Local Express 2.425s (-8.1% 🟢) 3.009s (~) 0.584s 10 1.83x
💻 Local Next.js (Turbopack) 2.460s (-7.8% 🟢) 3.009s (-6.3% 🟢) 0.548s 10 1.86x
💻 Local Nitro 2.481s (-9.4% 🟢) 3.009s (-10.0% 🟢) 0.527s 10 1.88x
Promise.race with 50 concurrent steps

💻 Local Development

World Framework Workflow Time Wall Time Overhead Samples vs Fastest
🐘 Postgres 🥇 Express 1.667s (+4.7%) 4.011s (-3.0%) 2.344s 8 1.00x
🐘 Postgres Nitro 1.818s (+10.5% 🔺) 4.297s (~) 2.479s 7 1.09x
🐘 Postgres Next.js (Turbopack) 3.370s (+29.3% 🔺) 6.215s (+18.8% 🔺) 2.846s 5 2.02x
💻 Local Nitro 4.481s (-30.4% 🟢) 5.680s (-23.4% 🟢) 1.199s 6 2.69x
💻 Local Express 5.162s (-18.5% 🟢) 6.014s (-11.8% 🟢) 0.852s 5 3.10x
💻 Local Next.js (Turbopack) 5.584s (-12.3% 🟢) 6.214s (-13.9% 🟢) 0.630s 5 3.35x
workflow with 10 sequential data payload steps (10KB)

💻 Local Development

World Framework Workflow Time Wall Time Overhead Samples vs Fastest
💻 Local 🥇 Express 0.567s (+2.7%) 1.005s (~) 0.438s 60 1.00x
💻 Local Nitro 0.573s (-5.1% 🟢) 1.005s (-3.3%) 0.433s 60 1.01x
🐘 Postgres Express 0.580s (+5.0%) 1.041s (+3.4%) 0.461s 58 1.02x
🐘 Postgres Next.js (Turbopack) 0.583s (-4.0%) 1.024s (-2.7%) 0.440s 59 1.03x
💻 Local Next.js (Turbopack) 0.608s (-8.0% 🟢) 1.005s (-5.0%) 0.397s 60 1.07x
🐘 Postgres Nitro 0.610s (+8.2% 🔺) 1.024s (~) 0.414s 59 1.08x
workflow with 25 sequential data payload steps (10KB)

💻 Local Development

World Framework Workflow Time Wall Time Overhead Samples vs Fastest
🐘 Postgres 🥇 Express 1.316s (-2.6%) 2.008s (-1.1%) 0.692s 45 1.00x
🐘 Postgres Nitro 1.376s (+4.8%) 2.009s (-2.2%) 0.632s 45 1.05x
🐘 Postgres Next.js (Turbopack) 1.392s (-6.5% 🟢) 2.030s (-5.0% 🟢) 0.638s 45 1.06x
💻 Local Express 1.464s (+4.5%) 2.006s (~) 0.542s 45 1.11x
💻 Local Nitro 1.475s (+3.2%) 2.028s (+1.1%) 0.553s 45 1.12x
💻 Local Next.js (Turbopack) 1.550s (+4.1%) 2.028s (+1.1%) 0.478s 45 1.18x
workflow with 50 sequential data payload steps (10KB)

💻 Local Development

World Framework Workflow Time Wall Time Overhead Samples vs Fastest
🐘 Postgres 🥇 Express 2.612s (-0.7%) 3.034s (-1.7%) 0.422s 40 1.00x
🐘 Postgres Next.js (Turbopack) 2.747s (-6.8% 🟢) 3.010s (-12.8% 🟢) 0.264s 40 1.05x
🐘 Postgres Nitro 2.918s (+12.1% 🔺) 3.344s (+10.2% 🔺) 0.425s 36 1.12x
💻 Local Express 3.150s (+5.5% 🔺) 3.821s (+10.3% 🔺) 0.671s 32 1.21x
💻 Local Nitro 3.258s (+2.5%) 3.912s (-0.8%) 0.654s 31 1.25x
💻 Local Next.js (Turbopack) 3.334s (+4.2%) 4.042s (+0.8%) 0.708s 30 1.28x
workflow with 10 concurrent data payload steps (10KB)

💻 Local Development

World Framework Workflow Time Wall Time Overhead Samples vs Fastest
🐘 Postgres 🥇 Next.js (Turbopack) 0.199s (-13.9% 🟢) 1.007s (~) 0.808s 60 1.00x
🐘 Postgres Express 0.225s (+1.3%) 1.006s (~) 0.781s 60 1.13x
🐘 Postgres Nitro 0.227s (+5.6% 🔺) 1.007s (~) 0.780s 60 1.14x
💻 Local Express 0.514s (+14.2% 🔺) 1.004s (~) 0.491s 60 2.58x
💻 Local Nitro 0.531s (+23.6% 🔺) 1.005s (~) 0.473s 60 2.67x
💻 Local Next.js (Turbopack) 0.585s (-6.9% 🟢) 1.005s (~) 0.419s 60 2.94x
workflow with 25 concurrent data payload steps (10KB)

💻 Local Development

World Framework Workflow Time Wall Time Overhead Samples vs Fastest
🐘 Postgres 🥇 Express 0.360s (+8.2% 🔺) 1.007s (~) 0.646s 90 1.00x
🐘 Postgres Nitro 0.367s (+9.1% 🔺) 1.007s (~) 0.640s 90 1.02x
🐘 Postgres Next.js (Turbopack) 0.374s (+17.5% 🔺) 1.041s (+2.0%) 0.668s 87 1.04x
💻 Local Nitro 2.399s (+24.7% 🔺) 3.009s (+20.0% 🔺) 0.610s 30 6.66x
💻 Local Next.js (Turbopack) 2.551s (-12.8% 🟢) 3.009s (-10.9% 🟢) 0.458s 30 7.08x
💻 Local Express 2.629s (+20.9% 🔺) 3.114s (+12.5% 🔺) 0.485s 29 7.29x
workflow with 50 concurrent data payload steps (10KB)

💻 Local Development

World Framework Workflow Time Wall Time Overhead Samples vs Fastest
🐘 Postgres 🥇 Next.js (Turbopack) 0.591s (+18.1% 🔺) 3.035s (+20.7% 🔺) 2.445s 40 1.00x
🐘 Postgres Express 0.615s (+13.2% 🔺) 1.119s (~) 0.503s 108 1.04x
🐘 Postgres Nitro 0.619s (+12.8% 🔺) 1.151s (+2.0%) 0.532s 105 1.05x
💻 Local Nitro 5.216s (-45.9% 🟢) 8.162s (-24.3% 🟢) 2.945s 15 8.83x
💻 Local Express 5.874s (-36.5% 🟢) 8.598s (-17.7% 🟢) 2.724s 14 9.95x
💻 Local Next.js (Turbopack) 5.953s (-44.9% 🟢) 8.742s (-25.6% 🟢) 2.789s 14 10.08x
Stream Benchmarks (includes TTFB metrics)
workflow with stream

💻 Local Development

World Framework Workflow Time TTFB Slurp Wall Time Overhead Samples vs Fastest
💻 Local 🥇 Express 1.151s (+1.0%) 2.004s (~) 0.011s (~) 2.018s (~) 0.866s 10 1.00x
💻 Local Next.js (Turbopack) 1.152s (+1.1%) 1.965s (~) 0.013s (+6.7% 🔺) 2.020s (~) 0.868s 10 1.00x
💻 Local Nitro 1.152s (~) 2.005s (~) 0.012s (+15.9% 🔺) 2.020s (~) 0.868s 10 1.00x
🐘 Postgres Next.js (Turbopack) 1.161s (-4.9%) 2.001s (+0.6%) 0.001s (+22.2% 🔺) 2.011s (~) 0.849s 10 1.01x
🐘 Postgres Express 1.171s (+1.6%) 1.996s (~) 0.002s (+50.0% 🔺) 2.011s (~) 0.841s 10 1.02x
🐘 Postgres Nitro 1.179s (-0.7%) 2.002s (~) 0.002s (+25.0% 🔺) 2.013s (~) 0.834s 10 1.02x
stream pipeline with 5 transform steps (1MB)

💻 Local Development

World Framework Workflow Time TTFB Slurp Wall Time Overhead Samples vs Fastest
💻 Local 🥇 Express 1.549s (+0.6%) 2.012s (~) 0.012s (-13.9% 🟢) 2.027s (~) 0.478s 30 1.00x
💻 Local Nitro 1.569s (+0.6%) 2.008s (~) 0.012s (-10.4% 🟢) 2.024s (~) 0.455s 30 1.01x
🐘 Postgres Express 1.574s (-0.8%) 2.007s (-1.4%) 0.005s (+2.6%) 2.027s (-1.6%) 0.453s 30 1.02x
💻 Local Next.js (Turbopack) 1.579s (~) 1.971s (~) 0.013s (+8.9% 🔺) 2.025s (~) 0.446s 30 1.02x
🐘 Postgres Nitro 1.594s (+1.3%) 2.007s (~) 0.005s (-8.6% 🟢) 2.027s (~) 0.433s 30 1.03x
🐘 Postgres Next.js (Turbopack) 1.606s (-15.0% 🟢) 2.010s (-11.2% 🟢) 0.005s (-33.8% 🟢) 2.026s (-12.0% 🟢) 0.420s 30 1.04x
10 parallel streams (1MB each)

💻 Local Development

World Framework Workflow Time TTFB Slurp Wall Time Overhead Samples vs Fastest
🐘 Postgres 🥇 Express 0.808s (+3.8%) 1.169s (+10.2% 🔺) 0.000s (NaN%) 1.184s (+9.7% 🔺) 0.375s 51 1.00x
🐘 Postgres Nitro 0.856s (+11.2% 🔺) 1.146s (+3.7%) 0.000s (-38.9% 🟢) 1.169s (+4.5%) 0.313s 53 1.06x
🐘 Postgres Next.js (Turbopack) 0.876s (-36.4% 🟢) 1.303s (-33.6% 🟢) 0.000s (+Infinity% 🔺) 1.312s (-34.5% 🟢) 0.436s 46 1.08x
💻 Local Next.js (Turbopack) 1.267s (-8.3% 🟢) 1.977s (~) 0.000s (+20.0% 🔺) 2.016s (~) 0.749s 30 1.57x
💻 Local Express 1.287s (+1.1%) 1.888s (-6.2% 🟢) 0.000s (-60.9% 🟢) 1.891s (-6.2% 🟢) 0.604s 32 1.59x
💻 Local Nitro 1.310s (+0.9%) 1.919s (-4.7%) 0.000s (+20.5% 🔺) 1.922s (-4.7%) 0.613s 32 1.62x
fan-out fan-in 10 streams (1MB each)

💻 Local Development

World Framework Workflow Time TTFB Slurp Wall Time Overhead Samples vs Fastest
🐘 Postgres 🥇 Express 1.845s (+6.7% 🔺) 2.540s (+11.0% 🔺) 0.000s (-100.0% 🟢) 2.554s (+10.8% 🔺) 0.709s 24 1.00x
🐘 Postgres Nitro 1.847s (-2.3%) 2.497s (+2.6%) 0.000s (+4.2%) 2.512s (+2.5%) 0.665s 24 1.00x
🐘 Postgres Next.js (Turbopack) 2.069s (-26.8% 🟢) 2.608s (-22.3% 🟢) 0.000s (+Infinity% 🔺) 2.618s (-22.8% 🟢) 0.550s 23 1.12x
💻 Local Nitro 3.344s (-9.3% 🟢) 3.902s (-9.6% 🟢) 0.001s (+36.1% 🔺) 3.906s (-9.6% 🟢) 0.562s 16 1.81x
💻 Local Next.js (Turbopack) 3.374s (-3.2%) 3.929s (-1.5%) 0.000s (-75.0% 🟢) 3.969s (-1.6%) 0.594s 16 1.83x
💻 Local Express 3.441s (+1.3%) 4.161s (+3.4%) 0.000s (-30.0% 🟢) 4.164s (+3.4%) 0.724s 15 1.86x

Summary

Fastest Framework by World

Winner determined by most benchmark wins

World 🥇 Fastest Framework Wins
💻 Local Express 14/21
🐘 Postgres Express 12/21
Fastest World by Framework

Winner determined by most benchmark wins

Framework 🥇 Fastest World Wins
Express 🐘 Postgres 13/21
Next.js (Turbopack) 🐘 Postgres 14/21
Nitro 🐘 Postgres 13/21
Column Definitions
  • Workflow Time: Runtime reported by workflow (completedAt - createdAt) - primary metric
  • TTFB: Time to First Byte - time from workflow start until first stream byte received (stream benchmarks only)
  • Slurp: Time from first byte to complete stream consumption (stream benchmarks only)
  • Wall Time: Total testbench time (trigger workflow + poll for result)
  • Overhead: Testbench overhead (Wall Time - Workflow Time)
  • Samples: Number of benchmark iterations run
  • vs Fastest: How much slower compared to the fastest configuration for this benchmark

Worlds:

  • 💻 Local: In-memory filesystem world (local development)
  • 🐘 Postgres: PostgreSQL database world (local development)
  • ▲ Vercel: Vercel production/preview deployment
  • 🌐 Turso: Community world (local development)
  • 🌐 MongoDB: Community world (local development)
  • 🌐 Redis: Community world (local development)
  • 🌐 Jazz: Community world (local development)
  • 🌐 Redis: Community world (local development)
  • 🌐 Redis + BullMQ: Community world (local development)
  • 🌐 Cloudflare: Community world (local development)
  • 🌐 MySQL: Community world (local development)
  • 🌐 Azure: Community world (local development)
  • 🌐 NATS JetStream: Community world (local development)
  • 🌐 Upstash: Community world (local development)
  • 🌐 Platformatic: Community world (local development)

📋 View full workflow run


Some benchmark jobs failed:

  • Local: success
  • Postgres: success
  • Vercel: failure

Check the workflow run for details.

@NathanColosimo NathanColosimo force-pushed the nathanc/2331-step-update-pg-transaction branch from 98f5ea8 to 1ed5483 Compare June 30, 2026 19:40
@vercel vercel Bot temporarily deployed to Preview – workflow-docs June 30, 2026 23:36 Inactive
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

@workflow/world-postgres: late concurrent step_started can land after step_completed in the event log (non-atomic entity UPDATE + event INSERT)

1 participant