Skip to content

QuickJS engine: inline step execution + WASM module caching - #3049

Draft
TooTallNate wants to merge 1 commit into
quickjs-vmfrom
quickjs-vm-perf
Draft

QuickJS engine: inline step execution + WASM module caching#3049
TooTallNate wants to merge 1 commit into
quickjs-vmfrom
quickjs-vm-perf

Conversation

@TooTallNate

Copy link
Copy Markdown
Member

Stacked PR — based on #3048 (quickjs-vm). Review only the top commit here until the base merges.

Summary

PR 2 of the QuickJS VM roadmap: performance work for the WORKFLOW_VM=quickjs engine.

  1. Live-VM inline step execution. The QuickJS entrypoint now keeps the suspended VM alive (startQuickJSWorkflow returns a session with continueWithEvents) and drives an inline continuation loop per invocation:

    • Durable side effects are dispatched for each suspension, then cheap events are fed into the live VM first (attr_set, hook_created, elapsed wait_completed, events from concurrent invocations) so promise chains not gated on steps — hook.getConflict(), setAttributes(), racing sleeps — advance before any step body blocks the invocation.
    • Once no cheap progress remains, up to WORKFLOW_MAX_INLINE_STEPS steps created by this invocation are executed inline, in parallel (executeStep + in-process single-flight), with the ReplayBudget paused during step bodies. Terminal events are fed back into the same live VM — no fresh-VM re-replay and no queue round-trip per step.
    • Racing timers stay correct: before blocking on step bodies, a delayed wait-continuation message is enqueued for the soonest pending wait (same mechanism as the node:vm engine), so a separate invocation writes wait_completed at the right log position while this one is busy. This is what keeps Promise.race([step, sleep]) semantics.
    • Overflow steps, retry/throttled outcomes, and (on redeliveries) potentially-orphaned steps are handed to the queue. Crash recovery for inline-executed steps rides the workflow message's at-least-once redelivery: deliveryAttempt > 1 triggers backstop step messages. (Ownership-lease stamping like the node engine's ownerMessageId model is a noted follow-up; the interim model can rarely double-run a step body cross-instance, within the documented at-least-once step contract.)
    • This loop also subsumes PR 1's attr_set / getConflict immediate-requeue round-trips — those now resolve in-process.
  2. Process-wide WASM module caching. WebAssembly.compile of the QuickJS runtime (~600 KB) and its native extensions now happens once per process (shared promise), instead of on every invocation.

  3. The VM interrupt budget is now per-execution-burst (reset on each continueWithEvents) instead of per-VM-lifetime, since sessions legitimately live for minutes across inline step bodies.

Results (local dev, world-local, nextjs-turbopack)

  • 135/135 e2e tests pass (three full-suite runs; one rare load-flake observed once in hookWorkflow across ~25 runs — under investigation, also being watched on the base branch)
  • Full core unit suite green (1,563 tests)
  • Step-heavy tests improve measurably even at local queue latencies (e.g. fibonacciWorkflow −10%, writableForwardedFromStepWorkflow −36%); the intended payoff is cloud worlds where each queue hop is a network round-trip. Suite wall-clock is dominated by fixed sleeps and unchanged (±2%).

Notes

  • Terminal-drain dispatch (dispatchPendingOps) no longer queues steps at all — queueing is a caller decision (queueStepMessage), keeping drain semantics identical to the node engine's drainPendingQueueItems.
  • The cold-start benchmark comparing VM boot strategies (fresh eval / bytecode / snapshot restore), which gates PR 5, moves to that PR.

@changeset-bot

changeset-bot Bot commented Jul 22, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: af8a0d6

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

This PR includes changesets to release 16 packages
Name Type
@workflow/core Patch
workflow Patch
@workflow/builders Patch
@workflow/cli Patch
@workflow/next Patch
@workflow/nitro Patch
@workflow/vitest Patch
@workflow/web-shared Patch
@workflow/web Patch
@workflow/world-testing Patch
@workflow/astro Patch
@workflow/nest Patch
@workflow/nuxt Patch
@workflow/rollup Patch
@workflow/sveltekit Patch
@workflow/vite 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

@vercel

vercel Bot commented Jul 22, 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 Jul 22, 2026 10:22am
example-nextjs-workflow-webpack Ready Ready Preview, Comment Jul 22, 2026 10:22am
example-workflow Ready Ready Preview, Comment Jul 22, 2026 10:22am
workbench-astro-workflow Ready Ready Preview, Comment Jul 22, 2026 10:22am
workbench-express-workflow Ready Ready Preview, Comment Jul 22, 2026 10:22am
workbench-fastify-workflow Ready Ready Preview, Comment Jul 22, 2026 10:22am
workbench-hono-workflow Ready Ready Preview, Comment Jul 22, 2026 10:22am
workbench-nestjs-workflow Ready Ready Preview, Comment Jul 22, 2026 10:22am
workbench-nitro-workflow Ready Ready Preview, Comment Jul 22, 2026 10:22am
workbench-nuxt-workflow Ready Ready Preview, Comment Jul 22, 2026 10:22am
workbench-sveltekit-workflow Ready Ready Preview, Comment Jul 22, 2026 10:22am
workbench-tanstack-start-workflow Ready Ready Preview, Comment Jul 22, 2026 10:22am
workbench-vite-workflow Ready Ready Preview, Comment Jul 22, 2026 10:22am
workflow-docs Ready Ready Preview, Comment, Open in v0 Jul 22, 2026 10:22am
workflow-swc-playground Building Building Preview, Comment Jul 22, 2026 10:22am
workflow-tarballs Ready Ready Preview, Comment Jul 22, 2026 10:22am
workflow-web Ready Ready Preview, Comment Jul 22, 2026 10:22am

@github-actions

github-actions Bot commented Jul 22, 2026

Copy link
Copy Markdown
Contributor

🧪 E2E Test Results

All tests passed

Summary

Passed Failed Skipped Total
✅ ▲ Vercel Production 1455 0 239 1694
✅ 💻 Local Development 135 0 19 154
✅ 📦 Local Production 270 0 38 308
✅ 🐘 Local Postgres 270 0 38 308
✅ 🪟 Windows 154 0 0 154
✅ 📋 Other 252 0 56 308
✅ vercel-multi-region 27 0 0 27
Total 2563 0 390 2953

Details by Category

✅ ▲ Vercel Production
App Passed Failed Skipped
✅ astro 126 0 28
✅ example 126 0 28
✅ express 126 0 28
✅ fastify 126 0 28
✅ hono 126 0 28
✅ nextjs-turbopack 151 0 3
✅ nextjs-webpack 151 0 3
✅ nitro 126 0 28
✅ nuxt 126 0 28
✅ sveltekit 145 0 9
✅ vite 126 0 28
✅ 💻 Local Development
App Passed Failed Skipped
✅ nextjs-turbopack-canary 135 0 19
✅ 📦 Local Production
App Passed Failed Skipped
✅ nextjs-turbopack-canary 135 0 19
✅ nextjs-webpack-canary 135 0 19
✅ 🐘 Local Postgres
App Passed Failed Skipped
✅ nextjs-turbopack-canary 135 0 19
✅ nextjs-webpack-canary 135 0 19
✅ 🪟 Windows
App Passed Failed Skipped
✅ nextjs-turbopack 154 0 0
✅ 📋 Other
App Passed Failed Skipped
✅ e2e-vercel-prod-nest 126 0 28
✅ e2e-vercel-prod-tanstack-start 126 0 28
✅ vercel-multi-region
App Passed Failed Skipped
✅ nextjs-turbopack 27 0 0

📋 View full workflow run


Some E2E test jobs failed:

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

Check the workflow run for details.

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.

1 participant