[tests] Re-enable concurrent e2e suite - #2083

Closed
VaguelySerious wants to merge 6 commits into
mainfrom
peter/enable-e2e-concurrency
Closed

[tests] Re-enable concurrent e2e suite#2083
VaguelySerious wants to merge 6 commits into
mainfrom
peter/enable-e2e-concurrency

Conversation

@VaguelySerious

Copy link
Copy Markdown
Member

Summary

  • Re-enables describe.concurrent in packages/core/e2e/e2e.test.ts, which was made sequential in 78048e0 ("no concurrency") with a TODO to re-enable.
  • Each workbench currently runs ~87 e2e tests serially against its preview deploy; this is the dominant ~15-minute cost per matrix entry.
  • This is a draft to see which (if any) tests are still flake-prone under concurrency after recent fixes (e.g. the abort-fetch class of flakes in [tests] Fix abort-fetch e2e flake #2081).

Test plan

  • All workbench matrix entries (E2E Vercel Prod) pass
  • Compare wall-clock vs. recent main runs
  • If failures cluster around specific tests, mark them .sequential rather than reverting the whole flip

Notes

If concurrency exposes real isolation bugs (shared identifiers, cross-test state), I'll fix those before unwinding the draft state. If it's only known-flaky externals, those should already have been addressed in #2081 / follow-ups.

🤖 Generated with Claude Code

Flips packages/core/e2e/e2e.test.ts back to describe.concurrent. The
suite was made sequential ("temporarily disabling concurrent tests to
avoid flakiness") and never re-enabled. Each workbench now runs ~87
tests serially against its preview deploy, dominating CI wall-clock.
Putting this up as a draft to see which (if any) tests are still
flake-prone under concurrency, after recent fixes to the abort-fetch
class of flakes.
@vercel

vercelBot commented May 22, 2026

Copy link
Copy Markdown
Contributor

@changeset-bot

changeset-botBot commented May 22, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: c7060b1

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

This PR includes changesets to release 18 packages
NameType
@workflow/world-localPatch
@workflow/cliPatch
@workflow/corePatch
@workflow/vitestPatch
@workflow/world-postgresPatch
workflowPatch
@workflow/world-testingPatch
@workflow/buildersPatch
@workflow/nextPatch
@workflow/nitroPatch
@workflow/web-sharedPatch
@workflow/webPatch
@workflow/astroPatch
@workflow/nestPatch
@workflow/rollupPatch
@workflow/sveltekitPatch
@workflow/vitePatch
@workflow/nuxtPatch

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-actionsBot commented May 22, 2026

Copy link
Copy Markdown
Contributor

📊 Benchmark Results

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

workflow with no steps

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
💻 Local🥇 Nitro0.032s (-26.0% 🟢)1.006s (~)0.974s101.00x
💻 LocalExpress0.032s (-26.9% 🟢)1.006s (~)0.974s101.02x
💻 LocalNext.js (Turbopack)0.034s1.005s0.972s101.06x
🐘 PostgresExpress0.050s (-13.1% 🟢)1.012s (~)0.961s101.58x
🐘 PostgresNitro0.051s (-46.3% 🟢)1.013s (-2.9%)0.962s101.60x
🐘 PostgresNext.js (Turbopack)0.056s1.011s0.955s101.77x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Next.js (Turbopack)0.306s (+21.6% 🔺)2.546s (+9.1% 🔺)2.240s101.00x
▲ VercelNitro0.426s (+4.0%)2.554s (+1.8%)2.128s101.39x
▲ VercelExpress⚠️missing----

🔍 Observability: Next.js (Turbopack) | Nitro

workflow with 1 step

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
💻 Local🥇 Nitro1.076s (-4.9%)2.007s (~)0.931s101.00x
💻 LocalExpress1.079s (-4.1%)2.007s (~)0.928s101.00x
🐘 PostgresNitro1.082s (-5.1% 🟢)2.008s (~)0.926s101.01x
🐘 PostgresExpress1.087s (-5.2% 🟢)2.009s (~)0.922s101.01x
💻 LocalNext.js (Turbopack)1.089s2.005s0.916s101.01x
🐘 PostgresNext.js (Turbopack)1.117s2.008s0.891s101.04x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Nitro1.806s (-53.6% 🟢)3.829s (-35.2% 🟢)2.023s101.00x
▲ VercelNext.js (Turbopack)1.906s (-6.3% 🟢)3.724s (-2.8%)1.818s101.06x
▲ VercelExpress⚠️missing----

🔍 Observability: Nitro | Next.js (Turbopack)

workflow with 10 sequential steps

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Nitro10.387s (-4.4%)11.018s (~)0.630s31.00x
💻 LocalExpress10.434s (-4.5%)11.023s (~)0.590s31.00x
💻 LocalNitro10.434s (-4.7%)11.024s (~)0.590s31.00x
🐘 PostgresExpress10.435s (-4.8%)11.014s (~)0.579s31.00x
💻 LocalNext.js (Turbopack)10.493s11.021s0.528s31.01x
🐘 PostgresNext.js (Turbopack)10.702s11.020s0.318s31.03x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Nitro13.961s (-41.2% 🟢)16.231s (-35.4% 🟢)2.269s21.00x
▲ VercelNext.js (Turbopack)14.589s (-15.8% 🟢)16.525s (-14.8% 🟢)1.936s21.04x
▲ VercelExpress⚠️missing----

🔍 Observability: Nitro | Next.js (Turbopack)

workflow with 25 sequential steps

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Nitro13.428s (-8.0% 🟢)14.016s (-6.7% 🟢)0.588s51.00x
🐘 PostgresExpress13.500s (-7.4% 🟢)14.019s (-6.7% 🟢)0.518s51.01x
💻 LocalExpress13.527s (-9.6% 🟢)14.027s (-6.7% 🟢)0.500s51.01x
💻 LocalNitro13.592s (-9.8% 🟢)14.027s (-12.5% 🟢)0.435s51.01x
💻 LocalNext.js (Turbopack)13.761s14.025s0.264s51.02x
🐘 PostgresNext.js (Turbopack)14.104s15.019s0.915s41.05x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Nitro21.896s (-66.0% 🟢)23.781s (-64.3% 🟢)1.885s31.00x
▲ VercelNext.js (Turbopack)22.228s (-57.7% 🟢)24.764s (-54.7% 🟢)2.536s31.02x
▲ VercelExpress⚠️missing----

🔍 Observability: Nitro | Next.js (Turbopack)

workflow with 50 sequential steps

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Nitro11.854s (-15.1% 🟢)12.015s (-16.0% 🟢)0.161s81.00x
💻 LocalNitro11.923s (-29.0% 🟢)12.021s (-29.4% 🟢)0.098s81.01x
🐘 PostgresExpress11.984s (-14.4% 🟢)12.017s (-17.7% 🟢)0.033s81.01x
💻 LocalExpress12.021s (-27.6% 🟢)12.399s (-27.2% 🟢)0.379s81.01x
💻 LocalNext.js (Turbopack)12.282s13.022s0.740s71.04x
🐘 PostgresNext.js (Turbopack)13.164s14.015s0.851s71.11x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Nitro31.366s (-92.6% 🟢)33.410s (-92.1% 🟢)2.044s31.00x
▲ VercelNext.js (Turbopack)32.382s (-91.8% 🟢)34.323s (-91.3% 🟢)1.940s31.03x
▲ VercelExpress⚠️missing----

🔍 Observability: Nitro | Next.js (Turbopack)

Promise.all with 10 concurrent steps

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Nitro1.158s (-9.2% 🟢)2.009s (~)0.851s151.00x
🐘 PostgresExpress1.160s (-8.0% 🟢)2.008s (~)0.848s151.00x
💻 LocalNitro1.172s (-28.2% 🟢)2.006s (-3.3%)0.835s151.01x
💻 LocalExpress1.179s (-20.8% 🟢)2.007s (~)0.828s151.02x
💻 LocalNext.js (Turbopack)1.212s2.005s0.793s151.05x
🐘 PostgresNext.js (Turbopack)1.218s2.008s0.790s151.05x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Nitro2.877s (+2.1%)4.515s (+4.5%)1.638s81.00x
▲ VercelNext.js (Turbopack)2.967s (-12.7% 🟢)4.728s (-4.2%)1.761s71.03x
▲ VercelExpress⚠️missing----

🔍 Observability: Nitro | Next.js (Turbopack)

Promise.all with 25 concurrent steps

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Express1.212s (-48.6% 🟢)2.007s (-33.3% 🟢)0.795s151.00x
🐘 PostgresNitro1.224s (-47.9% 🟢)2.009s (-33.2% 🟢)0.785s151.01x
🐘 PostgresNext.js (Turbopack)1.336s2.007s0.671s151.10x
💻 LocalNext.js (Turbopack)1.524s2.004s0.480s151.26x
💻 LocalNitro1.744s (-44.5% 🟢)2.007s (-48.3% 🟢)0.264s151.44x
💻 LocalExpress1.835s (-37.8% 🟢)2.140s (-38.0% 🟢)0.304s151.51x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Next.js (Turbopack)4.146s (-41.6% 🟢)6.056s (-32.0% 🟢)1.910s51.00x
▲ VercelNitro5.247s (+29.5% 🔺)7.231s (+22.1% 🔺)1.984s51.27x
▲ VercelExpress⚠️missing----

🔍 Observability: Next.js (Turbopack) | Nitro

Promise.all with 50 concurrent steps

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Express1.339s (-61.6% 🟢)2.007s (-49.9% 🟢)0.669s151.00x
🐘 PostgresNitro1.358s (-61.0% 🟢)2.007s (-49.9% 🟢)0.649s151.01x
🐘 PostgresNext.js (Turbopack)1.617s2.007s0.390s151.21x
💻 LocalNext.js (Turbopack)3.177s3.453s0.276s92.37x
💻 LocalExpress5.435s (-34.8% 🟢)6.214s (-31.2% 🟢)0.779s54.06x
💻 LocalNitro5.468s (-34.5% 🟢)6.012s (-33.3% 🟢)0.544s54.08x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Nitro6.778s (+92.3% 🔺)8.495s (+53.5% 🔺)1.716s41.00x
▲ VercelNext.js (Turbopack)8.699s (-2.4%)11.031s (+0.6%)2.332s31.28x
▲ VercelExpress⚠️missing----

🔍 Observability: Nitro | Next.js (Turbopack)

Promise.race with 10 concurrent steps

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Express1.142s (-9.1% 🟢)2.009s (~)0.866s151.00x
🐘 PostgresNitro1.152s (-8.3% 🟢)2.008s (~)0.856s151.01x
🐘 PostgresNext.js (Turbopack)1.209s2.008s0.800s151.06x
💻 LocalNext.js (Turbopack)1.209s2.004s0.795s151.06x
💻 LocalNitro1.417s (-24.1% 🟢)2.006s (-14.3% 🟢)0.590s151.24x
💻 LocalExpress1.427s (-24.7% 🟢)2.006s (-15.1% 🟢)0.580s151.25x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Next.js (Turbopack)2.873s (-2.0%)4.428s (-4.6%)1.555s71.00x
▲ VercelNitro2.925s (+18.9% 🔺)5.006s (+20.1% 🔺)2.081s61.02x
▲ VercelExpress⚠️missing----

🔍 Observability: Next.js (Turbopack) | Nitro

Promise.race with 25 concurrent steps

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Nitro1.214s (-48.1% 🟢)2.008s (-33.3% 🟢)0.794s151.00x
🐘 PostgresExpress1.226s (-47.6% 🟢)2.009s (-33.3% 🟢)0.783s151.01x
🐘 PostgresNext.js (Turbopack)1.343s2.009s0.666s151.11x
💻 LocalNext.js (Turbopack)1.579s2.005s0.426s151.30x
💻 LocalNitro2.034s (-33.6% 🟢)2.510s (-35.4% 🟢)0.475s121.68x
💻 LocalExpress2.057s (-34.3% 🟢)2.508s (-33.3% 🟢)0.451s121.69x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Nitro3.368s (+4.2%)5.319s (+4.8%)1.951s61.00x
▲ VercelNext.js (Turbopack)4.111s (+30.8% 🔺)5.838s (+29.1% 🔺)1.726s61.22x
▲ VercelExpress⚠️missing----

🔍 Observability: Nitro | Next.js (Turbopack)

Promise.race with 50 concurrent steps

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Nitro1.316s (-62.2% 🟢)2.009s (-49.9% 🟢)0.693s151.00x
🐘 PostgresExpress1.334s (-61.9% 🟢)2.007s (-50.0% 🟢)0.673s151.01x
🐘 PostgresNext.js (Turbopack)1.644s2.008s0.364s151.25x
💻 LocalNext.js (Turbopack)3.454s4.012s0.558s82.62x
💻 LocalExpress5.760s (-34.6% 🟢)6.216s (-33.0% 🟢)0.456s54.38x
💻 LocalNitro5.993s (-34.5% 🟢)6.513s (-35.0% 🟢)0.521s64.55x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Next.js (Turbopack)5.982s (-11.5% 🟢)7.562s (-11.5% 🟢)1.580s41.00x
▲ VercelNitro6.436s (+26.4% 🔺)8.067s (+18.3% 🔺)1.632s41.08x
▲ VercelExpress⚠️missing----

🔍 Observability: Next.js (Turbopack) | Nitro

workflow with 10 sequential data payload steps (10KB)

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Nitro0.428s (-47.8% 🟢)1.006s (~)0.578s601.00x
🐘 PostgresExpress0.467s (-44.3% 🟢)1.006s (-1.7%)0.539s601.09x
💻 LocalNitro0.493s (-49.7% 🟢)1.022s (-6.6% 🟢)0.529s591.15x
💻 LocalExpress0.517s (-47.4% 🟢)1.022s (-5.0% 🟢)0.504s591.21x
💻 LocalNext.js (Turbopack)0.615s1.021s0.406s591.44x
🐘 PostgresNext.js (Turbopack)0.675s1.006s0.332s601.58x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Nitro5.901s (-73.2% 🟢)7.712s (-67.9% 🟢)1.811s81.00x
▲ VercelNext.js (Turbopack)6.006s (-58.6% 🟢)7.961s (-50.5% 🟢)1.956s81.02x
▲ VercelExpress⚠️missing----

🔍 Observability: Nitro | Next.js (Turbopack)

workflow with 25 sequential data payload steps (10KB)

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Nitro0.995s (-48.4% 🟢)1.350s (-35.7% 🟢)0.355s671.00x
🐘 PostgresExpress1.188s (-39.9% 🟢)1.922s (-14.9% 🟢)0.734s471.19x
💻 LocalNitro1.220s (-59.8% 🟢)2.006s (-46.6% 🟢)0.786s451.23x
💻 LocalExpress1.260s (-58.2% 🟢)2.007s (-44.0% 🟢)0.746s451.27x
💻 LocalNext.js (Turbopack)1.421s2.004s0.583s451.43x
🐘 PostgresNext.js (Turbopack)1.660s2.008s0.347s451.67x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Nitro16.140s (-59.1% 🟢)18.101s (-56.2% 🟢)1.961s61.00x
▲ VercelNext.js (Turbopack)16.668s (-66.5% 🟢)19.003s (-63.3% 🟢)2.335s51.03x
▲ VercelExpress⚠️missing----

🔍 Observability: Nitro | Next.js (Turbopack)

workflow with 50 sequential data payload steps (10KB)

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Nitro1.994s (-51.4% 🟢)2.335s (-49.3% 🟢)0.341s521.00x
🐘 PostgresExpress2.106s (-47.2% 🟢)2.697s (-38.3% 🟢)0.591s451.06x
💻 LocalNitro2.786s (-70.0% 🟢)3.058s (-69.5% 🟢)0.272s401.40x
💻 LocalExpress2.801s (-69.6% 🟢)3.033s (-69.7% 🟢)0.231s401.41x
🐘 PostgresNext.js (Turbopack)3.212s4.009s0.797s301.61x
💻 LocalNext.js (Turbopack)3.466s4.074s0.608s301.74x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Nitro27.620s (-71.5% 🟢)29.816s (-69.7% 🟢)2.196s51.00x
▲ VercelNext.js (Turbopack)32.787s (-69.4% 🟢)35.186s (-67.7% 🟢)2.399s41.19x
▲ VercelExpress⚠️missing----

🔍 Observability: Nitro | Next.js (Turbopack)

workflow with 10 concurrent data payload steps (10KB)

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Nitro0.190s (-33.1% 🟢)1.006s (~)0.816s601.00x
🐘 PostgresExpress0.198s (-30.0% 🟢)1.023s (+1.5%)0.825s591.04x
🐘 PostgresNext.js (Turbopack)0.230s1.006s0.776s601.21x
💻 LocalNext.js (Turbopack)0.337s1.003s0.666s601.78x
💻 LocalExpress0.381s (-32.0% 🟢)1.004s (~)0.623s602.01x
💻 LocalNitro0.399s (-34.0% 🟢)1.004s (-1.7%)0.605s602.11x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Nitro2.453s (+47.7% 🔺)4.362s (+30.2% 🔺)1.909s141.00x
▲ VercelNext.js (Turbopack)2.813s (+39.1% 🔺)4.846s (+27.8% 🔺)2.034s131.15x
▲ VercelExpress⚠️missing----

🔍 Observability: Nitro | Next.js (Turbopack)

workflow with 25 concurrent data payload steps (10KB)

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Nitro0.284s (-42.8% 🟢)1.006s (~)0.722s901.00x
🐘 PostgresExpress0.308s (-39.6% 🟢)1.006s (~)0.698s901.08x
🐘 PostgresNext.js (Turbopack)0.435s1.006s0.571s901.53x
💻 LocalNext.js (Turbopack)1.433s2.028s0.595s455.05x
💻 LocalExpress2.159s (-14.1% 🟢)2.715s (-9.8% 🟢)0.556s347.60x
💻 LocalNitro2.171s (-14.5% 🟢)2.884s (-4.1%)0.713s327.65x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Nitro5.296s (+64.2% 🔺)6.881s (+42.7% 🔺)1.585s141.00x
▲ VercelNext.js (Turbopack)6.720s (+90.1% 🔺)8.608s (+65.8% 🔺)1.888s111.27x
▲ VercelExpress⚠️missing----

🔍 Observability: Nitro | Next.js (Turbopack)

workflow with 50 concurrent data payload steps (10KB)

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Nitro0.583s (-26.2% 🟢)1.006s (~)0.422s1201.00x
🐘 PostgresExpress0.652s (-20.4% 🟢)1.006s (-1.1%)0.355s1201.12x
🐘 PostgresNext.js (Turbopack)0.890s1.078s0.188s1121.53x
💻 LocalNext.js (Turbopack)6.974s7.706s0.731s1611.96x
💻 LocalNitro10.196s (-8.9% 🟢)10.777s (-7.6% 🟢)0.581s1217.48x
💻 LocalExpress10.526s (-5.9% 🟢)11.031s (-7.6% 🟢)0.505s1118.04x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Next.js (Turbopack)15.606s (+51.1% 🔺)17.743s (+44.4% 🔺)2.137s71.00x
▲ VercelNitro16.197s (+109.7% 🔺)18.211s (+93.7% 🔺)2.014s71.04x
▲ VercelExpress⚠️missing----

🔍 Observability: Next.js (Turbopack) | Nitro

Stream Benchmarks(includes TTFB metrics)
workflow with stream

💻 Local Development

WorldFrameworkWorkflow TimeTTFBSlurpWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Nitro1.128s (+450.3% 🔺)2.001s (+100.2% 🔺)0.001s (-33.3% 🟢)2.010s (+98.8% 🔺)0.882s101.00x
💻 LocalNext.js (Turbopack)1.129s2.002s0.006s2.011s0.882s101.00x
💻 LocalNitro1.133s (+430.0% 🔺)2.005s (+99.6% 🔺)0.013s (+4.0%)2.020s (+98.3% 🔺)0.888s101.00x
💻 LocalExpress1.138s (+471.7% 🔺)2.006s (+99.7% 🔺)0.013s (+5.8% 🔺)2.021s (+98.5% 🔺)0.883s101.01x
🐘 PostgresExpress1.145s (+458.3% 🔺)2.001s (+100.4% 🔺)0.001s (-18.8% 🟢)2.010s (+98.7% 🔺)0.865s101.01x
🐘 PostgresNext.js (Turbopack)1.201s2.001s0.002s2.010s0.809s101.06x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeTTFBSlurpWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Next.js (Turbopack)2.444s (-64.3% 🟢)3.510s (-59.4% 🟢)2.280s (+260.8% 🔺)6.380s (-34.8% 🟢)3.936s101.00x
▲ VercelNitro2.490s (-35.0% 🟢)3.601s (-31.8% 🟢)13.675s (+1742.7% 🔺)17.941s (+176.7% 🔺)15.451s101.02x
▲ VercelExpress⚠️missing-----

🔍 Observability: Next.js (Turbopack) | Nitro

stream pipeline with 5 transform steps (1MB)

💻 Local Development

WorldFrameworkWorkflow TimeTTFBSlurpWall TimeOverheadSamplesvs Fastest
💻 Local🥇 Next.js (Turbopack)1.475s2.007s0.006s2.015s0.540s301.00x
🐘 PostgresNitro1.494s (+139.3% 🔺)2.002s (+98.9% 🔺)0.004s (+6.4% 🔺)2.027s (+98.3% 🔺)0.534s301.01x
💻 LocalNitro1.541s (+83.7% 🔺)2.013s (+98.9% 🔺)0.010s (+4.9%)2.025s (+81.5% 🔺)0.484s301.04x
🐘 PostgresExpress1.543s (+144.9% 🔺)2.004s (+99.1% 🔺)0.004s (+3.6%)2.028s (+98.2% 🔺)0.484s301.05x
💻 LocalExpress1.549s (+104.6% 🔺)2.013s (+95.7% 🔺)0.010s (+9.4% 🔺)2.026s (+94.8% 🔺)0.476s301.05x
🐘 PostgresNext.js (Turbopack)1.678s2.010s0.004s2.025s0.347s301.14x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeTTFBSlurpWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Next.js (Turbopack)5.989s (-64.6% 🟢)7.269s (-60.1% 🟢)0.284s (+34.4% 🔺)8.226s (-56.6% 🟢)2.237s81.00x
▲ VercelNitro6.280s (-78.7% 🟢)7.470s (-75.8% 🟢)0.267s (+138.6% 🔺)8.303s (-73.9% 🟢)2.023s81.05x
▲ VercelExpress⚠️missing-----

🔍 Observability: Next.js (Turbopack) | Nitro

10 parallel streams (1MB each)

💻 Local Development

WorldFrameworkWorkflow TimeTTFBSlurpWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Nitro0.640s (-34.0% 🟢)1.012s (-18.8% 🟢)0.000s (+22.0% 🔺)1.030s (-18.1% 🟢)0.391s591.00x
🐘 PostgresExpress0.664s (-30.9% 🟢)1.011s (-20.9% 🟢)0.000s (-61.0% 🟢)1.032s (-21.0% 🟢)0.369s591.04x
🐘 PostgresNext.js (Turbopack)0.766s1.017s0.000s1.032s0.266s591.20x
💻 LocalNext.js (Turbopack)1.042s1.631s0.000s1.634s0.592s371.63x
💻 LocalExpress1.344s (+9.7% 🔺)2.016s (~)0.000s (-30.0% 🟢)2.018s (~)0.674s302.10x
💻 LocalNitro1.373s (+12.3% 🔺)2.016s (~)0.000s (+66.7% 🔺)2.018s (~)0.645s302.15x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeTTFBSlurpWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Nitro3.957s (+29.7% 🔺)5.319s (+21.1% 🔺)0.000s (+18.2% 🔺)5.894s (+22.6% 🔺)1.937s111.00x
▲ VercelNext.js (Turbopack)3.981s (-60.9% 🟢)5.285s (-54.1% 🟢)0.000s (+Infinity% 🔺)5.843s (-51.5% 🟢)1.862s111.01x
▲ VercelExpress⚠️missing-----

🔍 Observability: Nitro | Next.js (Turbopack)

fan-out fan-in 10 streams (1MB each)

💻 Local Development

WorldFrameworkWorkflow TimeTTFBSlurpWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Nitro1.278s (-28.7% 🟢)2.029s (-5.2% 🟢)0.000s (-100.0% 🟢)2.049s (-5.7% 🟢)0.771s301.00x
🐘 PostgresExpress1.384s (-21.9% 🟢)2.029s (-6.8% 🟢)0.000s (+Infinity% 🔺)2.044s (-7.1% 🟢)0.659s301.08x
🐘 PostgresNext.js (Turbopack)1.500s2.145s0.000s2.153s0.654s281.17x
💻 LocalNext.js (Turbopack)2.061s2.523s0.000s2.528s0.467s241.61x
💻 LocalExpress3.109s (-10.3% 🟢)3.781s (-6.3% 🟢)0.001s (-21.9% 🟢)3.784s (-6.3% 🟢)0.675s162.43x
💻 LocalNitro3.183s (-6.0% 🟢)3.903s (-3.2%)0.000s (-18.0% 🟢)3.906s (-3.2%)0.723s162.49x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeTTFBSlurpWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Nitro5.980s (+46.1% 🔺)7.651s (+42.4% 🔺)0.000s (-54.2% 🟢)8.181s (+41.2% 🔺)2.201s81.00x
▲ VercelNext.js (Turbopack)6.105s (+8.7% 🔺)7.426s (+6.4% 🔺)0.000s (~)7.952s (+5.5% 🔺)1.847s81.02x
▲ VercelExpress⚠️missing-----

🔍 Observability: Nitro | Next.js (Turbopack)

Summary

Fastest Framework by World

Winner determined by most benchmark wins

World🥇 Fastest FrameworkWins
💻 LocalNext.js (Turbopack)12/21
🐘 PostgresNitro17/21
▲ VercelNitro14/21
Fastest World by Framework

Winner determined by most benchmark wins

Framework🥇 Fastest WorldWins
Express🐘 Postgres17/21
Next.js (Turbopack)🐘 Postgres11/21
Nitro🐘 Postgres19/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)

📋 View full workflow run


Some benchmark jobs failed:

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

Check the workflow run for details.

@github-actions

github-actionsBot commented May 22, 2026

Copy link
Copy Markdown
Contributor

🧪 E2E Test Results

Some tests failed

Summary

PassedFailedSkippedTotal
❌ ▲ Vercel Production121932191441
❌ 💻 Local Development161052191834
❌ 📦 Local Production161412191834
❌ 🐘 Local Postgres150212001703
✅ 🪟 Windows13100131
❌ 📋 Other7401176917
Total68161110337860

❌ Failed Tests

▲ Vercel Production (3 failed)

nitro (1 failed):

  • outputStreamInsideStepWorkflow - getWritable() called inside step functions | wrun_01KS7ZJGW73X7CKYTEQBGY87E5 | 🔍 observability

nuxt (1 failed):

  • hookWorkflow is not resumable via public webhook endpoint | wrun_01KS7ZJ3YXC4E16B345GAMR59H | 🔍 observability

sveltekit (1 failed):

💻 Local Development (5 failed)

astro-stable (2 failed):

  • fibonacciWorkflow - recursive workflow composition via start() | wrun_01KS7ZKMSDH7F5ZJHKWBKZDJPQ
  • AbortController abortExternalSignalInFlightWorkflow: external abort fires mid-flight, propagates to nested steps

nextjs-webpack-stable-lazy-discovery-disabled (1 failed):

  • error handling error propagation step errors basic step error preserves message and stack trace

nuxt-stable (1 failed):

  • AbortController abortExternalSignalInFlightWorkflow: external abort fires mid-flight, propagates to nested steps

sveltekit-stable (1 failed):

  • AbortController abortExternalSignalInFlightWorkflow: external abort fires mid-flight, propagates to nested steps
📦 Local Production (1 failed)

nuxt-stable (1 failed):

  • error handling error propagation step errors basic step error preserves message and stack trace
🐘 Local Postgres (1 failed)

nuxt-stable (1 failed):

  • error handling error propagation step errors basic step error preserves message and stack trace
📋 Other (1 failed)

e2e-local-dev-tanstack-start- (1 failed):

  • AbortController abortExternalSignalInFlightWorkflow: external abort fires mid-flight, propagates to nested steps

Details by Category

❌ ▲ Vercel Production
AppPassedFailedSkipped
✅ astro105026
✅ example105026
✅ express105026
✅ fastify105026
✅ hono105026
✅ nextjs-turbopack12902
✅ nextjs-webpack12902
❌ nitro104126
❌ nuxt104126
❌ sveltekit12317
✅ vite105026
❌ 💻 Local Development
AppPassedFailedSkipped
❌ astro-stable104225
✅ express-stable106025
✅ fastify-stable106025
✅ hono-stable106025
✅ nextjs-turbopack-canary112019
✅ nextjs-turbopack-stable-lazy-discovery-disabled13100
✅ nextjs-turbopack-stable-lazy-discovery-enabled13100
✅ nextjs-webpack-canary112019
❌ nextjs-webpack-stable-lazy-discovery-disabled13010
✅ nextjs-webpack-stable-lazy-discovery-enabled13100
✅ nitro-stable106025
❌ nuxt-stable105125
❌ sveltekit-stable12416
✅ vite-stable106025
❌ 📦 Local Production
AppPassedFailedSkipped
✅ astro-stable106025
✅ express-stable106025
✅ fastify-stable106025
✅ hono-stable106025
✅ nextjs-turbopack-canary112019
✅ nextjs-turbopack-stable-lazy-discovery-disabled13100
✅ nextjs-turbopack-stable-lazy-discovery-enabled13100
✅ nextjs-webpack-canary112019
✅ nextjs-webpack-stable-lazy-discovery-disabled13100
✅ nextjs-webpack-stable-lazy-discovery-enabled13100
✅ nitro-stable106025
❌ nuxt-stable105125
✅ sveltekit-stable12506
✅ vite-stable106025
❌ 🐘 Local Postgres
AppPassedFailedSkipped
✅ astro-stable106025
✅ express-stable106025
✅ fastify-stable106025
✅ hono-stable106025
✅ nextjs-turbopack-stable-lazy-discovery-disabled13100
✅ nextjs-turbopack-stable-lazy-discovery-enabled13100
✅ nextjs-webpack-canary112019
✅ nextjs-webpack-stable-lazy-discovery-disabled13100
✅ nextjs-webpack-stable-lazy-discovery-enabled13100
✅ nitro-stable106025
❌ nuxt-stable105125
✅ sveltekit-stable12506
✅ vite-stable106025
✅ 🪟 Windows
AppPassedFailedSkipped
✅ nextjs-turbopack13100
❌ 📋 Other
AppPassedFailedSkipped
✅ e2e-local-dev-nest-stable106025
❌ e2e-local-dev-tanstack-start-105125
✅ e2e-local-postgres-nest-stable106025
✅ e2e-local-postgres-tanstack-start-106025
✅ e2e-local-prod-nest-stable106025
✅ e2e-local-prod-tanstack-start-106025
✅ e2e-vercel-prod-tanstack-start105026

📋 View full workflow run


Some E2E test jobs failed:

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

Check the workflow run for details.

@VaguelySerious

Copy link
Copy Markdown
MemberAuthor

CI verdict

Speedup is real. Per-workbench wall-clock dropped roughly 3×: from ~14–17 min on main to ~5–6 min here. Vitest's own run is even more dramatic — express went from ~15 min to 203s (~4–5× on the actual test phase).

But correctness regressed. 25 checks failed: every `E2E Vercel Prod Tests (*)` matrix entry except `nitro`, plus several `E2E Local Dev/Prod/Postgres` jobs and `E2E Windows Tests`.

Failure pattern

Every failure I sampled is the same shape: `Error: Test timed out in 60000ms`. The which tests fail varies per workbench — i.e. it's not a single broken test:

WorkbenchFailed test
expresserror handling > catchability > FatalError can be caught… (×2)
nuxtabortThrowIfAbortedWorkflow, step throw round-trips FatalError
astroabortDeterministicBranchFromStepWorkflow, abortListenerWorkflow, cross-file step error
honoFatalError fails immediately without retries
tanstack-startabortHookOrderingWorkflow (×2), abortReasonWorkflow
fastifyabortHookOrderingWorkflow [hook-first-hook-first]
sveltekitFatalError fails immediately without retries
viteabortDeterministicBranchWorkflow, abortVoidSleepTimeoutWorkflow

The cluster is real though: AbortController + error-handling tests, which all do multiple sequential workflow round-trips and historically run close to the 60s budget.

Diagnosis

Default vitest `maxConcurrency` is 5. With five workflow runs hammering one workbench in parallel, the heavier multi-round-trip tests appear to queue past the per-test 60s budget. Local-Postgres/Prod/Dev also fail, so it's not Vercel-specific — workflow-server (local) and the in-process world also slow under contention.

Proposed next steps (separate PRs)

Holding off on bundling point (5) (shortening `sleep()` durations) into this PR — the suite isn't healthy enough to add another variable.

Three angles, in order of cheapest:

  1. Bump default `testTimeout` from 60s to ~180s (in `vitest.config.ts`). Cheap, but masks the queueing rather than fixing it. May allow individual tests to silently regress to 3× their real cost.
  2. Lower `maxConcurrency` to 3 (config-level or `describe.concurrent(name, { concurrent: 3 }, ...)`). Keeps most of the speedup, eases contention. Best first attempt IMO.
  3. Mark the error-handling and AbortController describes `.sequential` and leave the rest concurrent. Finest-grained, preserves max parallelism elsewhere, but requires per-block annotations.

Leaving this PR as draft for now. Happy to iterate as a follow-up.

The e2e suite tracked failed-run diagnostics via two module-level
mutables (`trackedRuns` and `currentTestName`) that every beforeEach
reset. Under describe.concurrent that races: when a test times out,
the displayed diagnostic typically belongs to whichever sibling task
most recently called setupRunTracking(). That's why PR #2083's first
CI run showed an unrelated `errorStepCrossFile` run as the
"diagnostic" for the FatalError catchability test that actually
timed out — leaving us blind to the real failure cause.
Switch to a `Map<taskId, PerTaskState>` keyed off
`getCurrentTest().id`, with the entry dropped via onTestFinished to
keep the map bounded. Also unconditionally log
`[trackRun] <test> → <runId>` so we can still correlate runs from
stdout even when diagnostics fetching itself stalls past the test's
deadline.
…Error type in readJSON
The previous writeExclusive used `fs.writeFile(path, data, { flag: 'wx' })`,
which is atomic for the file *creation* (O_CREAT|O_EXCL) but NOT for the
subsequent data write. A concurrent reader could catch the file with
zero bytes (or just `{`) before the writer's content reached the page
cache.
This race had been silently papered over by `readHookTokenClaim`, which
swallows any SyntaxError as "no claim exists" — which is wrong: a partial
write means a claim *does* exist, just not yet flushed, and treating it as
absent lets duplicate hook tokens through. PR #2083's concurrent e2e run
also surfaced the same race on runs/<id>.json creation, producing
size=0 byte run files in CI logs.
Fix: write content to a temp file first, then `link(2)` it atomically
into place. POSIX link is atomic and fails with EEXIST if the target
already exists, preserving the exclusive-claim semantics. Readers now
either see ENOENT or the fully-populated inode — never a half-written
file.
Also preserve the SyntaxError type when annotating readJSON failures
with file path / size / content snippet, so callers like
readHookTokenClaim that intentionally swallow `error instanceof SyntaxError`
continue to work (the previous diagnostic threw a plain Error and broke
them).
Vitest's default `maxConcurrency` of 5 saturates the workbench
preview deploys and the in-process world runtime under
`describe.concurrent`, pushing load-heavy tests (fibonacci tree
spawn, multi-step abort sequencing, AbortSignal.any composition)
into their per-test deadlines on PR #2083's run 4 even though
the same tests pass cleanly when run sequentially.
Drop to 3 in the root vitest config (only `test:e2e` and `bench`
consume it — per-package configs are untouched). The Vercel-deploy
matrix went from ~14-17min sequential → ~5-6min at concurrency 5;
trading some of that headroom for stability is the right call now
that the isolation/correctness fixes have landed.
Dropping vitest's default concurrency cap from 5 to 3 (commit c7060b1)
didn't reduce the remaining e2e flake count — runs 3/4 at default
concurrency=5 and run 5 at concurrency=3 all landed on exactly 15
failures. The remaining failures are external-network and source-map
class issues (httpbin/postman-echo abort tests, nuxt/nextjs-webpack
sourcemap assertions, fibonacci tree spawn timeouts) that lower test
parallelism doesn't address. Keeping the speedup is more valuable than
the small load reduction.
@VaguelySerious

Copy link
Copy Markdown
MemberAuthor

Update

Three substantive fixes layered on, then one revert. Final state of the PR:

What was fixed

  1. Per-task diagnostic isolation (a115774) — setupRunTracking tracked failed-run diagnostics via two module-level mutables (trackedRuns, currentTestName) that every beforeEach reset. Under describe.concurrent, every test clobbered every other's state, so the failing test's diagnostic typically showed an unrelated sibling's workflow. Switched to Map<taskId, PerTaskState> keyed off getCurrentTest().id. Also unconditionally log [trackRun] <test> → <runId> so even when diagnostics fetching stalls past a 60s timeout, the per-test runId is still in stdout for backend correlation.
  2. writeExclusive content atomicity (2fce0bb) — fs.writeFile(path, data, { flag: 'wx' }) is atomic for the file creation (O_CREAT|O_EXCL) but not for the data write that follows. A concurrent reader could observe a half-written file (e.g. just {), which the hook-token-claim path was silently swallowing as "no claim exists" — a real correctness bug. Run-creation hit the same race and produced size=0 run JSON files surfaced in CI logs. Switched to write-then-link(2): write data to a temp file first, then atomically link it into place. POSIX link is atomic and fails with EEXIST if the target exists, preserving the exclusive-claim semantics. Readers now either see ENOENT or the fully-populated inode.
  3. readJSON SyntaxError annotation (same commit) — annotates partial-read SyntaxError with file path + size + content snippet, preserving the SyntaxError type so existing handlers like readHookTokenClaim's instanceof SyntaxError catch still work.

What was tried and reverted

  • maxConcurrency: 3 (c7060b1 → reverted in 6e264eb) — runs 3/4 at concurrency=5 and run 5 at concurrency=3 all landed on exactly 15 e2e failures. The remaining flakes don't respond to lower test parallelism, so the cap traded speedup for nothing.

What's left (out of scope for this PR)

  • Sourcemap assertion failures on nuxt and nextjs-webpack: expect(stack).not.toContain('99_e2e.ts') fires because hasStepSourceMaps() reports false but the stack does contain the .ts filename. Pre-existing test/runtime mismatch — independent of concurrency, just hits more often now that more workflows run per CI invocation. Needs separate investigation into the source-map detection logic.
  • External-network flakes on abort tests (abortFetchInFlightWorkflow, abortFetchUncaughtWorkflow, etc.): httpbin/postman-echo delay endpoints intermittently return early from GH Actions runners. [tests] Fix abort-fetch e2e flake #2081 added a fallback URL pair; under heavier concurrent load both upstreams flake. The proper fix is a per-workbench /api/delay/:n route.
  • Fibonacci tree spawn timeouts on Vercel Prod: fibonacciWorkflow with fib(6) spawns a tree of child runs; under concurrent load this saturates the workflow scheduler past the test's 180s budget. Could be addressed with a smaller fib(N), a longer budget, or marking that single test .sequential.

Verdict

The actual concurrency-introduced isolation/correctness bugs are fixed. Concurrency speedup holds (~3× job wall-clock; ~4–5× pure vitest run). The remaining failures are pre-existing issues that the concurrency change has made more visible but doesn't itself cause. Happy to keep this draft until those classes are tackled, or to merge with a green CI rerun depending on appetite.

Sign up for freeto 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

@VaguelySerious
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Add copy buttons to all
 blocks\n(function() {\n function addCopyButtons() {\n document.querySelectorAll('pre code').forEach(function(codeBlock) {\n if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;\n codeBlock.parentElement.setAttribute('data-copy-added', 'true');\n \n var btn = document.createElement('button');\n btn.textContent = 'Copy';\n btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';\n btn.onmouseover = function() { this.style.opacity = '1'; };\n btn.onmouseout = function() { this.style.opacity = '0.7'; };\n btn.onclick = function() {\n navigator.clipboard.writeText(codeBlock.textContent).then(function() {\n btn.textContent = 'Copied!';\n setTimeout(function() { btn.textContent = 'Copy'; }, 1500);\n });\n };\n codeBlock.parentElement.style.position = 'relative';\n codeBlock.parentElement.appendChild(btn);\n });\n }\n \n addCopyButtons();\n \n // Re-run on dynamic content\n var observer = new MutationObserver(addCopyButtons);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Add Copy Buttons to Code Blocks");
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
Skip to content

[tests] Re-enable concurrent e2e suite - #2083

Closed
VaguelySerious wants to merge 6 commits into
mainfrom
peter/enable-e2e-concurrency
Closed

[tests] Re-enable concurrent e2e suite#2083
VaguelySerious wants to merge 6 commits into
mainfrom
peter/enable-e2e-concurrency

Conversation

@VaguelySerious

Copy link
Copy Markdown
Member

Summary

  • Re-enables describe.concurrent in packages/core/e2e/e2e.test.ts, which was made sequential in 78048e0 ("no concurrency") with a TODO to re-enable.
  • Each workbench currently runs ~87 e2e tests serially against its preview deploy; this is the dominant ~15-minute cost per matrix entry.
  • This is a draft to see which (if any) tests are still flake-prone under concurrency after recent fixes (e.g. the abort-fetch class of flakes in [tests] Fix abort-fetch e2e flake #2081).

Test plan

  • All workbench matrix entries (E2E Vercel Prod) pass
  • Compare wall-clock vs. recent main runs
  • If failures cluster around specific tests, mark them .sequential rather than reverting the whole flip

Notes

If concurrency exposes real isolation bugs (shared identifiers, cross-test state), I'll fix those before unwinding the draft state. If it's only known-flaky externals, those should already have been addressed in #2081 / follow-ups.

🤖 Generated with Claude Code

Flips packages/core/e2e/e2e.test.ts back to describe.concurrent. The
suite was made sequential ("temporarily disabling concurrent tests to
avoid flakiness") and never re-enabled. Each workbench now runs ~87
tests serially against its preview deploy, dominating CI wall-clock.
Putting this up as a draft to see which (if any) tests are still
flake-prone under concurrency, after recent fixes to the abort-fetch
class of flakes.
@vercel

vercelBot commented May 22, 2026

Copy link
Copy Markdown
Contributor

@changeset-bot

changeset-botBot commented May 22, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: c7060b1

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

This PR includes changesets to release 18 packages
NameType
@workflow/world-localPatch
@workflow/cliPatch
@workflow/corePatch
@workflow/vitestPatch
@workflow/world-postgresPatch
workflowPatch
@workflow/world-testingPatch
@workflow/buildersPatch
@workflow/nextPatch
@workflow/nitroPatch
@workflow/web-sharedPatch
@workflow/webPatch
@workflow/astroPatch
@workflow/nestPatch
@workflow/rollupPatch
@workflow/sveltekitPatch
@workflow/vitePatch
@workflow/nuxtPatch

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-actionsBot commented May 22, 2026

Copy link
Copy Markdown
Contributor

📊 Benchmark Results

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

workflow with no steps

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
💻 Local🥇 Nitro0.032s (-26.0% 🟢)1.006s (~)0.974s101.00x
💻 LocalExpress0.032s (-26.9% 🟢)1.006s (~)0.974s101.02x
💻 LocalNext.js (Turbopack)0.034s1.005s0.972s101.06x
🐘 PostgresExpress0.050s (-13.1% 🟢)1.012s (~)0.961s101.58x
🐘 PostgresNitro0.051s (-46.3% 🟢)1.013s (-2.9%)0.962s101.60x
🐘 PostgresNext.js (Turbopack)0.056s1.011s0.955s101.77x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Next.js (Turbopack)0.306s (+21.6% 🔺)2.546s (+9.1% 🔺)2.240s101.00x
▲ VercelNitro0.426s (+4.0%)2.554s (+1.8%)2.128s101.39x
▲ VercelExpress⚠️missing----

🔍 Observability: Next.js (Turbopack) | Nitro

workflow with 1 step

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
💻 Local🥇 Nitro1.076s (-4.9%)2.007s (~)0.931s101.00x
💻 LocalExpress1.079s (-4.1%)2.007s (~)0.928s101.00x
🐘 PostgresNitro1.082s (-5.1% 🟢)2.008s (~)0.926s101.01x
🐘 PostgresExpress1.087s (-5.2% 🟢)2.009s (~)0.922s101.01x
💻 LocalNext.js (Turbopack)1.089s2.005s0.916s101.01x
🐘 PostgresNext.js (Turbopack)1.117s2.008s0.891s101.04x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Nitro1.806s (-53.6% 🟢)3.829s (-35.2% 🟢)2.023s101.00x
▲ VercelNext.js (Turbopack)1.906s (-6.3% 🟢)3.724s (-2.8%)1.818s101.06x
▲ VercelExpress⚠️missing----

🔍 Observability: Nitro | Next.js (Turbopack)

workflow with 10 sequential steps

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Nitro10.387s (-4.4%)11.018s (~)0.630s31.00x
💻 LocalExpress10.434s (-4.5%)11.023s (~)0.590s31.00x
💻 LocalNitro10.434s (-4.7%)11.024s (~)0.590s31.00x
🐘 PostgresExpress10.435s (-4.8%)11.014s (~)0.579s31.00x
💻 LocalNext.js (Turbopack)10.493s11.021s0.528s31.01x
🐘 PostgresNext.js (Turbopack)10.702s11.020s0.318s31.03x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Nitro13.961s (-41.2% 🟢)16.231s (-35.4% 🟢)2.269s21.00x
▲ VercelNext.js (Turbopack)14.589s (-15.8% 🟢)16.525s (-14.8% 🟢)1.936s21.04x
▲ VercelExpress⚠️missing----

🔍 Observability: Nitro | Next.js (Turbopack)

workflow with 25 sequential steps

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Nitro13.428s (-8.0% 🟢)14.016s (-6.7% 🟢)0.588s51.00x
🐘 PostgresExpress13.500s (-7.4% 🟢)14.019s (-6.7% 🟢)0.518s51.01x
💻 LocalExpress13.527s (-9.6% 🟢)14.027s (-6.7% 🟢)0.500s51.01x
💻 LocalNitro13.592s (-9.8% 🟢)14.027s (-12.5% 🟢)0.435s51.01x
💻 LocalNext.js (Turbopack)13.761s14.025s0.264s51.02x
🐘 PostgresNext.js (Turbopack)14.104s15.019s0.915s41.05x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Nitro21.896s (-66.0% 🟢)23.781s (-64.3% 🟢)1.885s31.00x
▲ VercelNext.js (Turbopack)22.228s (-57.7% 🟢)24.764s (-54.7% 🟢)2.536s31.02x
▲ VercelExpress⚠️missing----

🔍 Observability: Nitro | Next.js (Turbopack)

workflow with 50 sequential steps

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Nitro11.854s (-15.1% 🟢)12.015s (-16.0% 🟢)0.161s81.00x
💻 LocalNitro11.923s (-29.0% 🟢)12.021s (-29.4% 🟢)0.098s81.01x
🐘 PostgresExpress11.984s (-14.4% 🟢)12.017s (-17.7% 🟢)0.033s81.01x
💻 LocalExpress12.021s (-27.6% 🟢)12.399s (-27.2% 🟢)0.379s81.01x
💻 LocalNext.js (Turbopack)12.282s13.022s0.740s71.04x
🐘 PostgresNext.js (Turbopack)13.164s14.015s0.851s71.11x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Nitro31.366s (-92.6% 🟢)33.410s (-92.1% 🟢)2.044s31.00x
▲ VercelNext.js (Turbopack)32.382s (-91.8% 🟢)34.323s (-91.3% 🟢)1.940s31.03x
▲ VercelExpress⚠️missing----

🔍 Observability: Nitro | Next.js (Turbopack)

Promise.all with 10 concurrent steps

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Nitro1.158s (-9.2% 🟢)2.009s (~)0.851s151.00x
🐘 PostgresExpress1.160s (-8.0% 🟢)2.008s (~)0.848s151.00x
💻 LocalNitro1.172s (-28.2% 🟢)2.006s (-3.3%)0.835s151.01x
💻 LocalExpress1.179s (-20.8% 🟢)2.007s (~)0.828s151.02x
💻 LocalNext.js (Turbopack)1.212s2.005s0.793s151.05x
🐘 PostgresNext.js (Turbopack)1.218s2.008s0.790s151.05x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Nitro2.877s (+2.1%)4.515s (+4.5%)1.638s81.00x
▲ VercelNext.js (Turbopack)2.967s (-12.7% 🟢)4.728s (-4.2%)1.761s71.03x
▲ VercelExpress⚠️missing----

🔍 Observability: Nitro | Next.js (Turbopack)

Promise.all with 25 concurrent steps

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Express1.212s (-48.6% 🟢)2.007s (-33.3% 🟢)0.795s151.00x
🐘 PostgresNitro1.224s (-47.9% 🟢)2.009s (-33.2% 🟢)0.785s151.01x
🐘 PostgresNext.js (Turbopack)1.336s2.007s0.671s151.10x
💻 LocalNext.js (Turbopack)1.524s2.004s0.480s151.26x
💻 LocalNitro1.744s (-44.5% 🟢)2.007s (-48.3% 🟢)0.264s151.44x
💻 LocalExpress1.835s (-37.8% 🟢)2.140s (-38.0% 🟢)0.304s151.51x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Next.js (Turbopack)4.146s (-41.6% 🟢)6.056s (-32.0% 🟢)1.910s51.00x
▲ VercelNitro5.247s (+29.5% 🔺)7.231s (+22.1% 🔺)1.984s51.27x
▲ VercelExpress⚠️missing----

🔍 Observability: Next.js (Turbopack) | Nitro

Promise.all with 50 concurrent steps

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Express1.339s (-61.6% 🟢)2.007s (-49.9% 🟢)0.669s151.00x
🐘 PostgresNitro1.358s (-61.0% 🟢)2.007s (-49.9% 🟢)0.649s151.01x
🐘 PostgresNext.js (Turbopack)1.617s2.007s0.390s151.21x
💻 LocalNext.js (Turbopack)3.177s3.453s0.276s92.37x
💻 LocalExpress5.435s (-34.8% 🟢)6.214s (-31.2% 🟢)0.779s54.06x
💻 LocalNitro5.468s (-34.5% 🟢)6.012s (-33.3% 🟢)0.544s54.08x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Nitro6.778s (+92.3% 🔺)8.495s (+53.5% 🔺)1.716s41.00x
▲ VercelNext.js (Turbopack)8.699s (-2.4%)11.031s (+0.6%)2.332s31.28x
▲ VercelExpress⚠️missing----

🔍 Observability: Nitro | Next.js (Turbopack)

Promise.race with 10 concurrent steps

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Express1.142s (-9.1% 🟢)2.009s (~)0.866s151.00x
🐘 PostgresNitro1.152s (-8.3% 🟢)2.008s (~)0.856s151.01x
🐘 PostgresNext.js (Turbopack)1.209s2.008s0.800s151.06x
💻 LocalNext.js (Turbopack)1.209s2.004s0.795s151.06x
💻 LocalNitro1.417s (-24.1% 🟢)2.006s (-14.3% 🟢)0.590s151.24x
💻 LocalExpress1.427s (-24.7% 🟢)2.006s (-15.1% 🟢)0.580s151.25x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Next.js (Turbopack)2.873s (-2.0%)4.428s (-4.6%)1.555s71.00x
▲ VercelNitro2.925s (+18.9% 🔺)5.006s (+20.1% 🔺)2.081s61.02x
▲ VercelExpress⚠️missing----

🔍 Observability: Next.js (Turbopack) | Nitro

Promise.race with 25 concurrent steps

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Nitro1.214s (-48.1% 🟢)2.008s (-33.3% 🟢)0.794s151.00x
🐘 PostgresExpress1.226s (-47.6% 🟢)2.009s (-33.3% 🟢)0.783s151.01x
🐘 PostgresNext.js (Turbopack)1.343s2.009s0.666s151.11x
💻 LocalNext.js (Turbopack)1.579s2.005s0.426s151.30x
💻 LocalNitro2.034s (-33.6% 🟢)2.510s (-35.4% 🟢)0.475s121.68x
💻 LocalExpress2.057s (-34.3% 🟢)2.508s (-33.3% 🟢)0.451s121.69x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Nitro3.368s (+4.2%)5.319s (+4.8%)1.951s61.00x
▲ VercelNext.js (Turbopack)4.111s (+30.8% 🔺)5.838s (+29.1% 🔺)1.726s61.22x
▲ VercelExpress⚠️missing----

🔍 Observability: Nitro | Next.js (Turbopack)

Promise.race with 50 concurrent steps

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Nitro1.316s (-62.2% 🟢)2.009s (-49.9% 🟢)0.693s151.00x
🐘 PostgresExpress1.334s (-61.9% 🟢)2.007s (-50.0% 🟢)0.673s151.01x
🐘 PostgresNext.js (Turbopack)1.644s2.008s0.364s151.25x
💻 LocalNext.js (Turbopack)3.454s4.012s0.558s82.62x
💻 LocalExpress5.760s (-34.6% 🟢)6.216s (-33.0% 🟢)0.456s54.38x
💻 LocalNitro5.993s (-34.5% 🟢)6.513s (-35.0% 🟢)0.521s64.55x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Next.js (Turbopack)5.982s (-11.5% 🟢)7.562s (-11.5% 🟢)1.580s41.00x
▲ VercelNitro6.436s (+26.4% 🔺)8.067s (+18.3% 🔺)1.632s41.08x
▲ VercelExpress⚠️missing----

🔍 Observability: Next.js (Turbopack) | Nitro

workflow with 10 sequential data payload steps (10KB)

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Nitro0.428s (-47.8% 🟢)1.006s (~)0.578s601.00x
🐘 PostgresExpress0.467s (-44.3% 🟢)1.006s (-1.7%)0.539s601.09x
💻 LocalNitro0.493s (-49.7% 🟢)1.022s (-6.6% 🟢)0.529s591.15x
💻 LocalExpress0.517s (-47.4% 🟢)1.022s (-5.0% 🟢)0.504s591.21x
💻 LocalNext.js (Turbopack)0.615s1.021s0.406s591.44x
🐘 PostgresNext.js (Turbopack)0.675s1.006s0.332s601.58x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Nitro5.901s (-73.2% 🟢)7.712s (-67.9% 🟢)1.811s81.00x
▲ VercelNext.js (Turbopack)6.006s (-58.6% 🟢)7.961s (-50.5% 🟢)1.956s81.02x
▲ VercelExpress⚠️missing----

🔍 Observability: Nitro | Next.js (Turbopack)

workflow with 25 sequential data payload steps (10KB)

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Nitro0.995s (-48.4% 🟢)1.350s (-35.7% 🟢)0.355s671.00x
🐘 PostgresExpress1.188s (-39.9% 🟢)1.922s (-14.9% 🟢)0.734s471.19x
💻 LocalNitro1.220s (-59.8% 🟢)2.006s (-46.6% 🟢)0.786s451.23x
💻 LocalExpress1.260s (-58.2% 🟢)2.007s (-44.0% 🟢)0.746s451.27x
💻 LocalNext.js (Turbopack)1.421s2.004s0.583s451.43x
🐘 PostgresNext.js (Turbopack)1.660s2.008s0.347s451.67x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Nitro16.140s (-59.1% 🟢)18.101s (-56.2% 🟢)1.961s61.00x
▲ VercelNext.js (Turbopack)16.668s (-66.5% 🟢)19.003s (-63.3% 🟢)2.335s51.03x
▲ VercelExpress⚠️missing----

🔍 Observability: Nitro | Next.js (Turbopack)

workflow with 50 sequential data payload steps (10KB)

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Nitro1.994s (-51.4% 🟢)2.335s (-49.3% 🟢)0.341s521.00x
🐘 PostgresExpress2.106s (-47.2% 🟢)2.697s (-38.3% 🟢)0.591s451.06x
💻 LocalNitro2.786s (-70.0% 🟢)3.058s (-69.5% 🟢)0.272s401.40x
💻 LocalExpress2.801s (-69.6% 🟢)3.033s (-69.7% 🟢)0.231s401.41x
🐘 PostgresNext.js (Turbopack)3.212s4.009s0.797s301.61x
💻 LocalNext.js (Turbopack)3.466s4.074s0.608s301.74x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Nitro27.620s (-71.5% 🟢)29.816s (-69.7% 🟢)2.196s51.00x
▲ VercelNext.js (Turbopack)32.787s (-69.4% 🟢)35.186s (-67.7% 🟢)2.399s41.19x
▲ VercelExpress⚠️missing----

🔍 Observability: Nitro | Next.js (Turbopack)

workflow with 10 concurrent data payload steps (10KB)

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Nitro0.190s (-33.1% 🟢)1.006s (~)0.816s601.00x
🐘 PostgresExpress0.198s (-30.0% 🟢)1.023s (+1.5%)0.825s591.04x
🐘 PostgresNext.js (Turbopack)0.230s1.006s0.776s601.21x
💻 LocalNext.js (Turbopack)0.337s1.003s0.666s601.78x
💻 LocalExpress0.381s (-32.0% 🟢)1.004s (~)0.623s602.01x
💻 LocalNitro0.399s (-34.0% 🟢)1.004s (-1.7%)0.605s602.11x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Nitro2.453s (+47.7% 🔺)4.362s (+30.2% 🔺)1.909s141.00x
▲ VercelNext.js (Turbopack)2.813s (+39.1% 🔺)4.846s (+27.8% 🔺)2.034s131.15x
▲ VercelExpress⚠️missing----

🔍 Observability: Nitro | Next.js (Turbopack)

workflow with 25 concurrent data payload steps (10KB)

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Nitro0.284s (-42.8% 🟢)1.006s (~)0.722s901.00x
🐘 PostgresExpress0.308s (-39.6% 🟢)1.006s (~)0.698s901.08x
🐘 PostgresNext.js (Turbopack)0.435s1.006s0.571s901.53x
💻 LocalNext.js (Turbopack)1.433s2.028s0.595s455.05x
💻 LocalExpress2.159s (-14.1% 🟢)2.715s (-9.8% 🟢)0.556s347.60x
💻 LocalNitro2.171s (-14.5% 🟢)2.884s (-4.1%)0.713s327.65x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Nitro5.296s (+64.2% 🔺)6.881s (+42.7% 🔺)1.585s141.00x
▲ VercelNext.js (Turbopack)6.720s (+90.1% 🔺)8.608s (+65.8% 🔺)1.888s111.27x
▲ VercelExpress⚠️missing----

🔍 Observability: Nitro | Next.js (Turbopack)

workflow with 50 concurrent data payload steps (10KB)

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Nitro0.583s (-26.2% 🟢)1.006s (~)0.422s1201.00x
🐘 PostgresExpress0.652s (-20.4% 🟢)1.006s (-1.1%)0.355s1201.12x
🐘 PostgresNext.js (Turbopack)0.890s1.078s0.188s1121.53x
💻 LocalNext.js (Turbopack)6.974s7.706s0.731s1611.96x
💻 LocalNitro10.196s (-8.9% 🟢)10.777s (-7.6% 🟢)0.581s1217.48x
💻 LocalExpress10.526s (-5.9% 🟢)11.031s (-7.6% 🟢)0.505s1118.04x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Next.js (Turbopack)15.606s (+51.1% 🔺)17.743s (+44.4% 🔺)2.137s71.00x
▲ VercelNitro16.197s (+109.7% 🔺)18.211s (+93.7% 🔺)2.014s71.04x
▲ VercelExpress⚠️missing----

🔍 Observability: Next.js (Turbopack) | Nitro

Stream Benchmarks(includes TTFB metrics)
workflow with stream

💻 Local Development

WorldFrameworkWorkflow TimeTTFBSlurpWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Nitro1.128s (+450.3% 🔺)2.001s (+100.2% 🔺)0.001s (-33.3% 🟢)2.010s (+98.8% 🔺)0.882s101.00x
💻 LocalNext.js (Turbopack)1.129s2.002s0.006s2.011s0.882s101.00x
💻 LocalNitro1.133s (+430.0% 🔺)2.005s (+99.6% 🔺)0.013s (+4.0%)2.020s (+98.3% 🔺)0.888s101.00x
💻 LocalExpress1.138s (+471.7% 🔺)2.006s (+99.7% 🔺)0.013s (+5.8% 🔺)2.021s (+98.5% 🔺)0.883s101.01x
🐘 PostgresExpress1.145s (+458.3% 🔺)2.001s (+100.4% 🔺)0.001s (-18.8% 🟢)2.010s (+98.7% 🔺)0.865s101.01x
🐘 PostgresNext.js (Turbopack)1.201s2.001s0.002s2.010s0.809s101.06x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeTTFBSlurpWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Next.js (Turbopack)2.444s (-64.3% 🟢)3.510s (-59.4% 🟢)2.280s (+260.8% 🔺)6.380s (-34.8% 🟢)3.936s101.00x
▲ VercelNitro2.490s (-35.0% 🟢)3.601s (-31.8% 🟢)13.675s (+1742.7% 🔺)17.941s (+176.7% 🔺)15.451s101.02x
▲ VercelExpress⚠️missing-----

🔍 Observability: Next.js (Turbopack) | Nitro

stream pipeline with 5 transform steps (1MB)

💻 Local Development

WorldFrameworkWorkflow TimeTTFBSlurpWall TimeOverheadSamplesvs Fastest
💻 Local🥇 Next.js (Turbopack)1.475s2.007s0.006s2.015s0.540s301.00x
🐘 PostgresNitro1.494s (+139.3% 🔺)2.002s (+98.9% 🔺)0.004s (+6.4% 🔺)2.027s (+98.3% 🔺)0.534s301.01x
💻 LocalNitro1.541s (+83.7% 🔺)2.013s (+98.9% 🔺)0.010s (+4.9%)2.025s (+81.5% 🔺)0.484s301.04x
🐘 PostgresExpress1.543s (+144.9% 🔺)2.004s (+99.1% 🔺)0.004s (+3.6%)2.028s (+98.2% 🔺)0.484s301.05x
💻 LocalExpress1.549s (+104.6% 🔺)2.013s (+95.7% 🔺)0.010s (+9.4% 🔺)2.026s (+94.8% 🔺)0.476s301.05x
🐘 PostgresNext.js (Turbopack)1.678s2.010s0.004s2.025s0.347s301.14x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeTTFBSlurpWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Next.js (Turbopack)5.989s (-64.6% 🟢)7.269s (-60.1% 🟢)0.284s (+34.4% 🔺)8.226s (-56.6% 🟢)2.237s81.00x
▲ VercelNitro6.280s (-78.7% 🟢)7.470s (-75.8% 🟢)0.267s (+138.6% 🔺)8.303s (-73.9% 🟢)2.023s81.05x
▲ VercelExpress⚠️missing-----

🔍 Observability: Next.js (Turbopack) | Nitro

10 parallel streams (1MB each)

💻 Local Development

WorldFrameworkWorkflow TimeTTFBSlurpWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Nitro0.640s (-34.0% 🟢)1.012s (-18.8% 🟢)0.000s (+22.0% 🔺)1.030s (-18.1% 🟢)0.391s591.00x
🐘 PostgresExpress0.664s (-30.9% 🟢)1.011s (-20.9% 🟢)0.000s (-61.0% 🟢)1.032s (-21.0% 🟢)0.369s591.04x
🐘 PostgresNext.js (Turbopack)0.766s1.017s0.000s1.032s0.266s591.20x
💻 LocalNext.js (Turbopack)1.042s1.631s0.000s1.634s0.592s371.63x
💻 LocalExpress1.344s (+9.7% 🔺)2.016s (~)0.000s (-30.0% 🟢)2.018s (~)0.674s302.10x
💻 LocalNitro1.373s (+12.3% 🔺)2.016s (~)0.000s (+66.7% 🔺)2.018s (~)0.645s302.15x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeTTFBSlurpWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Nitro3.957s (+29.7% 🔺)5.319s (+21.1% 🔺)0.000s (+18.2% 🔺)5.894s (+22.6% 🔺)1.937s111.00x
▲ VercelNext.js (Turbopack)3.981s (-60.9% 🟢)5.285s (-54.1% 🟢)0.000s (+Infinity% 🔺)5.843s (-51.5% 🟢)1.862s111.01x
▲ VercelExpress⚠️missing-----

🔍 Observability: Nitro | Next.js (Turbopack)

fan-out fan-in 10 streams (1MB each)

💻 Local Development

WorldFrameworkWorkflow TimeTTFBSlurpWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Nitro1.278s (-28.7% 🟢)2.029s (-5.2% 🟢)0.000s (-100.0% 🟢)2.049s (-5.7% 🟢)0.771s301.00x
🐘 PostgresExpress1.384s (-21.9% 🟢)2.029s (-6.8% 🟢)0.000s (+Infinity% 🔺)2.044s (-7.1% 🟢)0.659s301.08x
🐘 PostgresNext.js (Turbopack)1.500s2.145s0.000s2.153s0.654s281.17x
💻 LocalNext.js (Turbopack)2.061s2.523s0.000s2.528s0.467s241.61x
💻 LocalExpress3.109s (-10.3% 🟢)3.781s (-6.3% 🟢)0.001s (-21.9% 🟢)3.784s (-6.3% 🟢)0.675s162.43x
💻 LocalNitro3.183s (-6.0% 🟢)3.903s (-3.2%)0.000s (-18.0% 🟢)3.906s (-3.2%)0.723s162.49x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeTTFBSlurpWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Nitro5.980s (+46.1% 🔺)7.651s (+42.4% 🔺)0.000s (-54.2% 🟢)8.181s (+41.2% 🔺)2.201s81.00x
▲ VercelNext.js (Turbopack)6.105s (+8.7% 🔺)7.426s (+6.4% 🔺)0.000s (~)7.952s (+5.5% 🔺)1.847s81.02x
▲ VercelExpress⚠️missing-----

🔍 Observability: Nitro | Next.js (Turbopack)

Summary

Fastest Framework by World

Winner determined by most benchmark wins

World🥇 Fastest FrameworkWins
💻 LocalNext.js (Turbopack)12/21
🐘 PostgresNitro17/21
▲ VercelNitro14/21
Fastest World by Framework

Winner determined by most benchmark wins

Framework🥇 Fastest WorldWins
Express🐘 Postgres17/21
Next.js (Turbopack)🐘 Postgres11/21
Nitro🐘 Postgres19/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)

📋 View full workflow run


Some benchmark jobs failed:

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

Check the workflow run for details.

@github-actions

github-actionsBot commented May 22, 2026

Copy link
Copy Markdown
Contributor

🧪 E2E Test Results

Some tests failed

Summary

PassedFailedSkippedTotal
❌ ▲ Vercel Production121932191441
❌ 💻 Local Development161052191834
❌ 📦 Local Production161412191834
❌ 🐘 Local Postgres150212001703
✅ 🪟 Windows13100131
❌ 📋 Other7401176917
Total68161110337860

❌ Failed Tests

▲ Vercel Production (3 failed)

nitro (1 failed):

  • outputStreamInsideStepWorkflow - getWritable() called inside step functions | wrun_01KS7ZJGW73X7CKYTEQBGY87E5 | 🔍 observability

nuxt (1 failed):

  • hookWorkflow is not resumable via public webhook endpoint | wrun_01KS7ZJ3YXC4E16B345GAMR59H | 🔍 observability

sveltekit (1 failed):

💻 Local Development (5 failed)

astro-stable (2 failed):

  • fibonacciWorkflow - recursive workflow composition via start() | wrun_01KS7ZKMSDH7F5ZJHKWBKZDJPQ
  • AbortController abortExternalSignalInFlightWorkflow: external abort fires mid-flight, propagates to nested steps

nextjs-webpack-stable-lazy-discovery-disabled (1 failed):

  • error handling error propagation step errors basic step error preserves message and stack trace

nuxt-stable (1 failed):

  • AbortController abortExternalSignalInFlightWorkflow: external abort fires mid-flight, propagates to nested steps

sveltekit-stable (1 failed):

  • AbortController abortExternalSignalInFlightWorkflow: external abort fires mid-flight, propagates to nested steps
📦 Local Production (1 failed)

nuxt-stable (1 failed):

  • error handling error propagation step errors basic step error preserves message and stack trace
🐘 Local Postgres (1 failed)

nuxt-stable (1 failed):

  • error handling error propagation step errors basic step error preserves message and stack trace
📋 Other (1 failed)

e2e-local-dev-tanstack-start- (1 failed):

  • AbortController abortExternalSignalInFlightWorkflow: external abort fires mid-flight, propagates to nested steps

Details by Category

❌ ▲ Vercel Production
AppPassedFailedSkipped
✅ astro105026
✅ example105026
✅ express105026
✅ fastify105026
✅ hono105026
✅ nextjs-turbopack12902
✅ nextjs-webpack12902
❌ nitro104126
❌ nuxt104126
❌ sveltekit12317
✅ vite105026
❌ 💻 Local Development
AppPassedFailedSkipped
❌ astro-stable104225
✅ express-stable106025
✅ fastify-stable106025
✅ hono-stable106025
✅ nextjs-turbopack-canary112019
✅ nextjs-turbopack-stable-lazy-discovery-disabled13100
✅ nextjs-turbopack-stable-lazy-discovery-enabled13100
✅ nextjs-webpack-canary112019
❌ nextjs-webpack-stable-lazy-discovery-disabled13010
✅ nextjs-webpack-stable-lazy-discovery-enabled13100
✅ nitro-stable106025
❌ nuxt-stable105125
❌ sveltekit-stable12416
✅ vite-stable106025
❌ 📦 Local Production
AppPassedFailedSkipped
✅ astro-stable106025
✅ express-stable106025
✅ fastify-stable106025
✅ hono-stable106025
✅ nextjs-turbopack-canary112019
✅ nextjs-turbopack-stable-lazy-discovery-disabled13100
✅ nextjs-turbopack-stable-lazy-discovery-enabled13100
✅ nextjs-webpack-canary112019
✅ nextjs-webpack-stable-lazy-discovery-disabled13100
✅ nextjs-webpack-stable-lazy-discovery-enabled13100
✅ nitro-stable106025
❌ nuxt-stable105125
✅ sveltekit-stable12506
✅ vite-stable106025
❌ 🐘 Local Postgres
AppPassedFailedSkipped
✅ astro-stable106025
✅ express-stable106025
✅ fastify-stable106025
✅ hono-stable106025
✅ nextjs-turbopack-stable-lazy-discovery-disabled13100
✅ nextjs-turbopack-stable-lazy-discovery-enabled13100
✅ nextjs-webpack-canary112019
✅ nextjs-webpack-stable-lazy-discovery-disabled13100
✅ nextjs-webpack-stable-lazy-discovery-enabled13100
✅ nitro-stable106025
❌ nuxt-stable105125
✅ sveltekit-stable12506
✅ vite-stable106025
✅ 🪟 Windows
AppPassedFailedSkipped
✅ nextjs-turbopack13100
❌ 📋 Other
AppPassedFailedSkipped
✅ e2e-local-dev-nest-stable106025
❌ e2e-local-dev-tanstack-start-105125
✅ e2e-local-postgres-nest-stable106025
✅ e2e-local-postgres-tanstack-start-106025
✅ e2e-local-prod-nest-stable106025
✅ e2e-local-prod-tanstack-start-106025
✅ e2e-vercel-prod-tanstack-start105026

📋 View full workflow run


Some E2E test jobs failed:

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

Check the workflow run for details.

@VaguelySerious

Copy link
Copy Markdown
MemberAuthor

CI verdict

Speedup is real. Per-workbench wall-clock dropped roughly 3×: from ~14–17 min on main to ~5–6 min here. Vitest's own run is even more dramatic — express went from ~15 min to 203s (~4–5× on the actual test phase).

But correctness regressed. 25 checks failed: every `E2E Vercel Prod Tests (*)` matrix entry except `nitro`, plus several `E2E Local Dev/Prod/Postgres` jobs and `E2E Windows Tests`.

Failure pattern

Every failure I sampled is the same shape: `Error: Test timed out in 60000ms`. The which tests fail varies per workbench — i.e. it's not a single broken test:

WorkbenchFailed test
expresserror handling > catchability > FatalError can be caught… (×2)
nuxtabortThrowIfAbortedWorkflow, step throw round-trips FatalError
astroabortDeterministicBranchFromStepWorkflow, abortListenerWorkflow, cross-file step error
honoFatalError fails immediately without retries
tanstack-startabortHookOrderingWorkflow (×2), abortReasonWorkflow
fastifyabortHookOrderingWorkflow [hook-first-hook-first]
sveltekitFatalError fails immediately without retries
viteabortDeterministicBranchWorkflow, abortVoidSleepTimeoutWorkflow

The cluster is real though: AbortController + error-handling tests, which all do multiple sequential workflow round-trips and historically run close to the 60s budget.

Diagnosis

Default vitest `maxConcurrency` is 5. With five workflow runs hammering one workbench in parallel, the heavier multi-round-trip tests appear to queue past the per-test 60s budget. Local-Postgres/Prod/Dev also fail, so it's not Vercel-specific — workflow-server (local) and the in-process world also slow under contention.

Proposed next steps (separate PRs)

Holding off on bundling point (5) (shortening `sleep()` durations) into this PR — the suite isn't healthy enough to add another variable.

Three angles, in order of cheapest:

  1. Bump default `testTimeout` from 60s to ~180s (in `vitest.config.ts`). Cheap, but masks the queueing rather than fixing it. May allow individual tests to silently regress to 3× their real cost.
  2. Lower `maxConcurrency` to 3 (config-level or `describe.concurrent(name, { concurrent: 3 }, ...)`). Keeps most of the speedup, eases contention. Best first attempt IMO.
  3. Mark the error-handling and AbortController describes `.sequential` and leave the rest concurrent. Finest-grained, preserves max parallelism elsewhere, but requires per-block annotations.

Leaving this PR as draft for now. Happy to iterate as a follow-up.

The e2e suite tracked failed-run diagnostics via two module-level
mutables (`trackedRuns` and `currentTestName`) that every beforeEach
reset. Under describe.concurrent that races: when a test times out,
the displayed diagnostic typically belongs to whichever sibling task
most recently called setupRunTracking(). That's why PR #2083's first
CI run showed an unrelated `errorStepCrossFile` run as the
"diagnostic" for the FatalError catchability test that actually
timed out — leaving us blind to the real failure cause.
Switch to a `Map<taskId, PerTaskState>` keyed off
`getCurrentTest().id`, with the entry dropped via onTestFinished to
keep the map bounded. Also unconditionally log
`[trackRun] <test> → <runId>` so we can still correlate runs from
stdout even when diagnostics fetching itself stalls past the test's
deadline.
…Error type in readJSON
The previous writeExclusive used `fs.writeFile(path, data, { flag: 'wx' })`,
which is atomic for the file *creation* (O_CREAT|O_EXCL) but NOT for the
subsequent data write. A concurrent reader could catch the file with
zero bytes (or just `{`) before the writer's content reached the page
cache.
This race had been silently papered over by `readHookTokenClaim`, which
swallows any SyntaxError as "no claim exists" — which is wrong: a partial
write means a claim *does* exist, just not yet flushed, and treating it as
absent lets duplicate hook tokens through. PR #2083's concurrent e2e run
also surfaced the same race on runs/<id>.json creation, producing
size=0 byte run files in CI logs.
Fix: write content to a temp file first, then `link(2)` it atomically
into place. POSIX link is atomic and fails with EEXIST if the target
already exists, preserving the exclusive-claim semantics. Readers now
either see ENOENT or the fully-populated inode — never a half-written
file.
Also preserve the SyntaxError type when annotating readJSON failures
with file path / size / content snippet, so callers like
readHookTokenClaim that intentionally swallow `error instanceof SyntaxError`
continue to work (the previous diagnostic threw a plain Error and broke
them).
Vitest's default `maxConcurrency` of 5 saturates the workbench
preview deploys and the in-process world runtime under
`describe.concurrent`, pushing load-heavy tests (fibonacci tree
spawn, multi-step abort sequencing, AbortSignal.any composition)
into their per-test deadlines on PR #2083's run 4 even though
the same tests pass cleanly when run sequentially.
Drop to 3 in the root vitest config (only `test:e2e` and `bench`
consume it — per-package configs are untouched). The Vercel-deploy
matrix went from ~14-17min sequential → ~5-6min at concurrency 5;
trading some of that headroom for stability is the right call now
that the isolation/correctness fixes have landed.
Dropping vitest's default concurrency cap from 5 to 3 (commit c7060b1)
didn't reduce the remaining e2e flake count — runs 3/4 at default
concurrency=5 and run 5 at concurrency=3 all landed on exactly 15
failures. The remaining failures are external-network and source-map
class issues (httpbin/postman-echo abort tests, nuxt/nextjs-webpack
sourcemap assertions, fibonacci tree spawn timeouts) that lower test
parallelism doesn't address. Keeping the speedup is more valuable than
the small load reduction.
@VaguelySerious

Copy link
Copy Markdown
MemberAuthor

Update

Three substantive fixes layered on, then one revert. Final state of the PR:

What was fixed

  1. Per-task diagnostic isolation (a115774) — setupRunTracking tracked failed-run diagnostics via two module-level mutables (trackedRuns, currentTestName) that every beforeEach reset. Under describe.concurrent, every test clobbered every other's state, so the failing test's diagnostic typically showed an unrelated sibling's workflow. Switched to Map<taskId, PerTaskState> keyed off getCurrentTest().id. Also unconditionally log [trackRun] <test> → <runId> so even when diagnostics fetching stalls past a 60s timeout, the per-test runId is still in stdout for backend correlation.
  2. writeExclusive content atomicity (2fce0bb) — fs.writeFile(path, data, { flag: 'wx' }) is atomic for the file creation (O_CREAT|O_EXCL) but not for the data write that follows. A concurrent reader could observe a half-written file (e.g. just {), which the hook-token-claim path was silently swallowing as "no claim exists" — a real correctness bug. Run-creation hit the same race and produced size=0 run JSON files surfaced in CI logs. Switched to write-then-link(2): write data to a temp file first, then atomically link it into place. POSIX link is atomic and fails with EEXIST if the target exists, preserving the exclusive-claim semantics. Readers now either see ENOENT or the fully-populated inode.
  3. readJSON SyntaxError annotation (same commit) — annotates partial-read SyntaxError with file path + size + content snippet, preserving the SyntaxError type so existing handlers like readHookTokenClaim's instanceof SyntaxError catch still work.

What was tried and reverted

  • maxConcurrency: 3 (c7060b1 → reverted in 6e264eb) — runs 3/4 at concurrency=5 and run 5 at concurrency=3 all landed on exactly 15 e2e failures. The remaining flakes don't respond to lower test parallelism, so the cap traded speedup for nothing.

What's left (out of scope for this PR)

  • Sourcemap assertion failures on nuxt and nextjs-webpack: expect(stack).not.toContain('99_e2e.ts') fires because hasStepSourceMaps() reports false but the stack does contain the .ts filename. Pre-existing test/runtime mismatch — independent of concurrency, just hits more often now that more workflows run per CI invocation. Needs separate investigation into the source-map detection logic.
  • External-network flakes on abort tests (abortFetchInFlightWorkflow, abortFetchUncaughtWorkflow, etc.): httpbin/postman-echo delay endpoints intermittently return early from GH Actions runners. [tests] Fix abort-fetch e2e flake #2081 added a fallback URL pair; under heavier concurrent load both upstreams flake. The proper fix is a per-workbench /api/delay/:n route.
  • Fibonacci tree spawn timeouts on Vercel Prod: fibonacciWorkflow with fib(6) spawns a tree of child runs; under concurrent load this saturates the workflow scheduler past the test's 180s budget. Could be addressed with a smaller fib(N), a longer budget, or marking that single test .sequential.

Verdict

The actual concurrency-introduced isolation/correctness bugs are fixed. Concurrency speedup holds (~3× job wall-clock; ~4–5× pure vitest run). The remaining failures are pre-existing issues that the concurrency change has made more visible but doesn't itself cause. Happy to keep this draft until those classes are tackled, or to merge with a green CI rerun depending on appetite.

Sign up for freeto 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

@VaguelySerious
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Force GitHub README to respect dark mode\n(function() {\n var style = document.createElement('style');\n style.textContent = '\n .markdown-body {\n color-scheme: dark light;\n }\n .markdown-body pre { background: #161b22 !important; }\n .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; }\n .markdown-body table th, .markdown-body table td { border-color: #30363d !important; }\n .markdown-body img { background: #0d1117; }\n .markdown-body blockquote { border-left-color: #8b949e; }\n .markdown-body hr { border-color: #30363d; }\n ';\n document.head.appendChild(style);\n})();", "GitHub Dark Mode README Fix"); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

[tests] Re-enable concurrent e2e suite - #2083

Closed
VaguelySerious wants to merge 6 commits into
mainfrom
peter/enable-e2e-concurrency
Closed

[tests] Re-enable concurrent e2e suite#2083
VaguelySerious wants to merge 6 commits into
mainfrom
peter/enable-e2e-concurrency

Conversation

@VaguelySerious

Copy link
Copy Markdown
Member

Summary

  • Re-enables describe.concurrent in packages/core/e2e/e2e.test.ts, which was made sequential in 78048e0 ("no concurrency") with a TODO to re-enable.
  • Each workbench currently runs ~87 e2e tests serially against its preview deploy; this is the dominant ~15-minute cost per matrix entry.
  • This is a draft to see which (if any) tests are still flake-prone under concurrency after recent fixes (e.g. the abort-fetch class of flakes in [tests] Fix abort-fetch e2e flake #2081).

Test plan

  • All workbench matrix entries (E2E Vercel Prod) pass
  • Compare wall-clock vs. recent main runs
  • If failures cluster around specific tests, mark them .sequential rather than reverting the whole flip

Notes

If concurrency exposes real isolation bugs (shared identifiers, cross-test state), I'll fix those before unwinding the draft state. If it's only known-flaky externals, those should already have been addressed in #2081 / follow-ups.

🤖 Generated with Claude Code

Flips packages/core/e2e/e2e.test.ts back to describe.concurrent. The
suite was made sequential ("temporarily disabling concurrent tests to
avoid flakiness") and never re-enabled. Each workbench now runs ~87
tests serially against its preview deploy, dominating CI wall-clock.
Putting this up as a draft to see which (if any) tests are still
flake-prone under concurrency, after recent fixes to the abort-fetch
class of flakes.
@vercel

vercelBot commented May 22, 2026

Copy link
Copy Markdown
Contributor

@changeset-bot

changeset-botBot commented May 22, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: c7060b1

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

This PR includes changesets to release 18 packages
NameType
@workflow/world-localPatch
@workflow/cliPatch
@workflow/corePatch
@workflow/vitestPatch
@workflow/world-postgresPatch
workflowPatch
@workflow/world-testingPatch
@workflow/buildersPatch
@workflow/nextPatch
@workflow/nitroPatch
@workflow/web-sharedPatch
@workflow/webPatch
@workflow/astroPatch
@workflow/nestPatch
@workflow/rollupPatch
@workflow/sveltekitPatch
@workflow/vitePatch
@workflow/nuxtPatch

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-actionsBot commented May 22, 2026

Copy link
Copy Markdown
Contributor

📊 Benchmark Results

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

workflow with no steps

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
💻 Local🥇 Nitro0.032s (-26.0% 🟢)1.006s (~)0.974s101.00x
💻 LocalExpress0.032s (-26.9% 🟢)1.006s (~)0.974s101.02x
💻 LocalNext.js (Turbopack)0.034s1.005s0.972s101.06x
🐘 PostgresExpress0.050s (-13.1% 🟢)1.012s (~)0.961s101.58x
🐘 PostgresNitro0.051s (-46.3% 🟢)1.013s (-2.9%)0.962s101.60x
🐘 PostgresNext.js (Turbopack)0.056s1.011s0.955s101.77x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Next.js (Turbopack)0.306s (+21.6% 🔺)2.546s (+9.1% 🔺)2.240s101.00x
▲ VercelNitro0.426s (+4.0%)2.554s (+1.8%)2.128s101.39x
▲ VercelExpress⚠️missing----

🔍 Observability: Next.js (Turbopack) | Nitro

workflow with 1 step

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
💻 Local🥇 Nitro1.076s (-4.9%)2.007s (~)0.931s101.00x
💻 LocalExpress1.079s (-4.1%)2.007s (~)0.928s101.00x
🐘 PostgresNitro1.082s (-5.1% 🟢)2.008s (~)0.926s101.01x
🐘 PostgresExpress1.087s (-5.2% 🟢)2.009s (~)0.922s101.01x
💻 LocalNext.js (Turbopack)1.089s2.005s0.916s101.01x
🐘 PostgresNext.js (Turbopack)1.117s2.008s0.891s101.04x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Nitro1.806s (-53.6% 🟢)3.829s (-35.2% 🟢)2.023s101.00x
▲ VercelNext.js (Turbopack)1.906s (-6.3% 🟢)3.724s (-2.8%)1.818s101.06x
▲ VercelExpress⚠️missing----

🔍 Observability: Nitro | Next.js (Turbopack)

workflow with 10 sequential steps

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Nitro10.387s (-4.4%)11.018s (~)0.630s31.00x
💻 LocalExpress10.434s (-4.5%)11.023s (~)0.590s31.00x
💻 LocalNitro10.434s (-4.7%)11.024s (~)0.590s31.00x
🐘 PostgresExpress10.435s (-4.8%)11.014s (~)0.579s31.00x
💻 LocalNext.js (Turbopack)10.493s11.021s0.528s31.01x
🐘 PostgresNext.js (Turbopack)10.702s11.020s0.318s31.03x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Nitro13.961s (-41.2% 🟢)16.231s (-35.4% 🟢)2.269s21.00x
▲ VercelNext.js (Turbopack)14.589s (-15.8% 🟢)16.525s (-14.8% 🟢)1.936s21.04x
▲ VercelExpress⚠️missing----

🔍 Observability: Nitro | Next.js (Turbopack)

workflow with 25 sequential steps

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Nitro13.428s (-8.0% 🟢)14.016s (-6.7% 🟢)0.588s51.00x
🐘 PostgresExpress13.500s (-7.4% 🟢)14.019s (-6.7% 🟢)0.518s51.01x
💻 LocalExpress13.527s (-9.6% 🟢)14.027s (-6.7% 🟢)0.500s51.01x
💻 LocalNitro13.592s (-9.8% 🟢)14.027s (-12.5% 🟢)0.435s51.01x
💻 LocalNext.js (Turbopack)13.761s14.025s0.264s51.02x
🐘 PostgresNext.js (Turbopack)14.104s15.019s0.915s41.05x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Nitro21.896s (-66.0% 🟢)23.781s (-64.3% 🟢)1.885s31.00x
▲ VercelNext.js (Turbopack)22.228s (-57.7% 🟢)24.764s (-54.7% 🟢)2.536s31.02x
▲ VercelExpress⚠️missing----

🔍 Observability: Nitro | Next.js (Turbopack)

workflow with 50 sequential steps

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Nitro11.854s (-15.1% 🟢)12.015s (-16.0% 🟢)0.161s81.00x
💻 LocalNitro11.923s (-29.0% 🟢)12.021s (-29.4% 🟢)0.098s81.01x
🐘 PostgresExpress11.984s (-14.4% 🟢)12.017s (-17.7% 🟢)0.033s81.01x
💻 LocalExpress12.021s (-27.6% 🟢)12.399s (-27.2% 🟢)0.379s81.01x
💻 LocalNext.js (Turbopack)12.282s13.022s0.740s71.04x
🐘 PostgresNext.js (Turbopack)13.164s14.015s0.851s71.11x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Nitro31.366s (-92.6% 🟢)33.410s (-92.1% 🟢)2.044s31.00x
▲ VercelNext.js (Turbopack)32.382s (-91.8% 🟢)34.323s (-91.3% 🟢)1.940s31.03x
▲ VercelExpress⚠️missing----

🔍 Observability: Nitro | Next.js (Turbopack)

Promise.all with 10 concurrent steps

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Nitro1.158s (-9.2% 🟢)2.009s (~)0.851s151.00x
🐘 PostgresExpress1.160s (-8.0% 🟢)2.008s (~)0.848s151.00x
💻 LocalNitro1.172s (-28.2% 🟢)2.006s (-3.3%)0.835s151.01x
💻 LocalExpress1.179s (-20.8% 🟢)2.007s (~)0.828s151.02x
💻 LocalNext.js (Turbopack)1.212s2.005s0.793s151.05x
🐘 PostgresNext.js (Turbopack)1.218s2.008s0.790s151.05x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Nitro2.877s (+2.1%)4.515s (+4.5%)1.638s81.00x
▲ VercelNext.js (Turbopack)2.967s (-12.7% 🟢)4.728s (-4.2%)1.761s71.03x
▲ VercelExpress⚠️missing----

🔍 Observability: Nitro | Next.js (Turbopack)

Promise.all with 25 concurrent steps

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Express1.212s (-48.6% 🟢)2.007s (-33.3% 🟢)0.795s151.00x
🐘 PostgresNitro1.224s (-47.9% 🟢)2.009s (-33.2% 🟢)0.785s151.01x
🐘 PostgresNext.js (Turbopack)1.336s2.007s0.671s151.10x
💻 LocalNext.js (Turbopack)1.524s2.004s0.480s151.26x
💻 LocalNitro1.744s (-44.5% 🟢)2.007s (-48.3% 🟢)0.264s151.44x
💻 LocalExpress1.835s (-37.8% 🟢)2.140s (-38.0% 🟢)0.304s151.51x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Next.js (Turbopack)4.146s (-41.6% 🟢)6.056s (-32.0% 🟢)1.910s51.00x
▲ VercelNitro5.247s (+29.5% 🔺)7.231s (+22.1% 🔺)1.984s51.27x
▲ VercelExpress⚠️missing----

🔍 Observability: Next.js (Turbopack) | Nitro

Promise.all with 50 concurrent steps

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Express1.339s (-61.6% 🟢)2.007s (-49.9% 🟢)0.669s151.00x
🐘 PostgresNitro1.358s (-61.0% 🟢)2.007s (-49.9% 🟢)0.649s151.01x
🐘 PostgresNext.js (Turbopack)1.617s2.007s0.390s151.21x
💻 LocalNext.js (Turbopack)3.177s3.453s0.276s92.37x
💻 LocalExpress5.435s (-34.8% 🟢)6.214s (-31.2% 🟢)0.779s54.06x
💻 LocalNitro5.468s (-34.5% 🟢)6.012s (-33.3% 🟢)0.544s54.08x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Nitro6.778s (+92.3% 🔺)8.495s (+53.5% 🔺)1.716s41.00x
▲ VercelNext.js (Turbopack)8.699s (-2.4%)11.031s (+0.6%)2.332s31.28x
▲ VercelExpress⚠️missing----

🔍 Observability: Nitro | Next.js (Turbopack)

Promise.race with 10 concurrent steps

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Express1.142s (-9.1% 🟢)2.009s (~)0.866s151.00x
🐘 PostgresNitro1.152s (-8.3% 🟢)2.008s (~)0.856s151.01x
🐘 PostgresNext.js (Turbopack)1.209s2.008s0.800s151.06x
💻 LocalNext.js (Turbopack)1.209s2.004s0.795s151.06x
💻 LocalNitro1.417s (-24.1% 🟢)2.006s (-14.3% 🟢)0.590s151.24x
💻 LocalExpress1.427s (-24.7% 🟢)2.006s (-15.1% 🟢)0.580s151.25x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Next.js (Turbopack)2.873s (-2.0%)4.428s (-4.6%)1.555s71.00x
▲ VercelNitro2.925s (+18.9% 🔺)5.006s (+20.1% 🔺)2.081s61.02x
▲ VercelExpress⚠️missing----

🔍 Observability: Next.js (Turbopack) | Nitro

Promise.race with 25 concurrent steps

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Nitro1.214s (-48.1% 🟢)2.008s (-33.3% 🟢)0.794s151.00x
🐘 PostgresExpress1.226s (-47.6% 🟢)2.009s (-33.3% 🟢)0.783s151.01x
🐘 PostgresNext.js (Turbopack)1.343s2.009s0.666s151.11x
💻 LocalNext.js (Turbopack)1.579s2.005s0.426s151.30x
💻 LocalNitro2.034s (-33.6% 🟢)2.510s (-35.4% 🟢)0.475s121.68x
💻 LocalExpress2.057s (-34.3% 🟢)2.508s (-33.3% 🟢)0.451s121.69x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Nitro3.368s (+4.2%)5.319s (+4.8%)1.951s61.00x
▲ VercelNext.js (Turbopack)4.111s (+30.8% 🔺)5.838s (+29.1% 🔺)1.726s61.22x
▲ VercelExpress⚠️missing----

🔍 Observability: Nitro | Next.js (Turbopack)

Promise.race with 50 concurrent steps

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Nitro1.316s (-62.2% 🟢)2.009s (-49.9% 🟢)0.693s151.00x
🐘 PostgresExpress1.334s (-61.9% 🟢)2.007s (-50.0% 🟢)0.673s151.01x
🐘 PostgresNext.js (Turbopack)1.644s2.008s0.364s151.25x
💻 LocalNext.js (Turbopack)3.454s4.012s0.558s82.62x
💻 LocalExpress5.760s (-34.6% 🟢)6.216s (-33.0% 🟢)0.456s54.38x
💻 LocalNitro5.993s (-34.5% 🟢)6.513s (-35.0% 🟢)0.521s64.55x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Next.js (Turbopack)5.982s (-11.5% 🟢)7.562s (-11.5% 🟢)1.580s41.00x
▲ VercelNitro6.436s (+26.4% 🔺)8.067s (+18.3% 🔺)1.632s41.08x
▲ VercelExpress⚠️missing----

🔍 Observability: Next.js (Turbopack) | Nitro

workflow with 10 sequential data payload steps (10KB)

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Nitro0.428s (-47.8% 🟢)1.006s (~)0.578s601.00x
🐘 PostgresExpress0.467s (-44.3% 🟢)1.006s (-1.7%)0.539s601.09x
💻 LocalNitro0.493s (-49.7% 🟢)1.022s (-6.6% 🟢)0.529s591.15x
💻 LocalExpress0.517s (-47.4% 🟢)1.022s (-5.0% 🟢)0.504s591.21x
💻 LocalNext.js (Turbopack)0.615s1.021s0.406s591.44x
🐘 PostgresNext.js (Turbopack)0.675s1.006s0.332s601.58x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Nitro5.901s (-73.2% 🟢)7.712s (-67.9% 🟢)1.811s81.00x
▲ VercelNext.js (Turbopack)6.006s (-58.6% 🟢)7.961s (-50.5% 🟢)1.956s81.02x
▲ VercelExpress⚠️missing----

🔍 Observability: Nitro | Next.js (Turbopack)

workflow with 25 sequential data payload steps (10KB)

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Nitro0.995s (-48.4% 🟢)1.350s (-35.7% 🟢)0.355s671.00x
🐘 PostgresExpress1.188s (-39.9% 🟢)1.922s (-14.9% 🟢)0.734s471.19x
💻 LocalNitro1.220s (-59.8% 🟢)2.006s (-46.6% 🟢)0.786s451.23x
💻 LocalExpress1.260s (-58.2% 🟢)2.007s (-44.0% 🟢)0.746s451.27x
💻 LocalNext.js (Turbopack)1.421s2.004s0.583s451.43x
🐘 PostgresNext.js (Turbopack)1.660s2.008s0.347s451.67x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Nitro16.140s (-59.1% 🟢)18.101s (-56.2% 🟢)1.961s61.00x
▲ VercelNext.js (Turbopack)16.668s (-66.5% 🟢)19.003s (-63.3% 🟢)2.335s51.03x
▲ VercelExpress⚠️missing----

🔍 Observability: Nitro | Next.js (Turbopack)

workflow with 50 sequential data payload steps (10KB)

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Nitro1.994s (-51.4% 🟢)2.335s (-49.3% 🟢)0.341s521.00x
🐘 PostgresExpress2.106s (-47.2% 🟢)2.697s (-38.3% 🟢)0.591s451.06x
💻 LocalNitro2.786s (-70.0% 🟢)3.058s (-69.5% 🟢)0.272s401.40x
💻 LocalExpress2.801s (-69.6% 🟢)3.033s (-69.7% 🟢)0.231s401.41x
🐘 PostgresNext.js (Turbopack)3.212s4.009s0.797s301.61x
💻 LocalNext.js (Turbopack)3.466s4.074s0.608s301.74x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Nitro27.620s (-71.5% 🟢)29.816s (-69.7% 🟢)2.196s51.00x
▲ VercelNext.js (Turbopack)32.787s (-69.4% 🟢)35.186s (-67.7% 🟢)2.399s41.19x
▲ VercelExpress⚠️missing----

🔍 Observability: Nitro | Next.js (Turbopack)

workflow with 10 concurrent data payload steps (10KB)

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Nitro0.190s (-33.1% 🟢)1.006s (~)0.816s601.00x
🐘 PostgresExpress0.198s (-30.0% 🟢)1.023s (+1.5%)0.825s591.04x
🐘 PostgresNext.js (Turbopack)0.230s1.006s0.776s601.21x
💻 LocalNext.js (Turbopack)0.337s1.003s0.666s601.78x
💻 LocalExpress0.381s (-32.0% 🟢)1.004s (~)0.623s602.01x
💻 LocalNitro0.399s (-34.0% 🟢)1.004s (-1.7%)0.605s602.11x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Nitro2.453s (+47.7% 🔺)4.362s (+30.2% 🔺)1.909s141.00x
▲ VercelNext.js (Turbopack)2.813s (+39.1% 🔺)4.846s (+27.8% 🔺)2.034s131.15x
▲ VercelExpress⚠️missing----

🔍 Observability: Nitro | Next.js (Turbopack)

workflow with 25 concurrent data payload steps (10KB)

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Nitro0.284s (-42.8% 🟢)1.006s (~)0.722s901.00x
🐘 PostgresExpress0.308s (-39.6% 🟢)1.006s (~)0.698s901.08x
🐘 PostgresNext.js (Turbopack)0.435s1.006s0.571s901.53x
💻 LocalNext.js (Turbopack)1.433s2.028s0.595s455.05x
💻 LocalExpress2.159s (-14.1% 🟢)2.715s (-9.8% 🟢)0.556s347.60x
💻 LocalNitro2.171s (-14.5% 🟢)2.884s (-4.1%)0.713s327.65x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Nitro5.296s (+64.2% 🔺)6.881s (+42.7% 🔺)1.585s141.00x
▲ VercelNext.js (Turbopack)6.720s (+90.1% 🔺)8.608s (+65.8% 🔺)1.888s111.27x
▲ VercelExpress⚠️missing----

🔍 Observability: Nitro | Next.js (Turbopack)

workflow with 50 concurrent data payload steps (10KB)

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Nitro0.583s (-26.2% 🟢)1.006s (~)0.422s1201.00x
🐘 PostgresExpress0.652s (-20.4% 🟢)1.006s (-1.1%)0.355s1201.12x
🐘 PostgresNext.js (Turbopack)0.890s1.078s0.188s1121.53x
💻 LocalNext.js (Turbopack)6.974s7.706s0.731s1611.96x
💻 LocalNitro10.196s (-8.9% 🟢)10.777s (-7.6% 🟢)0.581s1217.48x
💻 LocalExpress10.526s (-5.9% 🟢)11.031s (-7.6% 🟢)0.505s1118.04x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Next.js (Turbopack)15.606s (+51.1% 🔺)17.743s (+44.4% 🔺)2.137s71.00x
▲ VercelNitro16.197s (+109.7% 🔺)18.211s (+93.7% 🔺)2.014s71.04x
▲ VercelExpress⚠️missing----

🔍 Observability: Next.js (Turbopack) | Nitro

Stream Benchmarks(includes TTFB metrics)
workflow with stream

💻 Local Development

WorldFrameworkWorkflow TimeTTFBSlurpWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Nitro1.128s (+450.3% 🔺)2.001s (+100.2% 🔺)0.001s (-33.3% 🟢)2.010s (+98.8% 🔺)0.882s101.00x
💻 LocalNext.js (Turbopack)1.129s2.002s0.006s2.011s0.882s101.00x
💻 LocalNitro1.133s (+430.0% 🔺)2.005s (+99.6% 🔺)0.013s (+4.0%)2.020s (+98.3% 🔺)0.888s101.00x
💻 LocalExpress1.138s (+471.7% 🔺)2.006s (+99.7% 🔺)0.013s (+5.8% 🔺)2.021s (+98.5% 🔺)0.883s101.01x
🐘 PostgresExpress1.145s (+458.3% 🔺)2.001s (+100.4% 🔺)0.001s (-18.8% 🟢)2.010s (+98.7% 🔺)0.865s101.01x
🐘 PostgresNext.js (Turbopack)1.201s2.001s0.002s2.010s0.809s101.06x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeTTFBSlurpWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Next.js (Turbopack)2.444s (-64.3% 🟢)3.510s (-59.4% 🟢)2.280s (+260.8% 🔺)6.380s (-34.8% 🟢)3.936s101.00x
▲ VercelNitro2.490s (-35.0% 🟢)3.601s (-31.8% 🟢)13.675s (+1742.7% 🔺)17.941s (+176.7% 🔺)15.451s101.02x
▲ VercelExpress⚠️missing-----

🔍 Observability: Next.js (Turbopack) | Nitro

stream pipeline with 5 transform steps (1MB)

💻 Local Development

WorldFrameworkWorkflow TimeTTFBSlurpWall TimeOverheadSamplesvs Fastest
💻 Local🥇 Next.js (Turbopack)1.475s2.007s0.006s2.015s0.540s301.00x
🐘 PostgresNitro1.494s (+139.3% 🔺)2.002s (+98.9% 🔺)0.004s (+6.4% 🔺)2.027s (+98.3% 🔺)0.534s301.01x
💻 LocalNitro1.541s (+83.7% 🔺)2.013s (+98.9% 🔺)0.010s (+4.9%)2.025s (+81.5% 🔺)0.484s301.04x
🐘 PostgresExpress1.543s (+144.9% 🔺)2.004s (+99.1% 🔺)0.004s (+3.6%)2.028s (+98.2% 🔺)0.484s301.05x
💻 LocalExpress1.549s (+104.6% 🔺)2.013s (+95.7% 🔺)0.010s (+9.4% 🔺)2.026s (+94.8% 🔺)0.476s301.05x
🐘 PostgresNext.js (Turbopack)1.678s2.010s0.004s2.025s0.347s301.14x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeTTFBSlurpWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Next.js (Turbopack)5.989s (-64.6% 🟢)7.269s (-60.1% 🟢)0.284s (+34.4% 🔺)8.226s (-56.6% 🟢)2.237s81.00x
▲ VercelNitro6.280s (-78.7% 🟢)7.470s (-75.8% 🟢)0.267s (+138.6% 🔺)8.303s (-73.9% 🟢)2.023s81.05x
▲ VercelExpress⚠️missing-----

🔍 Observability: Next.js (Turbopack) | Nitro

10 parallel streams (1MB each)

💻 Local Development

WorldFrameworkWorkflow TimeTTFBSlurpWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Nitro0.640s (-34.0% 🟢)1.012s (-18.8% 🟢)0.000s (+22.0% 🔺)1.030s (-18.1% 🟢)0.391s591.00x
🐘 PostgresExpress0.664s (-30.9% 🟢)1.011s (-20.9% 🟢)0.000s (-61.0% 🟢)1.032s (-21.0% 🟢)0.369s591.04x
🐘 PostgresNext.js (Turbopack)0.766s1.017s0.000s1.032s0.266s591.20x
💻 LocalNext.js (Turbopack)1.042s1.631s0.000s1.634s0.592s371.63x
💻 LocalExpress1.344s (+9.7% 🔺)2.016s (~)0.000s (-30.0% 🟢)2.018s (~)0.674s302.10x
💻 LocalNitro1.373s (+12.3% 🔺)2.016s (~)0.000s (+66.7% 🔺)2.018s (~)0.645s302.15x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeTTFBSlurpWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Nitro3.957s (+29.7% 🔺)5.319s (+21.1% 🔺)0.000s (+18.2% 🔺)5.894s (+22.6% 🔺)1.937s111.00x
▲ VercelNext.js (Turbopack)3.981s (-60.9% 🟢)5.285s (-54.1% 🟢)0.000s (+Infinity% 🔺)5.843s (-51.5% 🟢)1.862s111.01x
▲ VercelExpress⚠️missing-----

🔍 Observability: Nitro | Next.js (Turbopack)

fan-out fan-in 10 streams (1MB each)

💻 Local Development

WorldFrameworkWorkflow TimeTTFBSlurpWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Nitro1.278s (-28.7% 🟢)2.029s (-5.2% 🟢)0.000s (-100.0% 🟢)2.049s (-5.7% 🟢)0.771s301.00x
🐘 PostgresExpress1.384s (-21.9% 🟢)2.029s (-6.8% 🟢)0.000s (+Infinity% 🔺)2.044s (-7.1% 🟢)0.659s301.08x
🐘 PostgresNext.js (Turbopack)1.500s2.145s0.000s2.153s0.654s281.17x
💻 LocalNext.js (Turbopack)2.061s2.523s0.000s2.528s0.467s241.61x
💻 LocalExpress3.109s (-10.3% 🟢)3.781s (-6.3% 🟢)0.001s (-21.9% 🟢)3.784s (-6.3% 🟢)0.675s162.43x
💻 LocalNitro3.183s (-6.0% 🟢)3.903s (-3.2%)0.000s (-18.0% 🟢)3.906s (-3.2%)0.723s162.49x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeTTFBSlurpWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Nitro5.980s (+46.1% 🔺)7.651s (+42.4% 🔺)0.000s (-54.2% 🟢)8.181s (+41.2% 🔺)2.201s81.00x
▲ VercelNext.js (Turbopack)6.105s (+8.7% 🔺)7.426s (+6.4% 🔺)0.000s (~)7.952s (+5.5% 🔺)1.847s81.02x
▲ VercelExpress⚠️missing-----

🔍 Observability: Nitro | Next.js (Turbopack)

Summary

Fastest Framework by World

Winner determined by most benchmark wins

World🥇 Fastest FrameworkWins
💻 LocalNext.js (Turbopack)12/21
🐘 PostgresNitro17/21
▲ VercelNitro14/21
Fastest World by Framework

Winner determined by most benchmark wins

Framework🥇 Fastest WorldWins
Express🐘 Postgres17/21
Next.js (Turbopack)🐘 Postgres11/21
Nitro🐘 Postgres19/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)

📋 View full workflow run


Some benchmark jobs failed:

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

Check the workflow run for details.

@github-actions

github-actionsBot commented May 22, 2026

Copy link
Copy Markdown
Contributor

🧪 E2E Test Results

Some tests failed

Summary

PassedFailedSkippedTotal
❌ ▲ Vercel Production121932191441
❌ 💻 Local Development161052191834
❌ 📦 Local Production161412191834
❌ 🐘 Local Postgres150212001703
✅ 🪟 Windows13100131
❌ 📋 Other7401176917
Total68161110337860

❌ Failed Tests

▲ Vercel Production (3 failed)

nitro (1 failed):

  • outputStreamInsideStepWorkflow - getWritable() called inside step functions | wrun_01KS7ZJGW73X7CKYTEQBGY87E5 | 🔍 observability

nuxt (1 failed):

  • hookWorkflow is not resumable via public webhook endpoint | wrun_01KS7ZJ3YXC4E16B345GAMR59H | 🔍 observability

sveltekit (1 failed):

💻 Local Development (5 failed)

astro-stable (2 failed):

  • fibonacciWorkflow - recursive workflow composition via start() | wrun_01KS7ZKMSDH7F5ZJHKWBKZDJPQ
  • AbortController abortExternalSignalInFlightWorkflow: external abort fires mid-flight, propagates to nested steps

nextjs-webpack-stable-lazy-discovery-disabled (1 failed):

  • error handling error propagation step errors basic step error preserves message and stack trace

nuxt-stable (1 failed):

  • AbortController abortExternalSignalInFlightWorkflow: external abort fires mid-flight, propagates to nested steps

sveltekit-stable (1 failed):

  • AbortController abortExternalSignalInFlightWorkflow: external abort fires mid-flight, propagates to nested steps
📦 Local Production (1 failed)

nuxt-stable (1 failed):

  • error handling error propagation step errors basic step error preserves message and stack trace
🐘 Local Postgres (1 failed)

nuxt-stable (1 failed):

  • error handling error propagation step errors basic step error preserves message and stack trace
📋 Other (1 failed)

e2e-local-dev-tanstack-start- (1 failed):

  • AbortController abortExternalSignalInFlightWorkflow: external abort fires mid-flight, propagates to nested steps

Details by Category

❌ ▲ Vercel Production
AppPassedFailedSkipped
✅ astro105026
✅ example105026
✅ express105026
✅ fastify105026
✅ hono105026
✅ nextjs-turbopack12902
✅ nextjs-webpack12902
❌ nitro104126
❌ nuxt104126
❌ sveltekit12317
✅ vite105026
❌ 💻 Local Development
AppPassedFailedSkipped
❌ astro-stable104225
✅ express-stable106025
✅ fastify-stable106025
✅ hono-stable106025
✅ nextjs-turbopack-canary112019
✅ nextjs-turbopack-stable-lazy-discovery-disabled13100
✅ nextjs-turbopack-stable-lazy-discovery-enabled13100
✅ nextjs-webpack-canary112019
❌ nextjs-webpack-stable-lazy-discovery-disabled13010
✅ nextjs-webpack-stable-lazy-discovery-enabled13100
✅ nitro-stable106025
❌ nuxt-stable105125
❌ sveltekit-stable12416
✅ vite-stable106025
❌ 📦 Local Production
AppPassedFailedSkipped
✅ astro-stable106025
✅ express-stable106025
✅ fastify-stable106025
✅ hono-stable106025
✅ nextjs-turbopack-canary112019
✅ nextjs-turbopack-stable-lazy-discovery-disabled13100
✅ nextjs-turbopack-stable-lazy-discovery-enabled13100
✅ nextjs-webpack-canary112019
✅ nextjs-webpack-stable-lazy-discovery-disabled13100
✅ nextjs-webpack-stable-lazy-discovery-enabled13100
✅ nitro-stable106025
❌ nuxt-stable105125
✅ sveltekit-stable12506
✅ vite-stable106025
❌ 🐘 Local Postgres
AppPassedFailedSkipped
✅ astro-stable106025
✅ express-stable106025
✅ fastify-stable106025
✅ hono-stable106025
✅ nextjs-turbopack-stable-lazy-discovery-disabled13100
✅ nextjs-turbopack-stable-lazy-discovery-enabled13100
✅ nextjs-webpack-canary112019
✅ nextjs-webpack-stable-lazy-discovery-disabled13100
✅ nextjs-webpack-stable-lazy-discovery-enabled13100
✅ nitro-stable106025
❌ nuxt-stable105125
✅ sveltekit-stable12506
✅ vite-stable106025
✅ 🪟 Windows
AppPassedFailedSkipped
✅ nextjs-turbopack13100
❌ 📋 Other
AppPassedFailedSkipped
✅ e2e-local-dev-nest-stable106025
❌ e2e-local-dev-tanstack-start-105125
✅ e2e-local-postgres-nest-stable106025
✅ e2e-local-postgres-tanstack-start-106025
✅ e2e-local-prod-nest-stable106025
✅ e2e-local-prod-tanstack-start-106025
✅ e2e-vercel-prod-tanstack-start105026

📋 View full workflow run


Some E2E test jobs failed:

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

Check the workflow run for details.

@VaguelySerious

Copy link
Copy Markdown
MemberAuthor

CI verdict

Speedup is real. Per-workbench wall-clock dropped roughly 3×: from ~14–17 min on main to ~5–6 min here. Vitest's own run is even more dramatic — express went from ~15 min to 203s (~4–5× on the actual test phase).

But correctness regressed. 25 checks failed: every `E2E Vercel Prod Tests (*)` matrix entry except `nitro`, plus several `E2E Local Dev/Prod/Postgres` jobs and `E2E Windows Tests`.

Failure pattern

Every failure I sampled is the same shape: `Error: Test timed out in 60000ms`. The which tests fail varies per workbench — i.e. it's not a single broken test:

WorkbenchFailed test
expresserror handling > catchability > FatalError can be caught… (×2)
nuxtabortThrowIfAbortedWorkflow, step throw round-trips FatalError
astroabortDeterministicBranchFromStepWorkflow, abortListenerWorkflow, cross-file step error
honoFatalError fails immediately without retries
tanstack-startabortHookOrderingWorkflow (×2), abortReasonWorkflow
fastifyabortHookOrderingWorkflow [hook-first-hook-first]
sveltekitFatalError fails immediately without retries
viteabortDeterministicBranchWorkflow, abortVoidSleepTimeoutWorkflow

The cluster is real though: AbortController + error-handling tests, which all do multiple sequential workflow round-trips and historically run close to the 60s budget.

Diagnosis

Default vitest `maxConcurrency` is 5. With five workflow runs hammering one workbench in parallel, the heavier multi-round-trip tests appear to queue past the per-test 60s budget. Local-Postgres/Prod/Dev also fail, so it's not Vercel-specific — workflow-server (local) and the in-process world also slow under contention.

Proposed next steps (separate PRs)

Holding off on bundling point (5) (shortening `sleep()` durations) into this PR — the suite isn't healthy enough to add another variable.

Three angles, in order of cheapest:

  1. Bump default `testTimeout` from 60s to ~180s (in `vitest.config.ts`). Cheap, but masks the queueing rather than fixing it. May allow individual tests to silently regress to 3× their real cost.
  2. Lower `maxConcurrency` to 3 (config-level or `describe.concurrent(name, { concurrent: 3 }, ...)`). Keeps most of the speedup, eases contention. Best first attempt IMO.
  3. Mark the error-handling and AbortController describes `.sequential` and leave the rest concurrent. Finest-grained, preserves max parallelism elsewhere, but requires per-block annotations.

Leaving this PR as draft for now. Happy to iterate as a follow-up.

The e2e suite tracked failed-run diagnostics via two module-level
mutables (`trackedRuns` and `currentTestName`) that every beforeEach
reset. Under describe.concurrent that races: when a test times out,
the displayed diagnostic typically belongs to whichever sibling task
most recently called setupRunTracking(). That's why PR #2083's first
CI run showed an unrelated `errorStepCrossFile` run as the
"diagnostic" for the FatalError catchability test that actually
timed out — leaving us blind to the real failure cause.
Switch to a `Map<taskId, PerTaskState>` keyed off
`getCurrentTest().id`, with the entry dropped via onTestFinished to
keep the map bounded. Also unconditionally log
`[trackRun] <test> → <runId>` so we can still correlate runs from
stdout even when diagnostics fetching itself stalls past the test's
deadline.
…Error type in readJSON
The previous writeExclusive used `fs.writeFile(path, data, { flag: 'wx' })`,
which is atomic for the file *creation* (O_CREAT|O_EXCL) but NOT for the
subsequent data write. A concurrent reader could catch the file with
zero bytes (or just `{`) before the writer's content reached the page
cache.
This race had been silently papered over by `readHookTokenClaim`, which
swallows any SyntaxError as "no claim exists" — which is wrong: a partial
write means a claim *does* exist, just not yet flushed, and treating it as
absent lets duplicate hook tokens through. PR #2083's concurrent e2e run
also surfaced the same race on runs/<id>.json creation, producing
size=0 byte run files in CI logs.
Fix: write content to a temp file first, then `link(2)` it atomically
into place. POSIX link is atomic and fails with EEXIST if the target
already exists, preserving the exclusive-claim semantics. Readers now
either see ENOENT or the fully-populated inode — never a half-written
file.
Also preserve the SyntaxError type when annotating readJSON failures
with file path / size / content snippet, so callers like
readHookTokenClaim that intentionally swallow `error instanceof SyntaxError`
continue to work (the previous diagnostic threw a plain Error and broke
them).
Vitest's default `maxConcurrency` of 5 saturates the workbench
preview deploys and the in-process world runtime under
`describe.concurrent`, pushing load-heavy tests (fibonacci tree
spawn, multi-step abort sequencing, AbortSignal.any composition)
into their per-test deadlines on PR #2083's run 4 even though
the same tests pass cleanly when run sequentially.
Drop to 3 in the root vitest config (only `test:e2e` and `bench`
consume it — per-package configs are untouched). The Vercel-deploy
matrix went from ~14-17min sequential → ~5-6min at concurrency 5;
trading some of that headroom for stability is the right call now
that the isolation/correctness fixes have landed.
Dropping vitest's default concurrency cap from 5 to 3 (commit c7060b1)
didn't reduce the remaining e2e flake count — runs 3/4 at default
concurrency=5 and run 5 at concurrency=3 all landed on exactly 15
failures. The remaining failures are external-network and source-map
class issues (httpbin/postman-echo abort tests, nuxt/nextjs-webpack
sourcemap assertions, fibonacci tree spawn timeouts) that lower test
parallelism doesn't address. Keeping the speedup is more valuable than
the small load reduction.
@VaguelySerious

Copy link
Copy Markdown
MemberAuthor

Update

Three substantive fixes layered on, then one revert. Final state of the PR:

What was fixed

  1. Per-task diagnostic isolation (a115774) — setupRunTracking tracked failed-run diagnostics via two module-level mutables (trackedRuns, currentTestName) that every beforeEach reset. Under describe.concurrent, every test clobbered every other's state, so the failing test's diagnostic typically showed an unrelated sibling's workflow. Switched to Map<taskId, PerTaskState> keyed off getCurrentTest().id. Also unconditionally log [trackRun] <test> → <runId> so even when diagnostics fetching stalls past a 60s timeout, the per-test runId is still in stdout for backend correlation.
  2. writeExclusive content atomicity (2fce0bb) — fs.writeFile(path, data, { flag: 'wx' }) is atomic for the file creation (O_CREAT|O_EXCL) but not for the data write that follows. A concurrent reader could observe a half-written file (e.g. just {), which the hook-token-claim path was silently swallowing as "no claim exists" — a real correctness bug. Run-creation hit the same race and produced size=0 run JSON files surfaced in CI logs. Switched to write-then-link(2): write data to a temp file first, then atomically link it into place. POSIX link is atomic and fails with EEXIST if the target exists, preserving the exclusive-claim semantics. Readers now either see ENOENT or the fully-populated inode.
  3. readJSON SyntaxError annotation (same commit) — annotates partial-read SyntaxError with file path + size + content snippet, preserving the SyntaxError type so existing handlers like readHookTokenClaim's instanceof SyntaxError catch still work.

What was tried and reverted

  • maxConcurrency: 3 (c7060b1 → reverted in 6e264eb) — runs 3/4 at concurrency=5 and run 5 at concurrency=3 all landed on exactly 15 e2e failures. The remaining flakes don't respond to lower test parallelism, so the cap traded speedup for nothing.

What's left (out of scope for this PR)

  • Sourcemap assertion failures on nuxt and nextjs-webpack: expect(stack).not.toContain('99_e2e.ts') fires because hasStepSourceMaps() reports false but the stack does contain the .ts filename. Pre-existing test/runtime mismatch — independent of concurrency, just hits more often now that more workflows run per CI invocation. Needs separate investigation into the source-map detection logic.
  • External-network flakes on abort tests (abortFetchInFlightWorkflow, abortFetchUncaughtWorkflow, etc.): httpbin/postman-echo delay endpoints intermittently return early from GH Actions runners. [tests] Fix abort-fetch e2e flake #2081 added a fallback URL pair; under heavier concurrent load both upstreams flake. The proper fix is a per-workbench /api/delay/:n route.
  • Fibonacci tree spawn timeouts on Vercel Prod: fibonacciWorkflow with fib(6) spawns a tree of child runs; under concurrent load this saturates the workflow scheduler past the test's 180s budget. Could be addressed with a smaller fib(N), a longer budget, or marking that single test .sequential.

Verdict

The actual concurrency-introduced isolation/correctness bugs are fixed. Concurrency speedup holds (~3× job wall-clock; ~4–5× pure vitest run). The remaining failures are pre-existing issues that the concurrency change has made more visible but doesn't itself cause. Happy to keep this draft until those classes are tackled, or to merge with a green CI rerun depending on appetite.

Sign up for freeto 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

@VaguelySerious
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Highlight search terms from Google/DuckDuckGo/Bing referrer\n(function() {\n var ref = document.referrer;\n var terms = [];\n \n if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) {\n var url = new URL(ref);\n var q = url.searchParams.get('q') || url.searchParams.get('p');\n if (q) {\n terms = q.split(/\\s+/).filter(function(t) { return t.length > 2; });\n }\n }\n \n if (terms.length === 0) return;\n \n var style = document.createElement('style');\n style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }';\n document.head.appendChild(style);\n \n function highlight(node) {\n if (node.nodeType === 3) { // text node\n var text = node.textContent;\n var found = false;\n terms.forEach(function(term) {\n var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\') + ')', 'gi');\n if (regex.test(text)) {\n found = true;\n var frag = document.createDocumentFragment();\n var parts = text.split(regex);\n parts.forEach(function(part, i) {\n if (i % 2 === 0) {\n frag.appendChild(document.createTextNode(part));\n } else {\n var span = document.createElement('span');\n span.className = 'userscript-highlight';\n span.textContent = part;\n frag.appendChild(span);\n }\n });\n node.parentNode.replaceChild(frag, node);\n }\n });\n } else if (node.nodeType === 1 && node.childNodes) { // element\n var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT'];\n if (!skipTags.includes(node.tagName)) {\n Array.from(node.childNodes).forEach(highlight);\n }\n }\n }\n \n highlight(document.body);\n \n // Re-highlight on dynamic content\n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1 || node.nodeType === 3) highlight(node);\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Highlight Search Terms"); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

[tests] Re-enable concurrent e2e suite - #2083

Closed
VaguelySerious wants to merge 6 commits into
mainfrom
peter/enable-e2e-concurrency
Closed

[tests] Re-enable concurrent e2e suite#2083
VaguelySerious wants to merge 6 commits into
mainfrom
peter/enable-e2e-concurrency

Conversation

@VaguelySerious

Copy link
Copy Markdown
Member

Summary

  • Re-enables describe.concurrent in packages/core/e2e/e2e.test.ts, which was made sequential in 78048e0 ("no concurrency") with a TODO to re-enable.
  • Each workbench currently runs ~87 e2e tests serially against its preview deploy; this is the dominant ~15-minute cost per matrix entry.
  • This is a draft to see which (if any) tests are still flake-prone under concurrency after recent fixes (e.g. the abort-fetch class of flakes in [tests] Fix abort-fetch e2e flake #2081).

Test plan

  • All workbench matrix entries (E2E Vercel Prod) pass
  • Compare wall-clock vs. recent main runs
  • If failures cluster around specific tests, mark them .sequential rather than reverting the whole flip

Notes

If concurrency exposes real isolation bugs (shared identifiers, cross-test state), I'll fix those before unwinding the draft state. If it's only known-flaky externals, those should already have been addressed in #2081 / follow-ups.

🤖 Generated with Claude Code

Flips packages/core/e2e/e2e.test.ts back to describe.concurrent. The
suite was made sequential ("temporarily disabling concurrent tests to
avoid flakiness") and never re-enabled. Each workbench now runs ~87
tests serially against its preview deploy, dominating CI wall-clock.
Putting this up as a draft to see which (if any) tests are still
flake-prone under concurrency, after recent fixes to the abort-fetch
class of flakes.
@vercel

vercelBot commented May 22, 2026

Copy link
Copy Markdown
Contributor

@changeset-bot

changeset-botBot commented May 22, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: c7060b1

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

This PR includes changesets to release 18 packages
NameType
@workflow/world-localPatch
@workflow/cliPatch
@workflow/corePatch
@workflow/vitestPatch
@workflow/world-postgresPatch
workflowPatch
@workflow/world-testingPatch
@workflow/buildersPatch
@workflow/nextPatch
@workflow/nitroPatch
@workflow/web-sharedPatch
@workflow/webPatch
@workflow/astroPatch
@workflow/nestPatch
@workflow/rollupPatch
@workflow/sveltekitPatch
@workflow/vitePatch
@workflow/nuxtPatch

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-actionsBot commented May 22, 2026

Copy link
Copy Markdown
Contributor

📊 Benchmark Results

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

workflow with no steps

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
💻 Local🥇 Nitro0.032s (-26.0% 🟢)1.006s (~)0.974s101.00x
💻 LocalExpress0.032s (-26.9% 🟢)1.006s (~)0.974s101.02x
💻 LocalNext.js (Turbopack)0.034s1.005s0.972s101.06x
🐘 PostgresExpress0.050s (-13.1% 🟢)1.012s (~)0.961s101.58x
🐘 PostgresNitro0.051s (-46.3% 🟢)1.013s (-2.9%)0.962s101.60x
🐘 PostgresNext.js (Turbopack)0.056s1.011s0.955s101.77x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Next.js (Turbopack)0.306s (+21.6% 🔺)2.546s (+9.1% 🔺)2.240s101.00x
▲ VercelNitro0.426s (+4.0%)2.554s (+1.8%)2.128s101.39x
▲ VercelExpress⚠️missing----

🔍 Observability: Next.js (Turbopack) | Nitro

workflow with 1 step

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
💻 Local🥇 Nitro1.076s (-4.9%)2.007s (~)0.931s101.00x
💻 LocalExpress1.079s (-4.1%)2.007s (~)0.928s101.00x
🐘 PostgresNitro1.082s (-5.1% 🟢)2.008s (~)0.926s101.01x
🐘 PostgresExpress1.087s (-5.2% 🟢)2.009s (~)0.922s101.01x
💻 LocalNext.js (Turbopack)1.089s2.005s0.916s101.01x
🐘 PostgresNext.js (Turbopack)1.117s2.008s0.891s101.04x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Nitro1.806s (-53.6% 🟢)3.829s (-35.2% 🟢)2.023s101.00x
▲ VercelNext.js (Turbopack)1.906s (-6.3% 🟢)3.724s (-2.8%)1.818s101.06x
▲ VercelExpress⚠️missing----

🔍 Observability: Nitro | Next.js (Turbopack)

workflow with 10 sequential steps

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Nitro10.387s (-4.4%)11.018s (~)0.630s31.00x
💻 LocalExpress10.434s (-4.5%)11.023s (~)0.590s31.00x
💻 LocalNitro10.434s (-4.7%)11.024s (~)0.590s31.00x
🐘 PostgresExpress10.435s (-4.8%)11.014s (~)0.579s31.00x
💻 LocalNext.js (Turbopack)10.493s11.021s0.528s31.01x
🐘 PostgresNext.js (Turbopack)10.702s11.020s0.318s31.03x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Nitro13.961s (-41.2% 🟢)16.231s (-35.4% 🟢)2.269s21.00x
▲ VercelNext.js (Turbopack)14.589s (-15.8% 🟢)16.525s (-14.8% 🟢)1.936s21.04x
▲ VercelExpress⚠️missing----

🔍 Observability: Nitro | Next.js (Turbopack)

workflow with 25 sequential steps

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Nitro13.428s (-8.0% 🟢)14.016s (-6.7% 🟢)0.588s51.00x
🐘 PostgresExpress13.500s (-7.4% 🟢)14.019s (-6.7% 🟢)0.518s51.01x
💻 LocalExpress13.527s (-9.6% 🟢)14.027s (-6.7% 🟢)0.500s51.01x
💻 LocalNitro13.592s (-9.8% 🟢)14.027s (-12.5% 🟢)0.435s51.01x
💻 LocalNext.js (Turbopack)13.761s14.025s0.264s51.02x
🐘 PostgresNext.js (Turbopack)14.104s15.019s0.915s41.05x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Nitro21.896s (-66.0% 🟢)23.781s (-64.3% 🟢)1.885s31.00x
▲ VercelNext.js (Turbopack)22.228s (-57.7% 🟢)24.764s (-54.7% 🟢)2.536s31.02x
▲ VercelExpress⚠️missing----

🔍 Observability: Nitro | Next.js (Turbopack)

workflow with 50 sequential steps

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Nitro11.854s (-15.1% 🟢)12.015s (-16.0% 🟢)0.161s81.00x
💻 LocalNitro11.923s (-29.0% 🟢)12.021s (-29.4% 🟢)0.098s81.01x
🐘 PostgresExpress11.984s (-14.4% 🟢)12.017s (-17.7% 🟢)0.033s81.01x
💻 LocalExpress12.021s (-27.6% 🟢)12.399s (-27.2% 🟢)0.379s81.01x
💻 LocalNext.js (Turbopack)12.282s13.022s0.740s71.04x
🐘 PostgresNext.js (Turbopack)13.164s14.015s0.851s71.11x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Nitro31.366s (-92.6% 🟢)33.410s (-92.1% 🟢)2.044s31.00x
▲ VercelNext.js (Turbopack)32.382s (-91.8% 🟢)34.323s (-91.3% 🟢)1.940s31.03x
▲ VercelExpress⚠️missing----

🔍 Observability: Nitro | Next.js (Turbopack)

Promise.all with 10 concurrent steps

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Nitro1.158s (-9.2% 🟢)2.009s (~)0.851s151.00x
🐘 PostgresExpress1.160s (-8.0% 🟢)2.008s (~)0.848s151.00x
💻 LocalNitro1.172s (-28.2% 🟢)2.006s (-3.3%)0.835s151.01x
💻 LocalExpress1.179s (-20.8% 🟢)2.007s (~)0.828s151.02x
💻 LocalNext.js (Turbopack)1.212s2.005s0.793s151.05x
🐘 PostgresNext.js (Turbopack)1.218s2.008s0.790s151.05x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Nitro2.877s (+2.1%)4.515s (+4.5%)1.638s81.00x
▲ VercelNext.js (Turbopack)2.967s (-12.7% 🟢)4.728s (-4.2%)1.761s71.03x
▲ VercelExpress⚠️missing----

🔍 Observability: Nitro | Next.js (Turbopack)

Promise.all with 25 concurrent steps

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Express1.212s (-48.6% 🟢)2.007s (-33.3% 🟢)0.795s151.00x
🐘 PostgresNitro1.224s (-47.9% 🟢)2.009s (-33.2% 🟢)0.785s151.01x
🐘 PostgresNext.js (Turbopack)1.336s2.007s0.671s151.10x
💻 LocalNext.js (Turbopack)1.524s2.004s0.480s151.26x
💻 LocalNitro1.744s (-44.5% 🟢)2.007s (-48.3% 🟢)0.264s151.44x
💻 LocalExpress1.835s (-37.8% 🟢)2.140s (-38.0% 🟢)0.304s151.51x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Next.js (Turbopack)4.146s (-41.6% 🟢)6.056s (-32.0% 🟢)1.910s51.00x
▲ VercelNitro5.247s (+29.5% 🔺)7.231s (+22.1% 🔺)1.984s51.27x
▲ VercelExpress⚠️missing----

🔍 Observability: Next.js (Turbopack) | Nitro

Promise.all with 50 concurrent steps

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Express1.339s (-61.6% 🟢)2.007s (-49.9% 🟢)0.669s151.00x
🐘 PostgresNitro1.358s (-61.0% 🟢)2.007s (-49.9% 🟢)0.649s151.01x
🐘 PostgresNext.js (Turbopack)1.617s2.007s0.390s151.21x
💻 LocalNext.js (Turbopack)3.177s3.453s0.276s92.37x
💻 LocalExpress5.435s (-34.8% 🟢)6.214s (-31.2% 🟢)0.779s54.06x
💻 LocalNitro5.468s (-34.5% 🟢)6.012s (-33.3% 🟢)0.544s54.08x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Nitro6.778s (+92.3% 🔺)8.495s (+53.5% 🔺)1.716s41.00x
▲ VercelNext.js (Turbopack)8.699s (-2.4%)11.031s (+0.6%)2.332s31.28x
▲ VercelExpress⚠️missing----

🔍 Observability: Nitro | Next.js (Turbopack)

Promise.race with 10 concurrent steps

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Express1.142s (-9.1% 🟢)2.009s (~)0.866s151.00x
🐘 PostgresNitro1.152s (-8.3% 🟢)2.008s (~)0.856s151.01x
🐘 PostgresNext.js (Turbopack)1.209s2.008s0.800s151.06x
💻 LocalNext.js (Turbopack)1.209s2.004s0.795s151.06x
💻 LocalNitro1.417s (-24.1% 🟢)2.006s (-14.3% 🟢)0.590s151.24x
💻 LocalExpress1.427s (-24.7% 🟢)2.006s (-15.1% 🟢)0.580s151.25x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Next.js (Turbopack)2.873s (-2.0%)4.428s (-4.6%)1.555s71.00x
▲ VercelNitro2.925s (+18.9% 🔺)5.006s (+20.1% 🔺)2.081s61.02x
▲ VercelExpress⚠️missing----

🔍 Observability: Next.js (Turbopack) | Nitro

Promise.race with 25 concurrent steps

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Nitro1.214s (-48.1% 🟢)2.008s (-33.3% 🟢)0.794s151.00x
🐘 PostgresExpress1.226s (-47.6% 🟢)2.009s (-33.3% 🟢)0.783s151.01x
🐘 PostgresNext.js (Turbopack)1.343s2.009s0.666s151.11x
💻 LocalNext.js (Turbopack)1.579s2.005s0.426s151.30x
💻 LocalNitro2.034s (-33.6% 🟢)2.510s (-35.4% 🟢)0.475s121.68x
💻 LocalExpress2.057s (-34.3% 🟢)2.508s (-33.3% 🟢)0.451s121.69x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Nitro3.368s (+4.2%)5.319s (+4.8%)1.951s61.00x
▲ VercelNext.js (Turbopack)4.111s (+30.8% 🔺)5.838s (+29.1% 🔺)1.726s61.22x
▲ VercelExpress⚠️missing----

🔍 Observability: Nitro | Next.js (Turbopack)

Promise.race with 50 concurrent steps

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Nitro1.316s (-62.2% 🟢)2.009s (-49.9% 🟢)0.693s151.00x
🐘 PostgresExpress1.334s (-61.9% 🟢)2.007s (-50.0% 🟢)0.673s151.01x
🐘 PostgresNext.js (Turbopack)1.644s2.008s0.364s151.25x
💻 LocalNext.js (Turbopack)3.454s4.012s0.558s82.62x
💻 LocalExpress5.760s (-34.6% 🟢)6.216s (-33.0% 🟢)0.456s54.38x
💻 LocalNitro5.993s (-34.5% 🟢)6.513s (-35.0% 🟢)0.521s64.55x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Next.js (Turbopack)5.982s (-11.5% 🟢)7.562s (-11.5% 🟢)1.580s41.00x
▲ VercelNitro6.436s (+26.4% 🔺)8.067s (+18.3% 🔺)1.632s41.08x
▲ VercelExpress⚠️missing----

🔍 Observability: Next.js (Turbopack) | Nitro

workflow with 10 sequential data payload steps (10KB)

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Nitro0.428s (-47.8% 🟢)1.006s (~)0.578s601.00x
🐘 PostgresExpress0.467s (-44.3% 🟢)1.006s (-1.7%)0.539s601.09x
💻 LocalNitro0.493s (-49.7% 🟢)1.022s (-6.6% 🟢)0.529s591.15x
💻 LocalExpress0.517s (-47.4% 🟢)1.022s (-5.0% 🟢)0.504s591.21x
💻 LocalNext.js (Turbopack)0.615s1.021s0.406s591.44x
🐘 PostgresNext.js (Turbopack)0.675s1.006s0.332s601.58x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Nitro5.901s (-73.2% 🟢)7.712s (-67.9% 🟢)1.811s81.00x
▲ VercelNext.js (Turbopack)6.006s (-58.6% 🟢)7.961s (-50.5% 🟢)1.956s81.02x
▲ VercelExpress⚠️missing----

🔍 Observability: Nitro | Next.js (Turbopack)

workflow with 25 sequential data payload steps (10KB)

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Nitro0.995s (-48.4% 🟢)1.350s (-35.7% 🟢)0.355s671.00x
🐘 PostgresExpress1.188s (-39.9% 🟢)1.922s (-14.9% 🟢)0.734s471.19x
💻 LocalNitro1.220s (-59.8% 🟢)2.006s (-46.6% 🟢)0.786s451.23x
💻 LocalExpress1.260s (-58.2% 🟢)2.007s (-44.0% 🟢)0.746s451.27x
💻 LocalNext.js (Turbopack)1.421s2.004s0.583s451.43x
🐘 PostgresNext.js (Turbopack)1.660s2.008s0.347s451.67x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Nitro16.140s (-59.1% 🟢)18.101s (-56.2% 🟢)1.961s61.00x
▲ VercelNext.js (Turbopack)16.668s (-66.5% 🟢)19.003s (-63.3% 🟢)2.335s51.03x
▲ VercelExpress⚠️missing----

🔍 Observability: Nitro | Next.js (Turbopack)

workflow with 50 sequential data payload steps (10KB)

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Nitro1.994s (-51.4% 🟢)2.335s (-49.3% 🟢)0.341s521.00x
🐘 PostgresExpress2.106s (-47.2% 🟢)2.697s (-38.3% 🟢)0.591s451.06x
💻 LocalNitro2.786s (-70.0% 🟢)3.058s (-69.5% 🟢)0.272s401.40x
💻 LocalExpress2.801s (-69.6% 🟢)3.033s (-69.7% 🟢)0.231s401.41x
🐘 PostgresNext.js (Turbopack)3.212s4.009s0.797s301.61x
💻 LocalNext.js (Turbopack)3.466s4.074s0.608s301.74x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Nitro27.620s (-71.5% 🟢)29.816s (-69.7% 🟢)2.196s51.00x
▲ VercelNext.js (Turbopack)32.787s (-69.4% 🟢)35.186s (-67.7% 🟢)2.399s41.19x
▲ VercelExpress⚠️missing----

🔍 Observability: Nitro | Next.js (Turbopack)

workflow with 10 concurrent data payload steps (10KB)

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Nitro0.190s (-33.1% 🟢)1.006s (~)0.816s601.00x
🐘 PostgresExpress0.198s (-30.0% 🟢)1.023s (+1.5%)0.825s591.04x
🐘 PostgresNext.js (Turbopack)0.230s1.006s0.776s601.21x
💻 LocalNext.js (Turbopack)0.337s1.003s0.666s601.78x
💻 LocalExpress0.381s (-32.0% 🟢)1.004s (~)0.623s602.01x
💻 LocalNitro0.399s (-34.0% 🟢)1.004s (-1.7%)0.605s602.11x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Nitro2.453s (+47.7% 🔺)4.362s (+30.2% 🔺)1.909s141.00x
▲ VercelNext.js (Turbopack)2.813s (+39.1% 🔺)4.846s (+27.8% 🔺)2.034s131.15x
▲ VercelExpress⚠️missing----

🔍 Observability: Nitro | Next.js (Turbopack)

workflow with 25 concurrent data payload steps (10KB)

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Nitro0.284s (-42.8% 🟢)1.006s (~)0.722s901.00x
🐘 PostgresExpress0.308s (-39.6% 🟢)1.006s (~)0.698s901.08x
🐘 PostgresNext.js (Turbopack)0.435s1.006s0.571s901.53x
💻 LocalNext.js (Turbopack)1.433s2.028s0.595s455.05x
💻 LocalExpress2.159s (-14.1% 🟢)2.715s (-9.8% 🟢)0.556s347.60x
💻 LocalNitro2.171s (-14.5% 🟢)2.884s (-4.1%)0.713s327.65x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Nitro5.296s (+64.2% 🔺)6.881s (+42.7% 🔺)1.585s141.00x
▲ VercelNext.js (Turbopack)6.720s (+90.1% 🔺)8.608s (+65.8% 🔺)1.888s111.27x
▲ VercelExpress⚠️missing----

🔍 Observability: Nitro | Next.js (Turbopack)

workflow with 50 concurrent data payload steps (10KB)

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Nitro0.583s (-26.2% 🟢)1.006s (~)0.422s1201.00x
🐘 PostgresExpress0.652s (-20.4% 🟢)1.006s (-1.1%)0.355s1201.12x
🐘 PostgresNext.js (Turbopack)0.890s1.078s0.188s1121.53x
💻 LocalNext.js (Turbopack)6.974s7.706s0.731s1611.96x
💻 LocalNitro10.196s (-8.9% 🟢)10.777s (-7.6% 🟢)0.581s1217.48x
💻 LocalExpress10.526s (-5.9% 🟢)11.031s (-7.6% 🟢)0.505s1118.04x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Next.js (Turbopack)15.606s (+51.1% 🔺)17.743s (+44.4% 🔺)2.137s71.00x
▲ VercelNitro16.197s (+109.7% 🔺)18.211s (+93.7% 🔺)2.014s71.04x
▲ VercelExpress⚠️missing----

🔍 Observability: Next.js (Turbopack) | Nitro

Stream Benchmarks(includes TTFB metrics)
workflow with stream

💻 Local Development

WorldFrameworkWorkflow TimeTTFBSlurpWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Nitro1.128s (+450.3% 🔺)2.001s (+100.2% 🔺)0.001s (-33.3% 🟢)2.010s (+98.8% 🔺)0.882s101.00x
💻 LocalNext.js (Turbopack)1.129s2.002s0.006s2.011s0.882s101.00x
💻 LocalNitro1.133s (+430.0% 🔺)2.005s (+99.6% 🔺)0.013s (+4.0%)2.020s (+98.3% 🔺)0.888s101.00x
💻 LocalExpress1.138s (+471.7% 🔺)2.006s (+99.7% 🔺)0.013s (+5.8% 🔺)2.021s (+98.5% 🔺)0.883s101.01x
🐘 PostgresExpress1.145s (+458.3% 🔺)2.001s (+100.4% 🔺)0.001s (-18.8% 🟢)2.010s (+98.7% 🔺)0.865s101.01x
🐘 PostgresNext.js (Turbopack)1.201s2.001s0.002s2.010s0.809s101.06x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeTTFBSlurpWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Next.js (Turbopack)2.444s (-64.3% 🟢)3.510s (-59.4% 🟢)2.280s (+260.8% 🔺)6.380s (-34.8% 🟢)3.936s101.00x
▲ VercelNitro2.490s (-35.0% 🟢)3.601s (-31.8% 🟢)13.675s (+1742.7% 🔺)17.941s (+176.7% 🔺)15.451s101.02x
▲ VercelExpress⚠️missing-----

🔍 Observability: Next.js (Turbopack) | Nitro

stream pipeline with 5 transform steps (1MB)

💻 Local Development

WorldFrameworkWorkflow TimeTTFBSlurpWall TimeOverheadSamplesvs Fastest
💻 Local🥇 Next.js (Turbopack)1.475s2.007s0.006s2.015s0.540s301.00x
🐘 PostgresNitro1.494s (+139.3% 🔺)2.002s (+98.9% 🔺)0.004s (+6.4% 🔺)2.027s (+98.3% 🔺)0.534s301.01x
💻 LocalNitro1.541s (+83.7% 🔺)2.013s (+98.9% 🔺)0.010s (+4.9%)2.025s (+81.5% 🔺)0.484s301.04x
🐘 PostgresExpress1.543s (+144.9% 🔺)2.004s (+99.1% 🔺)0.004s (+3.6%)2.028s (+98.2% 🔺)0.484s301.05x
💻 LocalExpress1.549s (+104.6% 🔺)2.013s (+95.7% 🔺)0.010s (+9.4% 🔺)2.026s (+94.8% 🔺)0.476s301.05x
🐘 PostgresNext.js (Turbopack)1.678s2.010s0.004s2.025s0.347s301.14x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeTTFBSlurpWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Next.js (Turbopack)5.989s (-64.6% 🟢)7.269s (-60.1% 🟢)0.284s (+34.4% 🔺)8.226s (-56.6% 🟢)2.237s81.00x
▲ VercelNitro6.280s (-78.7% 🟢)7.470s (-75.8% 🟢)0.267s (+138.6% 🔺)8.303s (-73.9% 🟢)2.023s81.05x
▲ VercelExpress⚠️missing-----

🔍 Observability: Next.js (Turbopack) | Nitro

10 parallel streams (1MB each)

💻 Local Development

WorldFrameworkWorkflow TimeTTFBSlurpWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Nitro0.640s (-34.0% 🟢)1.012s (-18.8% 🟢)0.000s (+22.0% 🔺)1.030s (-18.1% 🟢)0.391s591.00x
🐘 PostgresExpress0.664s (-30.9% 🟢)1.011s (-20.9% 🟢)0.000s (-61.0% 🟢)1.032s (-21.0% 🟢)0.369s591.04x
🐘 PostgresNext.js (Turbopack)0.766s1.017s0.000s1.032s0.266s591.20x
💻 LocalNext.js (Turbopack)1.042s1.631s0.000s1.634s0.592s371.63x
💻 LocalExpress1.344s (+9.7% 🔺)2.016s (~)0.000s (-30.0% 🟢)2.018s (~)0.674s302.10x
💻 LocalNitro1.373s (+12.3% 🔺)2.016s (~)0.000s (+66.7% 🔺)2.018s (~)0.645s302.15x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeTTFBSlurpWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Nitro3.957s (+29.7% 🔺)5.319s (+21.1% 🔺)0.000s (+18.2% 🔺)5.894s (+22.6% 🔺)1.937s111.00x
▲ VercelNext.js (Turbopack)3.981s (-60.9% 🟢)5.285s (-54.1% 🟢)0.000s (+Infinity% 🔺)5.843s (-51.5% 🟢)1.862s111.01x
▲ VercelExpress⚠️missing-----

🔍 Observability: Nitro | Next.js (Turbopack)

fan-out fan-in 10 streams (1MB each)

💻 Local Development

WorldFrameworkWorkflow TimeTTFBSlurpWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Nitro1.278s (-28.7% 🟢)2.029s (-5.2% 🟢)0.000s (-100.0% 🟢)2.049s (-5.7% 🟢)0.771s301.00x
🐘 PostgresExpress1.384s (-21.9% 🟢)2.029s (-6.8% 🟢)0.000s (+Infinity% 🔺)2.044s (-7.1% 🟢)0.659s301.08x
🐘 PostgresNext.js (Turbopack)1.500s2.145s0.000s2.153s0.654s281.17x
💻 LocalNext.js (Turbopack)2.061s2.523s0.000s2.528s0.467s241.61x
💻 LocalExpress3.109s (-10.3% 🟢)3.781s (-6.3% 🟢)0.001s (-21.9% 🟢)3.784s (-6.3% 🟢)0.675s162.43x
💻 LocalNitro3.183s (-6.0% 🟢)3.903s (-3.2%)0.000s (-18.0% 🟢)3.906s (-3.2%)0.723s162.49x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeTTFBSlurpWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Nitro5.980s (+46.1% 🔺)7.651s (+42.4% 🔺)0.000s (-54.2% 🟢)8.181s (+41.2% 🔺)2.201s81.00x
▲ VercelNext.js (Turbopack)6.105s (+8.7% 🔺)7.426s (+6.4% 🔺)0.000s (~)7.952s (+5.5% 🔺)1.847s81.02x
▲ VercelExpress⚠️missing-----

🔍 Observability: Nitro | Next.js (Turbopack)

Summary

Fastest Framework by World

Winner determined by most benchmark wins

World🥇 Fastest FrameworkWins
💻 LocalNext.js (Turbopack)12/21
🐘 PostgresNitro17/21
▲ VercelNitro14/21
Fastest World by Framework

Winner determined by most benchmark wins

Framework🥇 Fastest WorldWins
Express🐘 Postgres17/21
Next.js (Turbopack)🐘 Postgres11/21
Nitro🐘 Postgres19/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)

📋 View full workflow run


Some benchmark jobs failed:

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

Check the workflow run for details.

@github-actions

github-actionsBot commented May 22, 2026

Copy link
Copy Markdown
Contributor

🧪 E2E Test Results

Some tests failed

Summary

PassedFailedSkippedTotal
❌ ▲ Vercel Production121932191441
❌ 💻 Local Development161052191834
❌ 📦 Local Production161412191834
❌ 🐘 Local Postgres150212001703
✅ 🪟 Windows13100131
❌ 📋 Other7401176917
Total68161110337860

❌ Failed Tests

▲ Vercel Production (3 failed)

nitro (1 failed):

  • outputStreamInsideStepWorkflow - getWritable() called inside step functions | wrun_01KS7ZJGW73X7CKYTEQBGY87E5 | 🔍 observability

nuxt (1 failed):

  • hookWorkflow is not resumable via public webhook endpoint | wrun_01KS7ZJ3YXC4E16B345GAMR59H | 🔍 observability

sveltekit (1 failed):

💻 Local Development (5 failed)

astro-stable (2 failed):

  • fibonacciWorkflow - recursive workflow composition via start() | wrun_01KS7ZKMSDH7F5ZJHKWBKZDJPQ
  • AbortController abortExternalSignalInFlightWorkflow: external abort fires mid-flight, propagates to nested steps

nextjs-webpack-stable-lazy-discovery-disabled (1 failed):

  • error handling error propagation step errors basic step error preserves message and stack trace

nuxt-stable (1 failed):

  • AbortController abortExternalSignalInFlightWorkflow: external abort fires mid-flight, propagates to nested steps

sveltekit-stable (1 failed):

  • AbortController abortExternalSignalInFlightWorkflow: external abort fires mid-flight, propagates to nested steps
📦 Local Production (1 failed)

nuxt-stable (1 failed):

  • error handling error propagation step errors basic step error preserves message and stack trace
🐘 Local Postgres (1 failed)

nuxt-stable (1 failed):

  • error handling error propagation step errors basic step error preserves message and stack trace
📋 Other (1 failed)

e2e-local-dev-tanstack-start- (1 failed):

  • AbortController abortExternalSignalInFlightWorkflow: external abort fires mid-flight, propagates to nested steps

Details by Category

❌ ▲ Vercel Production
AppPassedFailedSkipped
✅ astro105026
✅ example105026
✅ express105026
✅ fastify105026
✅ hono105026
✅ nextjs-turbopack12902
✅ nextjs-webpack12902
❌ nitro104126
❌ nuxt104126
❌ sveltekit12317
✅ vite105026
❌ 💻 Local Development
AppPassedFailedSkipped
❌ astro-stable104225
✅ express-stable106025
✅ fastify-stable106025
✅ hono-stable106025
✅ nextjs-turbopack-canary112019
✅ nextjs-turbopack-stable-lazy-discovery-disabled13100
✅ nextjs-turbopack-stable-lazy-discovery-enabled13100
✅ nextjs-webpack-canary112019
❌ nextjs-webpack-stable-lazy-discovery-disabled13010
✅ nextjs-webpack-stable-lazy-discovery-enabled13100
✅ nitro-stable106025
❌ nuxt-stable105125
❌ sveltekit-stable12416
✅ vite-stable106025
❌ 📦 Local Production
AppPassedFailedSkipped
✅ astro-stable106025
✅ express-stable106025
✅ fastify-stable106025
✅ hono-stable106025
✅ nextjs-turbopack-canary112019
✅ nextjs-turbopack-stable-lazy-discovery-disabled13100
✅ nextjs-turbopack-stable-lazy-discovery-enabled13100
✅ nextjs-webpack-canary112019
✅ nextjs-webpack-stable-lazy-discovery-disabled13100
✅ nextjs-webpack-stable-lazy-discovery-enabled13100
✅ nitro-stable106025
❌ nuxt-stable105125
✅ sveltekit-stable12506
✅ vite-stable106025
❌ 🐘 Local Postgres
AppPassedFailedSkipped
✅ astro-stable106025
✅ express-stable106025
✅ fastify-stable106025
✅ hono-stable106025
✅ nextjs-turbopack-stable-lazy-discovery-disabled13100
✅ nextjs-turbopack-stable-lazy-discovery-enabled13100
✅ nextjs-webpack-canary112019
✅ nextjs-webpack-stable-lazy-discovery-disabled13100
✅ nextjs-webpack-stable-lazy-discovery-enabled13100
✅ nitro-stable106025
❌ nuxt-stable105125
✅ sveltekit-stable12506
✅ vite-stable106025
✅ 🪟 Windows
AppPassedFailedSkipped
✅ nextjs-turbopack13100
❌ 📋 Other
AppPassedFailedSkipped
✅ e2e-local-dev-nest-stable106025
❌ e2e-local-dev-tanstack-start-105125
✅ e2e-local-postgres-nest-stable106025
✅ e2e-local-postgres-tanstack-start-106025
✅ e2e-local-prod-nest-stable106025
✅ e2e-local-prod-tanstack-start-106025
✅ e2e-vercel-prod-tanstack-start105026

📋 View full workflow run


Some E2E test jobs failed:

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

Check the workflow run for details.

@VaguelySerious

Copy link
Copy Markdown
MemberAuthor

CI verdict

Speedup is real. Per-workbench wall-clock dropped roughly 3×: from ~14–17 min on main to ~5–6 min here. Vitest's own run is even more dramatic — express went from ~15 min to 203s (~4–5× on the actual test phase).

But correctness regressed. 25 checks failed: every `E2E Vercel Prod Tests (*)` matrix entry except `nitro`, plus several `E2E Local Dev/Prod/Postgres` jobs and `E2E Windows Tests`.

Failure pattern

Every failure I sampled is the same shape: `Error: Test timed out in 60000ms`. The which tests fail varies per workbench — i.e. it's not a single broken test:

WorkbenchFailed test
expresserror handling > catchability > FatalError can be caught… (×2)
nuxtabortThrowIfAbortedWorkflow, step throw round-trips FatalError
astroabortDeterministicBranchFromStepWorkflow, abortListenerWorkflow, cross-file step error
honoFatalError fails immediately without retries
tanstack-startabortHookOrderingWorkflow (×2), abortReasonWorkflow
fastifyabortHookOrderingWorkflow [hook-first-hook-first]
sveltekitFatalError fails immediately without retries
viteabortDeterministicBranchWorkflow, abortVoidSleepTimeoutWorkflow

The cluster is real though: AbortController + error-handling tests, which all do multiple sequential workflow round-trips and historically run close to the 60s budget.

Diagnosis

Default vitest `maxConcurrency` is 5. With five workflow runs hammering one workbench in parallel, the heavier multi-round-trip tests appear to queue past the per-test 60s budget. Local-Postgres/Prod/Dev also fail, so it's not Vercel-specific — workflow-server (local) and the in-process world also slow under contention.

Proposed next steps (separate PRs)

Holding off on bundling point (5) (shortening `sleep()` durations) into this PR — the suite isn't healthy enough to add another variable.

Three angles, in order of cheapest:

  1. Bump default `testTimeout` from 60s to ~180s (in `vitest.config.ts`). Cheap, but masks the queueing rather than fixing it. May allow individual tests to silently regress to 3× their real cost.
  2. Lower `maxConcurrency` to 3 (config-level or `describe.concurrent(name, { concurrent: 3 }, ...)`). Keeps most of the speedup, eases contention. Best first attempt IMO.
  3. Mark the error-handling and AbortController describes `.sequential` and leave the rest concurrent. Finest-grained, preserves max parallelism elsewhere, but requires per-block annotations.

Leaving this PR as draft for now. Happy to iterate as a follow-up.

The e2e suite tracked failed-run diagnostics via two module-level
mutables (`trackedRuns` and `currentTestName`) that every beforeEach
reset. Under describe.concurrent that races: when a test times out,
the displayed diagnostic typically belongs to whichever sibling task
most recently called setupRunTracking(). That's why PR #2083's first
CI run showed an unrelated `errorStepCrossFile` run as the
"diagnostic" for the FatalError catchability test that actually
timed out — leaving us blind to the real failure cause.
Switch to a `Map<taskId, PerTaskState>` keyed off
`getCurrentTest().id`, with the entry dropped via onTestFinished to
keep the map bounded. Also unconditionally log
`[trackRun] <test> → <runId>` so we can still correlate runs from
stdout even when diagnostics fetching itself stalls past the test's
deadline.
…Error type in readJSON
The previous writeExclusive used `fs.writeFile(path, data, { flag: 'wx' })`,
which is atomic for the file *creation* (O_CREAT|O_EXCL) but NOT for the
subsequent data write. A concurrent reader could catch the file with
zero bytes (or just `{`) before the writer's content reached the page
cache.
This race had been silently papered over by `readHookTokenClaim`, which
swallows any SyntaxError as "no claim exists" — which is wrong: a partial
write means a claim *does* exist, just not yet flushed, and treating it as
absent lets duplicate hook tokens through. PR #2083's concurrent e2e run
also surfaced the same race on runs/<id>.json creation, producing
size=0 byte run files in CI logs.
Fix: write content to a temp file first, then `link(2)` it atomically
into place. POSIX link is atomic and fails with EEXIST if the target
already exists, preserving the exclusive-claim semantics. Readers now
either see ENOENT or the fully-populated inode — never a half-written
file.
Also preserve the SyntaxError type when annotating readJSON failures
with file path / size / content snippet, so callers like
readHookTokenClaim that intentionally swallow `error instanceof SyntaxError`
continue to work (the previous diagnostic threw a plain Error and broke
them).
Vitest's default `maxConcurrency` of 5 saturates the workbench
preview deploys and the in-process world runtime under
`describe.concurrent`, pushing load-heavy tests (fibonacci tree
spawn, multi-step abort sequencing, AbortSignal.any composition)
into their per-test deadlines on PR #2083's run 4 even though
the same tests pass cleanly when run sequentially.
Drop to 3 in the root vitest config (only `test:e2e` and `bench`
consume it — per-package configs are untouched). The Vercel-deploy
matrix went from ~14-17min sequential → ~5-6min at concurrency 5;
trading some of that headroom for stability is the right call now
that the isolation/correctness fixes have landed.
Dropping vitest's default concurrency cap from 5 to 3 (commit c7060b1)
didn't reduce the remaining e2e flake count — runs 3/4 at default
concurrency=5 and run 5 at concurrency=3 all landed on exactly 15
failures. The remaining failures are external-network and source-map
class issues (httpbin/postman-echo abort tests, nuxt/nextjs-webpack
sourcemap assertions, fibonacci tree spawn timeouts) that lower test
parallelism doesn't address. Keeping the speedup is more valuable than
the small load reduction.
@VaguelySerious

Copy link
Copy Markdown
MemberAuthor

Update

Three substantive fixes layered on, then one revert. Final state of the PR:

What was fixed

  1. Per-task diagnostic isolation (a115774) — setupRunTracking tracked failed-run diagnostics via two module-level mutables (trackedRuns, currentTestName) that every beforeEach reset. Under describe.concurrent, every test clobbered every other's state, so the failing test's diagnostic typically showed an unrelated sibling's workflow. Switched to Map<taskId, PerTaskState> keyed off getCurrentTest().id. Also unconditionally log [trackRun] <test> → <runId> so even when diagnostics fetching stalls past a 60s timeout, the per-test runId is still in stdout for backend correlation.
  2. writeExclusive content atomicity (2fce0bb) — fs.writeFile(path, data, { flag: 'wx' }) is atomic for the file creation (O_CREAT|O_EXCL) but not for the data write that follows. A concurrent reader could observe a half-written file (e.g. just {), which the hook-token-claim path was silently swallowing as "no claim exists" — a real correctness bug. Run-creation hit the same race and produced size=0 run JSON files surfaced in CI logs. Switched to write-then-link(2): write data to a temp file first, then atomically link it into place. POSIX link is atomic and fails with EEXIST if the target exists, preserving the exclusive-claim semantics. Readers now either see ENOENT or the fully-populated inode.
  3. readJSON SyntaxError annotation (same commit) — annotates partial-read SyntaxError with file path + size + content snippet, preserving the SyntaxError type so existing handlers like readHookTokenClaim's instanceof SyntaxError catch still work.

What was tried and reverted

  • maxConcurrency: 3 (c7060b1 → reverted in 6e264eb) — runs 3/4 at concurrency=5 and run 5 at concurrency=3 all landed on exactly 15 e2e failures. The remaining flakes don't respond to lower test parallelism, so the cap traded speedup for nothing.

What's left (out of scope for this PR)

  • Sourcemap assertion failures on nuxt and nextjs-webpack: expect(stack).not.toContain('99_e2e.ts') fires because hasStepSourceMaps() reports false but the stack does contain the .ts filename. Pre-existing test/runtime mismatch — independent of concurrency, just hits more often now that more workflows run per CI invocation. Needs separate investigation into the source-map detection logic.
  • External-network flakes on abort tests (abortFetchInFlightWorkflow, abortFetchUncaughtWorkflow, etc.): httpbin/postman-echo delay endpoints intermittently return early from GH Actions runners. [tests] Fix abort-fetch e2e flake #2081 added a fallback URL pair; under heavier concurrent load both upstreams flake. The proper fix is a per-workbench /api/delay/:n route.
  • Fibonacci tree spawn timeouts on Vercel Prod: fibonacciWorkflow with fib(6) spawns a tree of child runs; under concurrent load this saturates the workflow scheduler past the test's 180s budget. Could be addressed with a smaller fib(N), a longer budget, or marking that single test .sequential.

Verdict

The actual concurrency-introduced isolation/correctness bugs are fixed. Concurrency speedup holds (~3× job wall-clock; ~4–5× pure vitest run). The remaining failures are pre-existing issues that the concurrency change has made more visible but doesn't itself cause. Happy to keep this draft until those classes are tackled, or to merge with a green CI rerun depending on appetite.

Sign up for freeto 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

@VaguelySerious
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Strip utm_, fbclid, gclid, etc. from all links on page\n(function() {\n var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content',\n 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid',\n 'ref', 'ref_src', 'source', 'medium', 'campaign'];\n \n function cleanUrl(url) {\n try {\n var u = new URL(url, window.location.origin);\n var changed = false;\n trackingParams.forEach(function(p) {\n if (u.searchParams.has(p)) {\n u.searchParams.delete(p);\n changed = true;\n }\n });\n return changed ? u.toString() : url;\n } catch (e) {\n return url;\n }\n }\n \n function cleanLinks() {\n document.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n \n cleanLinks();\n \n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1) {\n if (node.tagName === 'A') cleanLinks();\n node.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Remove Tracking Parameters from Links"); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + '
Skip to content

[tests] Re-enable concurrent e2e suite - #2083

Closed
VaguelySerious wants to merge 6 commits into
mainfrom
peter/enable-e2e-concurrency
Closed

[tests] Re-enable concurrent e2e suite#2083
VaguelySerious wants to merge 6 commits into
mainfrom
peter/enable-e2e-concurrency

Conversation

@VaguelySerious

Copy link
Copy Markdown
Member

Summary

  • Re-enables describe.concurrent in packages/core/e2e/e2e.test.ts, which was made sequential in 78048e0 ("no concurrency") with a TODO to re-enable.
  • Each workbench currently runs ~87 e2e tests serially against its preview deploy; this is the dominant ~15-minute cost per matrix entry.
  • This is a draft to see which (if any) tests are still flake-prone under concurrency after recent fixes (e.g. the abort-fetch class of flakes in [tests] Fix abort-fetch e2e flake #2081).

Test plan

  • All workbench matrix entries (E2E Vercel Prod) pass
  • Compare wall-clock vs. recent main runs
  • If failures cluster around specific tests, mark them .sequential rather than reverting the whole flip

Notes

If concurrency exposes real isolation bugs (shared identifiers, cross-test state), I'll fix those before unwinding the draft state. If it's only known-flaky externals, those should already have been addressed in #2081 / follow-ups.

🤖 Generated with Claude Code

Flips packages/core/e2e/e2e.test.ts back to describe.concurrent. The
suite was made sequential ("temporarily disabling concurrent tests to
avoid flakiness") and never re-enabled. Each workbench now runs ~87
tests serially against its preview deploy, dominating CI wall-clock.
Putting this up as a draft to see which (if any) tests are still
flake-prone under concurrency, after recent fixes to the abort-fetch
class of flakes.
@vercel

vercelBot commented May 22, 2026

Copy link
Copy Markdown
Contributor

@changeset-bot

changeset-botBot commented May 22, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: c7060b1

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

This PR includes changesets to release 18 packages
NameType
@workflow/world-localPatch
@workflow/cliPatch
@workflow/corePatch
@workflow/vitestPatch
@workflow/world-postgresPatch
workflowPatch
@workflow/world-testingPatch
@workflow/buildersPatch
@workflow/nextPatch
@workflow/nitroPatch
@workflow/web-sharedPatch
@workflow/webPatch
@workflow/astroPatch
@workflow/nestPatch
@workflow/rollupPatch
@workflow/sveltekitPatch
@workflow/vitePatch
@workflow/nuxtPatch

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-actionsBot commented May 22, 2026

Copy link
Copy Markdown
Contributor

📊 Benchmark Results

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

workflow with no steps

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
💻 Local🥇 Nitro0.032s (-26.0% 🟢)1.006s (~)0.974s101.00x
💻 LocalExpress0.032s (-26.9% 🟢)1.006s (~)0.974s101.02x
💻 LocalNext.js (Turbopack)0.034s1.005s0.972s101.06x
🐘 PostgresExpress0.050s (-13.1% 🟢)1.012s (~)0.961s101.58x
🐘 PostgresNitro0.051s (-46.3% 🟢)1.013s (-2.9%)0.962s101.60x
🐘 PostgresNext.js (Turbopack)0.056s1.011s0.955s101.77x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Next.js (Turbopack)0.306s (+21.6% 🔺)2.546s (+9.1% 🔺)2.240s101.00x
▲ VercelNitro0.426s (+4.0%)2.554s (+1.8%)2.128s101.39x
▲ VercelExpress⚠️missing----

🔍 Observability: Next.js (Turbopack) | Nitro

workflow with 1 step

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
💻 Local🥇 Nitro1.076s (-4.9%)2.007s (~)0.931s101.00x
💻 LocalExpress1.079s (-4.1%)2.007s (~)0.928s101.00x
🐘 PostgresNitro1.082s (-5.1% 🟢)2.008s (~)0.926s101.01x
🐘 PostgresExpress1.087s (-5.2% 🟢)2.009s (~)0.922s101.01x
💻 LocalNext.js (Turbopack)1.089s2.005s0.916s101.01x
🐘 PostgresNext.js (Turbopack)1.117s2.008s0.891s101.04x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Nitro1.806s (-53.6% 🟢)3.829s (-35.2% 🟢)2.023s101.00x
▲ VercelNext.js (Turbopack)1.906s (-6.3% 🟢)3.724s (-2.8%)1.818s101.06x
▲ VercelExpress⚠️missing----

🔍 Observability: Nitro | Next.js (Turbopack)

workflow with 10 sequential steps

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Nitro10.387s (-4.4%)11.018s (~)0.630s31.00x
💻 LocalExpress10.434s (-4.5%)11.023s (~)0.590s31.00x
💻 LocalNitro10.434s (-4.7%)11.024s (~)0.590s31.00x
🐘 PostgresExpress10.435s (-4.8%)11.014s (~)0.579s31.00x
💻 LocalNext.js (Turbopack)10.493s11.021s0.528s31.01x
🐘 PostgresNext.js (Turbopack)10.702s11.020s0.318s31.03x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Nitro13.961s (-41.2% 🟢)16.231s (-35.4% 🟢)2.269s21.00x
▲ VercelNext.js (Turbopack)14.589s (-15.8% 🟢)16.525s (-14.8% 🟢)1.936s21.04x
▲ VercelExpress⚠️missing----

🔍 Observability: Nitro | Next.js (Turbopack)

workflow with 25 sequential steps

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Nitro13.428s (-8.0% 🟢)14.016s (-6.7% 🟢)0.588s51.00x
🐘 PostgresExpress13.500s (-7.4% 🟢)14.019s (-6.7% 🟢)0.518s51.01x
💻 LocalExpress13.527s (-9.6% 🟢)14.027s (-6.7% 🟢)0.500s51.01x
💻 LocalNitro13.592s (-9.8% 🟢)14.027s (-12.5% 🟢)0.435s51.01x
💻 LocalNext.js (Turbopack)13.761s14.025s0.264s51.02x
🐘 PostgresNext.js (Turbopack)14.104s15.019s0.915s41.05x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Nitro21.896s (-66.0% 🟢)23.781s (-64.3% 🟢)1.885s31.00x
▲ VercelNext.js (Turbopack)22.228s (-57.7% 🟢)24.764s (-54.7% 🟢)2.536s31.02x
▲ VercelExpress⚠️missing----

🔍 Observability: Nitro | Next.js (Turbopack)

workflow with 50 sequential steps

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Nitro11.854s (-15.1% 🟢)12.015s (-16.0% 🟢)0.161s81.00x
💻 LocalNitro11.923s (-29.0% 🟢)12.021s (-29.4% 🟢)0.098s81.01x
🐘 PostgresExpress11.984s (-14.4% 🟢)12.017s (-17.7% 🟢)0.033s81.01x
💻 LocalExpress12.021s (-27.6% 🟢)12.399s (-27.2% 🟢)0.379s81.01x
💻 LocalNext.js (Turbopack)12.282s13.022s0.740s71.04x
🐘 PostgresNext.js (Turbopack)13.164s14.015s0.851s71.11x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Nitro31.366s (-92.6% 🟢)33.410s (-92.1% 🟢)2.044s31.00x
▲ VercelNext.js (Turbopack)32.382s (-91.8% 🟢)34.323s (-91.3% 🟢)1.940s31.03x
▲ VercelExpress⚠️missing----

🔍 Observability: Nitro | Next.js (Turbopack)

Promise.all with 10 concurrent steps

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Nitro1.158s (-9.2% 🟢)2.009s (~)0.851s151.00x
🐘 PostgresExpress1.160s (-8.0% 🟢)2.008s (~)0.848s151.00x
💻 LocalNitro1.172s (-28.2% 🟢)2.006s (-3.3%)0.835s151.01x
💻 LocalExpress1.179s (-20.8% 🟢)2.007s (~)0.828s151.02x
💻 LocalNext.js (Turbopack)1.212s2.005s0.793s151.05x
🐘 PostgresNext.js (Turbopack)1.218s2.008s0.790s151.05x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Nitro2.877s (+2.1%)4.515s (+4.5%)1.638s81.00x
▲ VercelNext.js (Turbopack)2.967s (-12.7% 🟢)4.728s (-4.2%)1.761s71.03x
▲ VercelExpress⚠️missing----

🔍 Observability: Nitro | Next.js (Turbopack)

Promise.all with 25 concurrent steps

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Express1.212s (-48.6% 🟢)2.007s (-33.3% 🟢)0.795s151.00x
🐘 PostgresNitro1.224s (-47.9% 🟢)2.009s (-33.2% 🟢)0.785s151.01x
🐘 PostgresNext.js (Turbopack)1.336s2.007s0.671s151.10x
💻 LocalNext.js (Turbopack)1.524s2.004s0.480s151.26x
💻 LocalNitro1.744s (-44.5% 🟢)2.007s (-48.3% 🟢)0.264s151.44x
💻 LocalExpress1.835s (-37.8% 🟢)2.140s (-38.0% 🟢)0.304s151.51x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Next.js (Turbopack)4.146s (-41.6% 🟢)6.056s (-32.0% 🟢)1.910s51.00x
▲ VercelNitro5.247s (+29.5% 🔺)7.231s (+22.1% 🔺)1.984s51.27x
▲ VercelExpress⚠️missing----

🔍 Observability: Next.js (Turbopack) | Nitro

Promise.all with 50 concurrent steps

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Express1.339s (-61.6% 🟢)2.007s (-49.9% 🟢)0.669s151.00x
🐘 PostgresNitro1.358s (-61.0% 🟢)2.007s (-49.9% 🟢)0.649s151.01x
🐘 PostgresNext.js (Turbopack)1.617s2.007s0.390s151.21x
💻 LocalNext.js (Turbopack)3.177s3.453s0.276s92.37x
💻 LocalExpress5.435s (-34.8% 🟢)6.214s (-31.2% 🟢)0.779s54.06x
💻 LocalNitro5.468s (-34.5% 🟢)6.012s (-33.3% 🟢)0.544s54.08x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Nitro6.778s (+92.3% 🔺)8.495s (+53.5% 🔺)1.716s41.00x
▲ VercelNext.js (Turbopack)8.699s (-2.4%)11.031s (+0.6%)2.332s31.28x
▲ VercelExpress⚠️missing----

🔍 Observability: Nitro | Next.js (Turbopack)

Promise.race with 10 concurrent steps

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Express1.142s (-9.1% 🟢)2.009s (~)0.866s151.00x
🐘 PostgresNitro1.152s (-8.3% 🟢)2.008s (~)0.856s151.01x
🐘 PostgresNext.js (Turbopack)1.209s2.008s0.800s151.06x
💻 LocalNext.js (Turbopack)1.209s2.004s0.795s151.06x
💻 LocalNitro1.417s (-24.1% 🟢)2.006s (-14.3% 🟢)0.590s151.24x
💻 LocalExpress1.427s (-24.7% 🟢)2.006s (-15.1% 🟢)0.580s151.25x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Next.js (Turbopack)2.873s (-2.0%)4.428s (-4.6%)1.555s71.00x
▲ VercelNitro2.925s (+18.9% 🔺)5.006s (+20.1% 🔺)2.081s61.02x
▲ VercelExpress⚠️missing----

🔍 Observability: Next.js (Turbopack) | Nitro

Promise.race with 25 concurrent steps

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Nitro1.214s (-48.1% 🟢)2.008s (-33.3% 🟢)0.794s151.00x
🐘 PostgresExpress1.226s (-47.6% 🟢)2.009s (-33.3% 🟢)0.783s151.01x
🐘 PostgresNext.js (Turbopack)1.343s2.009s0.666s151.11x
💻 LocalNext.js (Turbopack)1.579s2.005s0.426s151.30x
💻 LocalNitro2.034s (-33.6% 🟢)2.510s (-35.4% 🟢)0.475s121.68x
💻 LocalExpress2.057s (-34.3% 🟢)2.508s (-33.3% 🟢)0.451s121.69x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Nitro3.368s (+4.2%)5.319s (+4.8%)1.951s61.00x
▲ VercelNext.js (Turbopack)4.111s (+30.8% 🔺)5.838s (+29.1% 🔺)1.726s61.22x
▲ VercelExpress⚠️missing----

🔍 Observability: Nitro | Next.js (Turbopack)

Promise.race with 50 concurrent steps

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Nitro1.316s (-62.2% 🟢)2.009s (-49.9% 🟢)0.693s151.00x
🐘 PostgresExpress1.334s (-61.9% 🟢)2.007s (-50.0% 🟢)0.673s151.01x
🐘 PostgresNext.js (Turbopack)1.644s2.008s0.364s151.25x
💻 LocalNext.js (Turbopack)3.454s4.012s0.558s82.62x
💻 LocalExpress5.760s (-34.6% 🟢)6.216s (-33.0% 🟢)0.456s54.38x
💻 LocalNitro5.993s (-34.5% 🟢)6.513s (-35.0% 🟢)0.521s64.55x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Next.js (Turbopack)5.982s (-11.5% 🟢)7.562s (-11.5% 🟢)1.580s41.00x
▲ VercelNitro6.436s (+26.4% 🔺)8.067s (+18.3% 🔺)1.632s41.08x
▲ VercelExpress⚠️missing----

🔍 Observability: Next.js (Turbopack) | Nitro

workflow with 10 sequential data payload steps (10KB)

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Nitro0.428s (-47.8% 🟢)1.006s (~)0.578s601.00x
🐘 PostgresExpress0.467s (-44.3% 🟢)1.006s (-1.7%)0.539s601.09x
💻 LocalNitro0.493s (-49.7% 🟢)1.022s (-6.6% 🟢)0.529s591.15x
💻 LocalExpress0.517s (-47.4% 🟢)1.022s (-5.0% 🟢)0.504s591.21x
💻 LocalNext.js (Turbopack)0.615s1.021s0.406s591.44x
🐘 PostgresNext.js (Turbopack)0.675s1.006s0.332s601.58x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Nitro5.901s (-73.2% 🟢)7.712s (-67.9% 🟢)1.811s81.00x
▲ VercelNext.js (Turbopack)6.006s (-58.6% 🟢)7.961s (-50.5% 🟢)1.956s81.02x
▲ VercelExpress⚠️missing----

🔍 Observability: Nitro | Next.js (Turbopack)

workflow with 25 sequential data payload steps (10KB)

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Nitro0.995s (-48.4% 🟢)1.350s (-35.7% 🟢)0.355s671.00x
🐘 PostgresExpress1.188s (-39.9% 🟢)1.922s (-14.9% 🟢)0.734s471.19x
💻 LocalNitro1.220s (-59.8% 🟢)2.006s (-46.6% 🟢)0.786s451.23x
💻 LocalExpress1.260s (-58.2% 🟢)2.007s (-44.0% 🟢)0.746s451.27x
💻 LocalNext.js (Turbopack)1.421s2.004s0.583s451.43x
🐘 PostgresNext.js (Turbopack)1.660s2.008s0.347s451.67x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Nitro16.140s (-59.1% 🟢)18.101s (-56.2% 🟢)1.961s61.00x
▲ VercelNext.js (Turbopack)16.668s (-66.5% 🟢)19.003s (-63.3% 🟢)2.335s51.03x
▲ VercelExpress⚠️missing----

🔍 Observability: Nitro | Next.js (Turbopack)

workflow with 50 sequential data payload steps (10KB)

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Nitro1.994s (-51.4% 🟢)2.335s (-49.3% 🟢)0.341s521.00x
🐘 PostgresExpress2.106s (-47.2% 🟢)2.697s (-38.3% 🟢)0.591s451.06x
💻 LocalNitro2.786s (-70.0% 🟢)3.058s (-69.5% 🟢)0.272s401.40x
💻 LocalExpress2.801s (-69.6% 🟢)3.033s (-69.7% 🟢)0.231s401.41x
🐘 PostgresNext.js (Turbopack)3.212s4.009s0.797s301.61x
💻 LocalNext.js (Turbopack)3.466s4.074s0.608s301.74x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Nitro27.620s (-71.5% 🟢)29.816s (-69.7% 🟢)2.196s51.00x
▲ VercelNext.js (Turbopack)32.787s (-69.4% 🟢)35.186s (-67.7% 🟢)2.399s41.19x
▲ VercelExpress⚠️missing----

🔍 Observability: Nitro | Next.js (Turbopack)

workflow with 10 concurrent data payload steps (10KB)

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Nitro0.190s (-33.1% 🟢)1.006s (~)0.816s601.00x
🐘 PostgresExpress0.198s (-30.0% 🟢)1.023s (+1.5%)0.825s591.04x
🐘 PostgresNext.js (Turbopack)0.230s1.006s0.776s601.21x
💻 LocalNext.js (Turbopack)0.337s1.003s0.666s601.78x
💻 LocalExpress0.381s (-32.0% 🟢)1.004s (~)0.623s602.01x
💻 LocalNitro0.399s (-34.0% 🟢)1.004s (-1.7%)0.605s602.11x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Nitro2.453s (+47.7% 🔺)4.362s (+30.2% 🔺)1.909s141.00x
▲ VercelNext.js (Turbopack)2.813s (+39.1% 🔺)4.846s (+27.8% 🔺)2.034s131.15x
▲ VercelExpress⚠️missing----

🔍 Observability: Nitro | Next.js (Turbopack)

workflow with 25 concurrent data payload steps (10KB)

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Nitro0.284s (-42.8% 🟢)1.006s (~)0.722s901.00x
🐘 PostgresExpress0.308s (-39.6% 🟢)1.006s (~)0.698s901.08x
🐘 PostgresNext.js (Turbopack)0.435s1.006s0.571s901.53x
💻 LocalNext.js (Turbopack)1.433s2.028s0.595s455.05x
💻 LocalExpress2.159s (-14.1% 🟢)2.715s (-9.8% 🟢)0.556s347.60x
💻 LocalNitro2.171s (-14.5% 🟢)2.884s (-4.1%)0.713s327.65x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Nitro5.296s (+64.2% 🔺)6.881s (+42.7% 🔺)1.585s141.00x
▲ VercelNext.js (Turbopack)6.720s (+90.1% 🔺)8.608s (+65.8% 🔺)1.888s111.27x
▲ VercelExpress⚠️missing----

🔍 Observability: Nitro | Next.js (Turbopack)

workflow with 50 concurrent data payload steps (10KB)

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Nitro0.583s (-26.2% 🟢)1.006s (~)0.422s1201.00x
🐘 PostgresExpress0.652s (-20.4% 🟢)1.006s (-1.1%)0.355s1201.12x
🐘 PostgresNext.js (Turbopack)0.890s1.078s0.188s1121.53x
💻 LocalNext.js (Turbopack)6.974s7.706s0.731s1611.96x
💻 LocalNitro10.196s (-8.9% 🟢)10.777s (-7.6% 🟢)0.581s1217.48x
💻 LocalExpress10.526s (-5.9% 🟢)11.031s (-7.6% 🟢)0.505s1118.04x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Next.js (Turbopack)15.606s (+51.1% 🔺)17.743s (+44.4% 🔺)2.137s71.00x
▲ VercelNitro16.197s (+109.7% 🔺)18.211s (+93.7% 🔺)2.014s71.04x
▲ VercelExpress⚠️missing----

🔍 Observability: Next.js (Turbopack) | Nitro

Stream Benchmarks(includes TTFB metrics)
workflow with stream

💻 Local Development

WorldFrameworkWorkflow TimeTTFBSlurpWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Nitro1.128s (+450.3% 🔺)2.001s (+100.2% 🔺)0.001s (-33.3% 🟢)2.010s (+98.8% 🔺)0.882s101.00x
💻 LocalNext.js (Turbopack)1.129s2.002s0.006s2.011s0.882s101.00x
💻 LocalNitro1.133s (+430.0% 🔺)2.005s (+99.6% 🔺)0.013s (+4.0%)2.020s (+98.3% 🔺)0.888s101.00x
💻 LocalExpress1.138s (+471.7% 🔺)2.006s (+99.7% 🔺)0.013s (+5.8% 🔺)2.021s (+98.5% 🔺)0.883s101.01x
🐘 PostgresExpress1.145s (+458.3% 🔺)2.001s (+100.4% 🔺)0.001s (-18.8% 🟢)2.010s (+98.7% 🔺)0.865s101.01x
🐘 PostgresNext.js (Turbopack)1.201s2.001s0.002s2.010s0.809s101.06x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeTTFBSlurpWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Next.js (Turbopack)2.444s (-64.3% 🟢)3.510s (-59.4% 🟢)2.280s (+260.8% 🔺)6.380s (-34.8% 🟢)3.936s101.00x
▲ VercelNitro2.490s (-35.0% 🟢)3.601s (-31.8% 🟢)13.675s (+1742.7% 🔺)17.941s (+176.7% 🔺)15.451s101.02x
▲ VercelExpress⚠️missing-----

🔍 Observability: Next.js (Turbopack) | Nitro

stream pipeline with 5 transform steps (1MB)

💻 Local Development

WorldFrameworkWorkflow TimeTTFBSlurpWall TimeOverheadSamplesvs Fastest
💻 Local🥇 Next.js (Turbopack)1.475s2.007s0.006s2.015s0.540s301.00x
🐘 PostgresNitro1.494s (+139.3% 🔺)2.002s (+98.9% 🔺)0.004s (+6.4% 🔺)2.027s (+98.3% 🔺)0.534s301.01x
💻 LocalNitro1.541s (+83.7% 🔺)2.013s (+98.9% 🔺)0.010s (+4.9%)2.025s (+81.5% 🔺)0.484s301.04x
🐘 PostgresExpress1.543s (+144.9% 🔺)2.004s (+99.1% 🔺)0.004s (+3.6%)2.028s (+98.2% 🔺)0.484s301.05x
💻 LocalExpress1.549s (+104.6% 🔺)2.013s (+95.7% 🔺)0.010s (+9.4% 🔺)2.026s (+94.8% 🔺)0.476s301.05x
🐘 PostgresNext.js (Turbopack)1.678s2.010s0.004s2.025s0.347s301.14x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeTTFBSlurpWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Next.js (Turbopack)5.989s (-64.6% 🟢)7.269s (-60.1% 🟢)0.284s (+34.4% 🔺)8.226s (-56.6% 🟢)2.237s81.00x
▲ VercelNitro6.280s (-78.7% 🟢)7.470s (-75.8% 🟢)0.267s (+138.6% 🔺)8.303s (-73.9% 🟢)2.023s81.05x
▲ VercelExpress⚠️missing-----

🔍 Observability: Next.js (Turbopack) | Nitro

10 parallel streams (1MB each)

💻 Local Development

WorldFrameworkWorkflow TimeTTFBSlurpWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Nitro0.640s (-34.0% 🟢)1.012s (-18.8% 🟢)0.000s (+22.0% 🔺)1.030s (-18.1% 🟢)0.391s591.00x
🐘 PostgresExpress0.664s (-30.9% 🟢)1.011s (-20.9% 🟢)0.000s (-61.0% 🟢)1.032s (-21.0% 🟢)0.369s591.04x
🐘 PostgresNext.js (Turbopack)0.766s1.017s0.000s1.032s0.266s591.20x
💻 LocalNext.js (Turbopack)1.042s1.631s0.000s1.634s0.592s371.63x
💻 LocalExpress1.344s (+9.7% 🔺)2.016s (~)0.000s (-30.0% 🟢)2.018s (~)0.674s302.10x
💻 LocalNitro1.373s (+12.3% 🔺)2.016s (~)0.000s (+66.7% 🔺)2.018s (~)0.645s302.15x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeTTFBSlurpWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Nitro3.957s (+29.7% 🔺)5.319s (+21.1% 🔺)0.000s (+18.2% 🔺)5.894s (+22.6% 🔺)1.937s111.00x
▲ VercelNext.js (Turbopack)3.981s (-60.9% 🟢)5.285s (-54.1% 🟢)0.000s (+Infinity% 🔺)5.843s (-51.5% 🟢)1.862s111.01x
▲ VercelExpress⚠️missing-----

🔍 Observability: Nitro | Next.js (Turbopack)

fan-out fan-in 10 streams (1MB each)

💻 Local Development

WorldFrameworkWorkflow TimeTTFBSlurpWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Nitro1.278s (-28.7% 🟢)2.029s (-5.2% 🟢)0.000s (-100.0% 🟢)2.049s (-5.7% 🟢)0.771s301.00x
🐘 PostgresExpress1.384s (-21.9% 🟢)2.029s (-6.8% 🟢)0.000s (+Infinity% 🔺)2.044s (-7.1% 🟢)0.659s301.08x
🐘 PostgresNext.js (Turbopack)1.500s2.145s0.000s2.153s0.654s281.17x
💻 LocalNext.js (Turbopack)2.061s2.523s0.000s2.528s0.467s241.61x
💻 LocalExpress3.109s (-10.3% 🟢)3.781s (-6.3% 🟢)0.001s (-21.9% 🟢)3.784s (-6.3% 🟢)0.675s162.43x
💻 LocalNitro3.183s (-6.0% 🟢)3.903s (-3.2%)0.000s (-18.0% 🟢)3.906s (-3.2%)0.723s162.49x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeTTFBSlurpWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Nitro5.980s (+46.1% 🔺)7.651s (+42.4% 🔺)0.000s (-54.2% 🟢)8.181s (+41.2% 🔺)2.201s81.00x
▲ VercelNext.js (Turbopack)6.105s (+8.7% 🔺)7.426s (+6.4% 🔺)0.000s (~)7.952s (+5.5% 🔺)1.847s81.02x
▲ VercelExpress⚠️missing-----

🔍 Observability: Nitro | Next.js (Turbopack)

Summary

Fastest Framework by World

Winner determined by most benchmark wins

World🥇 Fastest FrameworkWins
💻 LocalNext.js (Turbopack)12/21
🐘 PostgresNitro17/21
▲ VercelNitro14/21
Fastest World by Framework

Winner determined by most benchmark wins

Framework🥇 Fastest WorldWins
Express🐘 Postgres17/21
Next.js (Turbopack)🐘 Postgres11/21
Nitro🐘 Postgres19/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)

📋 View full workflow run


Some benchmark jobs failed:

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

Check the workflow run for details.

@github-actions

github-actionsBot commented May 22, 2026

Copy link
Copy Markdown
Contributor

🧪 E2E Test Results

Some tests failed

Summary

PassedFailedSkippedTotal
❌ ▲ Vercel Production121932191441
❌ 💻 Local Development161052191834
❌ 📦 Local Production161412191834
❌ 🐘 Local Postgres150212001703
✅ 🪟 Windows13100131
❌ 📋 Other7401176917
Total68161110337860

❌ Failed Tests

▲ Vercel Production (3 failed)

nitro (1 failed):

  • outputStreamInsideStepWorkflow - getWritable() called inside step functions | wrun_01KS7ZJGW73X7CKYTEQBGY87E5 | 🔍 observability

nuxt (1 failed):

  • hookWorkflow is not resumable via public webhook endpoint | wrun_01KS7ZJ3YXC4E16B345GAMR59H | 🔍 observability

sveltekit (1 failed):

💻 Local Development (5 failed)

astro-stable (2 failed):

  • fibonacciWorkflow - recursive workflow composition via start() | wrun_01KS7ZKMSDH7F5ZJHKWBKZDJPQ
  • AbortController abortExternalSignalInFlightWorkflow: external abort fires mid-flight, propagates to nested steps

nextjs-webpack-stable-lazy-discovery-disabled (1 failed):

  • error handling error propagation step errors basic step error preserves message and stack trace

nuxt-stable (1 failed):

  • AbortController abortExternalSignalInFlightWorkflow: external abort fires mid-flight, propagates to nested steps

sveltekit-stable (1 failed):

  • AbortController abortExternalSignalInFlightWorkflow: external abort fires mid-flight, propagates to nested steps
📦 Local Production (1 failed)

nuxt-stable (1 failed):

  • error handling error propagation step errors basic step error preserves message and stack trace
🐘 Local Postgres (1 failed)

nuxt-stable (1 failed):

  • error handling error propagation step errors basic step error preserves message and stack trace
📋 Other (1 failed)

e2e-local-dev-tanstack-start- (1 failed):

  • AbortController abortExternalSignalInFlightWorkflow: external abort fires mid-flight, propagates to nested steps

Details by Category

❌ ▲ Vercel Production
AppPassedFailedSkipped
✅ astro105026
✅ example105026
✅ express105026
✅ fastify105026
✅ hono105026
✅ nextjs-turbopack12902
✅ nextjs-webpack12902
❌ nitro104126
❌ nuxt104126
❌ sveltekit12317
✅ vite105026
❌ 💻 Local Development
AppPassedFailedSkipped
❌ astro-stable104225
✅ express-stable106025
✅ fastify-stable106025
✅ hono-stable106025
✅ nextjs-turbopack-canary112019
✅ nextjs-turbopack-stable-lazy-discovery-disabled13100
✅ nextjs-turbopack-stable-lazy-discovery-enabled13100
✅ nextjs-webpack-canary112019
❌ nextjs-webpack-stable-lazy-discovery-disabled13010
✅ nextjs-webpack-stable-lazy-discovery-enabled13100
✅ nitro-stable106025
❌ nuxt-stable105125
❌ sveltekit-stable12416
✅ vite-stable106025
❌ 📦 Local Production
AppPassedFailedSkipped
✅ astro-stable106025
✅ express-stable106025
✅ fastify-stable106025
✅ hono-stable106025
✅ nextjs-turbopack-canary112019
✅ nextjs-turbopack-stable-lazy-discovery-disabled13100
✅ nextjs-turbopack-stable-lazy-discovery-enabled13100
✅ nextjs-webpack-canary112019
✅ nextjs-webpack-stable-lazy-discovery-disabled13100
✅ nextjs-webpack-stable-lazy-discovery-enabled13100
✅ nitro-stable106025
❌ nuxt-stable105125
✅ sveltekit-stable12506
✅ vite-stable106025
❌ 🐘 Local Postgres
AppPassedFailedSkipped
✅ astro-stable106025
✅ express-stable106025
✅ fastify-stable106025
✅ hono-stable106025
✅ nextjs-turbopack-stable-lazy-discovery-disabled13100
✅ nextjs-turbopack-stable-lazy-discovery-enabled13100
✅ nextjs-webpack-canary112019
✅ nextjs-webpack-stable-lazy-discovery-disabled13100
✅ nextjs-webpack-stable-lazy-discovery-enabled13100
✅ nitro-stable106025
❌ nuxt-stable105125
✅ sveltekit-stable12506
✅ vite-stable106025
✅ 🪟 Windows
AppPassedFailedSkipped
✅ nextjs-turbopack13100
❌ 📋 Other
AppPassedFailedSkipped
✅ e2e-local-dev-nest-stable106025
❌ e2e-local-dev-tanstack-start-105125
✅ e2e-local-postgres-nest-stable106025
✅ e2e-local-postgres-tanstack-start-106025
✅ e2e-local-prod-nest-stable106025
✅ e2e-local-prod-tanstack-start-106025
✅ e2e-vercel-prod-tanstack-start105026

📋 View full workflow run


Some E2E test jobs failed:

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

Check the workflow run for details.

@VaguelySerious

Copy link
Copy Markdown
MemberAuthor

CI verdict

Speedup is real. Per-workbench wall-clock dropped roughly 3×: from ~14–17 min on main to ~5–6 min here. Vitest's own run is even more dramatic — express went from ~15 min to 203s (~4–5× on the actual test phase).

But correctness regressed. 25 checks failed: every `E2E Vercel Prod Tests (*)` matrix entry except `nitro`, plus several `E2E Local Dev/Prod/Postgres` jobs and `E2E Windows Tests`.

Failure pattern

Every failure I sampled is the same shape: `Error: Test timed out in 60000ms`. The which tests fail varies per workbench — i.e. it's not a single broken test:

WorkbenchFailed test
expresserror handling > catchability > FatalError can be caught… (×2)
nuxtabortThrowIfAbortedWorkflow, step throw round-trips FatalError
astroabortDeterministicBranchFromStepWorkflow, abortListenerWorkflow, cross-file step error
honoFatalError fails immediately without retries
tanstack-startabortHookOrderingWorkflow (×2), abortReasonWorkflow
fastifyabortHookOrderingWorkflow [hook-first-hook-first]
sveltekitFatalError fails immediately without retries
viteabortDeterministicBranchWorkflow, abortVoidSleepTimeoutWorkflow

The cluster is real though: AbortController + error-handling tests, which all do multiple sequential workflow round-trips and historically run close to the 60s budget.

Diagnosis

Default vitest `maxConcurrency` is 5. With five workflow runs hammering one workbench in parallel, the heavier multi-round-trip tests appear to queue past the per-test 60s budget. Local-Postgres/Prod/Dev also fail, so it's not Vercel-specific — workflow-server (local) and the in-process world also slow under contention.

Proposed next steps (separate PRs)

Holding off on bundling point (5) (shortening `sleep()` durations) into this PR — the suite isn't healthy enough to add another variable.

Three angles, in order of cheapest:

  1. Bump default `testTimeout` from 60s to ~180s (in `vitest.config.ts`). Cheap, but masks the queueing rather than fixing it. May allow individual tests to silently regress to 3× their real cost.
  2. Lower `maxConcurrency` to 3 (config-level or `describe.concurrent(name, { concurrent: 3 }, ...)`). Keeps most of the speedup, eases contention. Best first attempt IMO.
  3. Mark the error-handling and AbortController describes `.sequential` and leave the rest concurrent. Finest-grained, preserves max parallelism elsewhere, but requires per-block annotations.

Leaving this PR as draft for now. Happy to iterate as a follow-up.

The e2e suite tracked failed-run diagnostics via two module-level
mutables (`trackedRuns` and `currentTestName`) that every beforeEach
reset. Under describe.concurrent that races: when a test times out,
the displayed diagnostic typically belongs to whichever sibling task
most recently called setupRunTracking(). That's why PR #2083's first
CI run showed an unrelated `errorStepCrossFile` run as the
"diagnostic" for the FatalError catchability test that actually
timed out — leaving us blind to the real failure cause.
Switch to a `Map<taskId, PerTaskState>` keyed off
`getCurrentTest().id`, with the entry dropped via onTestFinished to
keep the map bounded. Also unconditionally log
`[trackRun] <test> → <runId>` so we can still correlate runs from
stdout even when diagnostics fetching itself stalls past the test's
deadline.
…Error type in readJSON
The previous writeExclusive used `fs.writeFile(path, data, { flag: 'wx' })`,
which is atomic for the file *creation* (O_CREAT|O_EXCL) but NOT for the
subsequent data write. A concurrent reader could catch the file with
zero bytes (or just `{`) before the writer's content reached the page
cache.
This race had been silently papered over by `readHookTokenClaim`, which
swallows any SyntaxError as "no claim exists" — which is wrong: a partial
write means a claim *does* exist, just not yet flushed, and treating it as
absent lets duplicate hook tokens through. PR #2083's concurrent e2e run
also surfaced the same race on runs/<id>.json creation, producing
size=0 byte run files in CI logs.
Fix: write content to a temp file first, then `link(2)` it atomically
into place. POSIX link is atomic and fails with EEXIST if the target
already exists, preserving the exclusive-claim semantics. Readers now
either see ENOENT or the fully-populated inode — never a half-written
file.
Also preserve the SyntaxError type when annotating readJSON failures
with file path / size / content snippet, so callers like
readHookTokenClaim that intentionally swallow `error instanceof SyntaxError`
continue to work (the previous diagnostic threw a plain Error and broke
them).
Vitest's default `maxConcurrency` of 5 saturates the workbench
preview deploys and the in-process world runtime under
`describe.concurrent`, pushing load-heavy tests (fibonacci tree
spawn, multi-step abort sequencing, AbortSignal.any composition)
into their per-test deadlines on PR #2083's run 4 even though
the same tests pass cleanly when run sequentially.
Drop to 3 in the root vitest config (only `test:e2e` and `bench`
consume it — per-package configs are untouched). The Vercel-deploy
matrix went from ~14-17min sequential → ~5-6min at concurrency 5;
trading some of that headroom for stability is the right call now
that the isolation/correctness fixes have landed.
Dropping vitest's default concurrency cap from 5 to 3 (commit c7060b1)
didn't reduce the remaining e2e flake count — runs 3/4 at default
concurrency=5 and run 5 at concurrency=3 all landed on exactly 15
failures. The remaining failures are external-network and source-map
class issues (httpbin/postman-echo abort tests, nuxt/nextjs-webpack
sourcemap assertions, fibonacci tree spawn timeouts) that lower test
parallelism doesn't address. Keeping the speedup is more valuable than
the small load reduction.
@VaguelySerious

Copy link
Copy Markdown
MemberAuthor

Update

Three substantive fixes layered on, then one revert. Final state of the PR:

What was fixed

  1. Per-task diagnostic isolation (a115774) — setupRunTracking tracked failed-run diagnostics via two module-level mutables (trackedRuns, currentTestName) that every beforeEach reset. Under describe.concurrent, every test clobbered every other's state, so the failing test's diagnostic typically showed an unrelated sibling's workflow. Switched to Map<taskId, PerTaskState> keyed off getCurrentTest().id. Also unconditionally log [trackRun] <test> → <runId> so even when diagnostics fetching stalls past a 60s timeout, the per-test runId is still in stdout for backend correlation.
  2. writeExclusive content atomicity (2fce0bb) — fs.writeFile(path, data, { flag: 'wx' }) is atomic for the file creation (O_CREAT|O_EXCL) but not for the data write that follows. A concurrent reader could observe a half-written file (e.g. just {), which the hook-token-claim path was silently swallowing as "no claim exists" — a real correctness bug. Run-creation hit the same race and produced size=0 run JSON files surfaced in CI logs. Switched to write-then-link(2): write data to a temp file first, then atomically link it into place. POSIX link is atomic and fails with EEXIST if the target exists, preserving the exclusive-claim semantics. Readers now either see ENOENT or the fully-populated inode.
  3. readJSON SyntaxError annotation (same commit) — annotates partial-read SyntaxError with file path + size + content snippet, preserving the SyntaxError type so existing handlers like readHookTokenClaim's instanceof SyntaxError catch still work.

What was tried and reverted

  • maxConcurrency: 3 (c7060b1 → reverted in 6e264eb) — runs 3/4 at concurrency=5 and run 5 at concurrency=3 all landed on exactly 15 e2e failures. The remaining flakes don't respond to lower test parallelism, so the cap traded speedup for nothing.

What's left (out of scope for this PR)

  • Sourcemap assertion failures on nuxt and nextjs-webpack: expect(stack).not.toContain('99_e2e.ts') fires because hasStepSourceMaps() reports false but the stack does contain the .ts filename. Pre-existing test/runtime mismatch — independent of concurrency, just hits more often now that more workflows run per CI invocation. Needs separate investigation into the source-map detection logic.
  • External-network flakes on abort tests (abortFetchInFlightWorkflow, abortFetchUncaughtWorkflow, etc.): httpbin/postman-echo delay endpoints intermittently return early from GH Actions runners. [tests] Fix abort-fetch e2e flake #2081 added a fallback URL pair; under heavier concurrent load both upstreams flake. The proper fix is a per-workbench /api/delay/:n route.
  • Fibonacci tree spawn timeouts on Vercel Prod: fibonacciWorkflow with fib(6) spawns a tree of child runs; under concurrent load this saturates the workflow scheduler past the test's 180s budget. Could be addressed with a smaller fib(N), a longer budget, or marking that single test .sequential.

Verdict

The actual concurrency-introduced isolation/correctness bugs are fixed. Concurrency speedup holds (~3× job wall-clock; ~4–5× pure vitest run). The remaining failures are pre-existing issues that the concurrency change has made more visible but doesn't itself cause. Happy to keep this draft until those classes are tackled, or to merge with a green CI rerun depending on appetite.

Sign up for freeto 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

@VaguelySerious
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Auto-enable theater mode on YouTube\n(function() {\n function tryTheater() {\n var btn = document.querySelector('button[aria-label=\"Theater mode\"], ytd-player #player button[title=\"Theater mode\"]');\n if (btn && !btn.classList.contains('activated')) {\n btn.click();\n }\n }\n \n // Try immediately\n tryTheater();\n \n // Try after navigation (SPA)\n var lastUrl = location.href;\n setInterval(function() {\n if (location.href !== lastUrl) {\n lastUrl = location.href;\n setTimeout(tryTheater, 500);\n }\n }, 1000);\n \n // Also try on player load\n var observer = new MutationObserver(tryTheater);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "YouTube Theater Mode Default"); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

[tests] Re-enable concurrent e2e suite - #2083

Closed
VaguelySerious wants to merge 6 commits into
mainfrom
peter/enable-e2e-concurrency
Closed

[tests] Re-enable concurrent e2e suite#2083
VaguelySerious wants to merge 6 commits into
mainfrom
peter/enable-e2e-concurrency

Conversation

@VaguelySerious

Copy link
Copy Markdown
Member

Summary

  • Re-enables describe.concurrent in packages/core/e2e/e2e.test.ts, which was made sequential in 78048e0 ("no concurrency") with a TODO to re-enable.
  • Each workbench currently runs ~87 e2e tests serially against its preview deploy; this is the dominant ~15-minute cost per matrix entry.
  • This is a draft to see which (if any) tests are still flake-prone under concurrency after recent fixes (e.g. the abort-fetch class of flakes in [tests] Fix abort-fetch e2e flake #2081).

Test plan

  • All workbench matrix entries (E2E Vercel Prod) pass
  • Compare wall-clock vs. recent main runs
  • If failures cluster around specific tests, mark them .sequential rather than reverting the whole flip

Notes

If concurrency exposes real isolation bugs (shared identifiers, cross-test state), I'll fix those before unwinding the draft state. If it's only known-flaky externals, those should already have been addressed in #2081 / follow-ups.

🤖 Generated with Claude Code

Flips packages/core/e2e/e2e.test.ts back to describe.concurrent. The
suite was made sequential ("temporarily disabling concurrent tests to
avoid flakiness") and never re-enabled. Each workbench now runs ~87
tests serially against its preview deploy, dominating CI wall-clock.
Putting this up as a draft to see which (if any) tests are still
flake-prone under concurrency, after recent fixes to the abort-fetch
class of flakes.
@vercel

vercelBot commented May 22, 2026

Copy link
Copy Markdown
Contributor

@changeset-bot

changeset-botBot commented May 22, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: c7060b1

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

This PR includes changesets to release 18 packages
NameType
@workflow/world-localPatch
@workflow/cliPatch
@workflow/corePatch
@workflow/vitestPatch
@workflow/world-postgresPatch
workflowPatch
@workflow/world-testingPatch
@workflow/buildersPatch
@workflow/nextPatch
@workflow/nitroPatch
@workflow/web-sharedPatch
@workflow/webPatch
@workflow/astroPatch
@workflow/nestPatch
@workflow/rollupPatch
@workflow/sveltekitPatch
@workflow/vitePatch
@workflow/nuxtPatch

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-actionsBot commented May 22, 2026

Copy link
Copy Markdown
Contributor

📊 Benchmark Results

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

workflow with no steps

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
💻 Local🥇 Nitro0.032s (-26.0% 🟢)1.006s (~)0.974s101.00x
💻 LocalExpress0.032s (-26.9% 🟢)1.006s (~)0.974s101.02x
💻 LocalNext.js (Turbopack)0.034s1.005s0.972s101.06x
🐘 PostgresExpress0.050s (-13.1% 🟢)1.012s (~)0.961s101.58x
🐘 PostgresNitro0.051s (-46.3% 🟢)1.013s (-2.9%)0.962s101.60x
🐘 PostgresNext.js (Turbopack)0.056s1.011s0.955s101.77x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Next.js (Turbopack)0.306s (+21.6% 🔺)2.546s (+9.1% 🔺)2.240s101.00x
▲ VercelNitro0.426s (+4.0%)2.554s (+1.8%)2.128s101.39x
▲ VercelExpress⚠️missing----

🔍 Observability: Next.js (Turbopack) | Nitro

workflow with 1 step

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
💻 Local🥇 Nitro1.076s (-4.9%)2.007s (~)0.931s101.00x
💻 LocalExpress1.079s (-4.1%)2.007s (~)0.928s101.00x
🐘 PostgresNitro1.082s (-5.1% 🟢)2.008s (~)0.926s101.01x
🐘 PostgresExpress1.087s (-5.2% 🟢)2.009s (~)0.922s101.01x
💻 LocalNext.js (Turbopack)1.089s2.005s0.916s101.01x
🐘 PostgresNext.js (Turbopack)1.117s2.008s0.891s101.04x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Nitro1.806s (-53.6% 🟢)3.829s (-35.2% 🟢)2.023s101.00x
▲ VercelNext.js (Turbopack)1.906s (-6.3% 🟢)3.724s (-2.8%)1.818s101.06x
▲ VercelExpress⚠️missing----

🔍 Observability: Nitro | Next.js (Turbopack)

workflow with 10 sequential steps

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Nitro10.387s (-4.4%)11.018s (~)0.630s31.00x
💻 LocalExpress10.434s (-4.5%)11.023s (~)0.590s31.00x
💻 LocalNitro10.434s (-4.7%)11.024s (~)0.590s31.00x
🐘 PostgresExpress10.435s (-4.8%)11.014s (~)0.579s31.00x
💻 LocalNext.js (Turbopack)10.493s11.021s0.528s31.01x
🐘 PostgresNext.js (Turbopack)10.702s11.020s0.318s31.03x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Nitro13.961s (-41.2% 🟢)16.231s (-35.4% 🟢)2.269s21.00x
▲ VercelNext.js (Turbopack)14.589s (-15.8% 🟢)16.525s (-14.8% 🟢)1.936s21.04x
▲ VercelExpress⚠️missing----

🔍 Observability: Nitro | Next.js (Turbopack)

workflow with 25 sequential steps

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Nitro13.428s (-8.0% 🟢)14.016s (-6.7% 🟢)0.588s51.00x
🐘 PostgresExpress13.500s (-7.4% 🟢)14.019s (-6.7% 🟢)0.518s51.01x
💻 LocalExpress13.527s (-9.6% 🟢)14.027s (-6.7% 🟢)0.500s51.01x
💻 LocalNitro13.592s (-9.8% 🟢)14.027s (-12.5% 🟢)0.435s51.01x
💻 LocalNext.js (Turbopack)13.761s14.025s0.264s51.02x
🐘 PostgresNext.js (Turbopack)14.104s15.019s0.915s41.05x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Nitro21.896s (-66.0% 🟢)23.781s (-64.3% 🟢)1.885s31.00x
▲ VercelNext.js (Turbopack)22.228s (-57.7% 🟢)24.764s (-54.7% 🟢)2.536s31.02x
▲ VercelExpress⚠️missing----

🔍 Observability: Nitro | Next.js (Turbopack)

workflow with 50 sequential steps

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Nitro11.854s (-15.1% 🟢)12.015s (-16.0% 🟢)0.161s81.00x
💻 LocalNitro11.923s (-29.0% 🟢)12.021s (-29.4% 🟢)0.098s81.01x
🐘 PostgresExpress11.984s (-14.4% 🟢)12.017s (-17.7% 🟢)0.033s81.01x
💻 LocalExpress12.021s (-27.6% 🟢)12.399s (-27.2% 🟢)0.379s81.01x
💻 LocalNext.js (Turbopack)12.282s13.022s0.740s71.04x
🐘 PostgresNext.js (Turbopack)13.164s14.015s0.851s71.11x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Nitro31.366s (-92.6% 🟢)33.410s (-92.1% 🟢)2.044s31.00x
▲ VercelNext.js (Turbopack)32.382s (-91.8% 🟢)34.323s (-91.3% 🟢)1.940s31.03x
▲ VercelExpress⚠️missing----

🔍 Observability: Nitro | Next.js (Turbopack)

Promise.all with 10 concurrent steps

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Nitro1.158s (-9.2% 🟢)2.009s (~)0.851s151.00x
🐘 PostgresExpress1.160s (-8.0% 🟢)2.008s (~)0.848s151.00x
💻 LocalNitro1.172s (-28.2% 🟢)2.006s (-3.3%)0.835s151.01x
💻 LocalExpress1.179s (-20.8% 🟢)2.007s (~)0.828s151.02x
💻 LocalNext.js (Turbopack)1.212s2.005s0.793s151.05x
🐘 PostgresNext.js (Turbopack)1.218s2.008s0.790s151.05x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Nitro2.877s (+2.1%)4.515s (+4.5%)1.638s81.00x
▲ VercelNext.js (Turbopack)2.967s (-12.7% 🟢)4.728s (-4.2%)1.761s71.03x
▲ VercelExpress⚠️missing----

🔍 Observability: Nitro | Next.js (Turbopack)

Promise.all with 25 concurrent steps

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Express1.212s (-48.6% 🟢)2.007s (-33.3% 🟢)0.795s151.00x
🐘 PostgresNitro1.224s (-47.9% 🟢)2.009s (-33.2% 🟢)0.785s151.01x
🐘 PostgresNext.js (Turbopack)1.336s2.007s0.671s151.10x
💻 LocalNext.js (Turbopack)1.524s2.004s0.480s151.26x
💻 LocalNitro1.744s (-44.5% 🟢)2.007s (-48.3% 🟢)0.264s151.44x
💻 LocalExpress1.835s (-37.8% 🟢)2.140s (-38.0% 🟢)0.304s151.51x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Next.js (Turbopack)4.146s (-41.6% 🟢)6.056s (-32.0% 🟢)1.910s51.00x
▲ VercelNitro5.247s (+29.5% 🔺)7.231s (+22.1% 🔺)1.984s51.27x
▲ VercelExpress⚠️missing----

🔍 Observability: Next.js (Turbopack) | Nitro

Promise.all with 50 concurrent steps

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Express1.339s (-61.6% 🟢)2.007s (-49.9% 🟢)0.669s151.00x
🐘 PostgresNitro1.358s (-61.0% 🟢)2.007s (-49.9% 🟢)0.649s151.01x
🐘 PostgresNext.js (Turbopack)1.617s2.007s0.390s151.21x
💻 LocalNext.js (Turbopack)3.177s3.453s0.276s92.37x
💻 LocalExpress5.435s (-34.8% 🟢)6.214s (-31.2% 🟢)0.779s54.06x
💻 LocalNitro5.468s (-34.5% 🟢)6.012s (-33.3% 🟢)0.544s54.08x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Nitro6.778s (+92.3% 🔺)8.495s (+53.5% 🔺)1.716s41.00x
▲ VercelNext.js (Turbopack)8.699s (-2.4%)11.031s (+0.6%)2.332s31.28x
▲ VercelExpress⚠️missing----

🔍 Observability: Nitro | Next.js (Turbopack)

Promise.race with 10 concurrent steps

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Express1.142s (-9.1% 🟢)2.009s (~)0.866s151.00x
🐘 PostgresNitro1.152s (-8.3% 🟢)2.008s (~)0.856s151.01x
🐘 PostgresNext.js (Turbopack)1.209s2.008s0.800s151.06x
💻 LocalNext.js (Turbopack)1.209s2.004s0.795s151.06x
💻 LocalNitro1.417s (-24.1% 🟢)2.006s (-14.3% 🟢)0.590s151.24x
💻 LocalExpress1.427s (-24.7% 🟢)2.006s (-15.1% 🟢)0.580s151.25x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Next.js (Turbopack)2.873s (-2.0%)4.428s (-4.6%)1.555s71.00x
▲ VercelNitro2.925s (+18.9% 🔺)5.006s (+20.1% 🔺)2.081s61.02x
▲ VercelExpress⚠️missing----

🔍 Observability: Next.js (Turbopack) | Nitro

Promise.race with 25 concurrent steps

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Nitro1.214s (-48.1% 🟢)2.008s (-33.3% 🟢)0.794s151.00x
🐘 PostgresExpress1.226s (-47.6% 🟢)2.009s (-33.3% 🟢)0.783s151.01x
🐘 PostgresNext.js (Turbopack)1.343s2.009s0.666s151.11x
💻 LocalNext.js (Turbopack)1.579s2.005s0.426s151.30x
💻 LocalNitro2.034s (-33.6% 🟢)2.510s (-35.4% 🟢)0.475s121.68x
💻 LocalExpress2.057s (-34.3% 🟢)2.508s (-33.3% 🟢)0.451s121.69x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Nitro3.368s (+4.2%)5.319s (+4.8%)1.951s61.00x
▲ VercelNext.js (Turbopack)4.111s (+30.8% 🔺)5.838s (+29.1% 🔺)1.726s61.22x
▲ VercelExpress⚠️missing----

🔍 Observability: Nitro | Next.js (Turbopack)

Promise.race with 50 concurrent steps

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Nitro1.316s (-62.2% 🟢)2.009s (-49.9% 🟢)0.693s151.00x
🐘 PostgresExpress1.334s (-61.9% 🟢)2.007s (-50.0% 🟢)0.673s151.01x
🐘 PostgresNext.js (Turbopack)1.644s2.008s0.364s151.25x
💻 LocalNext.js (Turbopack)3.454s4.012s0.558s82.62x
💻 LocalExpress5.760s (-34.6% 🟢)6.216s (-33.0% 🟢)0.456s54.38x
💻 LocalNitro5.993s (-34.5% 🟢)6.513s (-35.0% 🟢)0.521s64.55x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Next.js (Turbopack)5.982s (-11.5% 🟢)7.562s (-11.5% 🟢)1.580s41.00x
▲ VercelNitro6.436s (+26.4% 🔺)8.067s (+18.3% 🔺)1.632s41.08x
▲ VercelExpress⚠️missing----

🔍 Observability: Next.js (Turbopack) | Nitro

workflow with 10 sequential data payload steps (10KB)

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Nitro0.428s (-47.8% 🟢)1.006s (~)0.578s601.00x
🐘 PostgresExpress0.467s (-44.3% 🟢)1.006s (-1.7%)0.539s601.09x
💻 LocalNitro0.493s (-49.7% 🟢)1.022s (-6.6% 🟢)0.529s591.15x
💻 LocalExpress0.517s (-47.4% 🟢)1.022s (-5.0% 🟢)0.504s591.21x
💻 LocalNext.js (Turbopack)0.615s1.021s0.406s591.44x
🐘 PostgresNext.js (Turbopack)0.675s1.006s0.332s601.58x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Nitro5.901s (-73.2% 🟢)7.712s (-67.9% 🟢)1.811s81.00x
▲ VercelNext.js (Turbopack)6.006s (-58.6% 🟢)7.961s (-50.5% 🟢)1.956s81.02x
▲ VercelExpress⚠️missing----

🔍 Observability: Nitro | Next.js (Turbopack)

workflow with 25 sequential data payload steps (10KB)

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Nitro0.995s (-48.4% 🟢)1.350s (-35.7% 🟢)0.355s671.00x
🐘 PostgresExpress1.188s (-39.9% 🟢)1.922s (-14.9% 🟢)0.734s471.19x
💻 LocalNitro1.220s (-59.8% 🟢)2.006s (-46.6% 🟢)0.786s451.23x
💻 LocalExpress1.260s (-58.2% 🟢)2.007s (-44.0% 🟢)0.746s451.27x
💻 LocalNext.js (Turbopack)1.421s2.004s0.583s451.43x
🐘 PostgresNext.js (Turbopack)1.660s2.008s0.347s451.67x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Nitro16.140s (-59.1% 🟢)18.101s (-56.2% 🟢)1.961s61.00x
▲ VercelNext.js (Turbopack)16.668s (-66.5% 🟢)19.003s (-63.3% 🟢)2.335s51.03x
▲ VercelExpress⚠️missing----

🔍 Observability: Nitro | Next.js (Turbopack)

workflow with 50 sequential data payload steps (10KB)

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Nitro1.994s (-51.4% 🟢)2.335s (-49.3% 🟢)0.341s521.00x
🐘 PostgresExpress2.106s (-47.2% 🟢)2.697s (-38.3% 🟢)0.591s451.06x
💻 LocalNitro2.786s (-70.0% 🟢)3.058s (-69.5% 🟢)0.272s401.40x
💻 LocalExpress2.801s (-69.6% 🟢)3.033s (-69.7% 🟢)0.231s401.41x
🐘 PostgresNext.js (Turbopack)3.212s4.009s0.797s301.61x
💻 LocalNext.js (Turbopack)3.466s4.074s0.608s301.74x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Nitro27.620s (-71.5% 🟢)29.816s (-69.7% 🟢)2.196s51.00x
▲ VercelNext.js (Turbopack)32.787s (-69.4% 🟢)35.186s (-67.7% 🟢)2.399s41.19x
▲ VercelExpress⚠️missing----

🔍 Observability: Nitro | Next.js (Turbopack)

workflow with 10 concurrent data payload steps (10KB)

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Nitro0.190s (-33.1% 🟢)1.006s (~)0.816s601.00x
🐘 PostgresExpress0.198s (-30.0% 🟢)1.023s (+1.5%)0.825s591.04x
🐘 PostgresNext.js (Turbopack)0.230s1.006s0.776s601.21x
💻 LocalNext.js (Turbopack)0.337s1.003s0.666s601.78x
💻 LocalExpress0.381s (-32.0% 🟢)1.004s (~)0.623s602.01x
💻 LocalNitro0.399s (-34.0% 🟢)1.004s (-1.7%)0.605s602.11x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Nitro2.453s (+47.7% 🔺)4.362s (+30.2% 🔺)1.909s141.00x
▲ VercelNext.js (Turbopack)2.813s (+39.1% 🔺)4.846s (+27.8% 🔺)2.034s131.15x
▲ VercelExpress⚠️missing----

🔍 Observability: Nitro | Next.js (Turbopack)

workflow with 25 concurrent data payload steps (10KB)

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Nitro0.284s (-42.8% 🟢)1.006s (~)0.722s901.00x
🐘 PostgresExpress0.308s (-39.6% 🟢)1.006s (~)0.698s901.08x
🐘 PostgresNext.js (Turbopack)0.435s1.006s0.571s901.53x
💻 LocalNext.js (Turbopack)1.433s2.028s0.595s455.05x
💻 LocalExpress2.159s (-14.1% 🟢)2.715s (-9.8% 🟢)0.556s347.60x
💻 LocalNitro2.171s (-14.5% 🟢)2.884s (-4.1%)0.713s327.65x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Nitro5.296s (+64.2% 🔺)6.881s (+42.7% 🔺)1.585s141.00x
▲ VercelNext.js (Turbopack)6.720s (+90.1% 🔺)8.608s (+65.8% 🔺)1.888s111.27x
▲ VercelExpress⚠️missing----

🔍 Observability: Nitro | Next.js (Turbopack)

workflow with 50 concurrent data payload steps (10KB)

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Nitro0.583s (-26.2% 🟢)1.006s (~)0.422s1201.00x
🐘 PostgresExpress0.652s (-20.4% 🟢)1.006s (-1.1%)0.355s1201.12x
🐘 PostgresNext.js (Turbopack)0.890s1.078s0.188s1121.53x
💻 LocalNext.js (Turbopack)6.974s7.706s0.731s1611.96x
💻 LocalNitro10.196s (-8.9% 🟢)10.777s (-7.6% 🟢)0.581s1217.48x
💻 LocalExpress10.526s (-5.9% 🟢)11.031s (-7.6% 🟢)0.505s1118.04x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Next.js (Turbopack)15.606s (+51.1% 🔺)17.743s (+44.4% 🔺)2.137s71.00x
▲ VercelNitro16.197s (+109.7% 🔺)18.211s (+93.7% 🔺)2.014s71.04x
▲ VercelExpress⚠️missing----

🔍 Observability: Next.js (Turbopack) | Nitro

Stream Benchmarks(includes TTFB metrics)
workflow with stream

💻 Local Development

WorldFrameworkWorkflow TimeTTFBSlurpWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Nitro1.128s (+450.3% 🔺)2.001s (+100.2% 🔺)0.001s (-33.3% 🟢)2.010s (+98.8% 🔺)0.882s101.00x
💻 LocalNext.js (Turbopack)1.129s2.002s0.006s2.011s0.882s101.00x
💻 LocalNitro1.133s (+430.0% 🔺)2.005s (+99.6% 🔺)0.013s (+4.0%)2.020s (+98.3% 🔺)0.888s101.00x
💻 LocalExpress1.138s (+471.7% 🔺)2.006s (+99.7% 🔺)0.013s (+5.8% 🔺)2.021s (+98.5% 🔺)0.883s101.01x
🐘 PostgresExpress1.145s (+458.3% 🔺)2.001s (+100.4% 🔺)0.001s (-18.8% 🟢)2.010s (+98.7% 🔺)0.865s101.01x
🐘 PostgresNext.js (Turbopack)1.201s2.001s0.002s2.010s0.809s101.06x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeTTFBSlurpWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Next.js (Turbopack)2.444s (-64.3% 🟢)3.510s (-59.4% 🟢)2.280s (+260.8% 🔺)6.380s (-34.8% 🟢)3.936s101.00x
▲ VercelNitro2.490s (-35.0% 🟢)3.601s (-31.8% 🟢)13.675s (+1742.7% 🔺)17.941s (+176.7% 🔺)15.451s101.02x
▲ VercelExpress⚠️missing-----

🔍 Observability: Next.js (Turbopack) | Nitro

stream pipeline with 5 transform steps (1MB)

💻 Local Development

WorldFrameworkWorkflow TimeTTFBSlurpWall TimeOverheadSamplesvs Fastest
💻 Local🥇 Next.js (Turbopack)1.475s2.007s0.006s2.015s0.540s301.00x
🐘 PostgresNitro1.494s (+139.3% 🔺)2.002s (+98.9% 🔺)0.004s (+6.4% 🔺)2.027s (+98.3% 🔺)0.534s301.01x
💻 LocalNitro1.541s (+83.7% 🔺)2.013s (+98.9% 🔺)0.010s (+4.9%)2.025s (+81.5% 🔺)0.484s301.04x
🐘 PostgresExpress1.543s (+144.9% 🔺)2.004s (+99.1% 🔺)0.004s (+3.6%)2.028s (+98.2% 🔺)0.484s301.05x
💻 LocalExpress1.549s (+104.6% 🔺)2.013s (+95.7% 🔺)0.010s (+9.4% 🔺)2.026s (+94.8% 🔺)0.476s301.05x
🐘 PostgresNext.js (Turbopack)1.678s2.010s0.004s2.025s0.347s301.14x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeTTFBSlurpWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Next.js (Turbopack)5.989s (-64.6% 🟢)7.269s (-60.1% 🟢)0.284s (+34.4% 🔺)8.226s (-56.6% 🟢)2.237s81.00x
▲ VercelNitro6.280s (-78.7% 🟢)7.470s (-75.8% 🟢)0.267s (+138.6% 🔺)8.303s (-73.9% 🟢)2.023s81.05x
▲ VercelExpress⚠️missing-----

🔍 Observability: Next.js (Turbopack) | Nitro

10 parallel streams (1MB each)

💻 Local Development

WorldFrameworkWorkflow TimeTTFBSlurpWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Nitro0.640s (-34.0% 🟢)1.012s (-18.8% 🟢)0.000s (+22.0% 🔺)1.030s (-18.1% 🟢)0.391s591.00x
🐘 PostgresExpress0.664s (-30.9% 🟢)1.011s (-20.9% 🟢)0.000s (-61.0% 🟢)1.032s (-21.0% 🟢)0.369s591.04x
🐘 PostgresNext.js (Turbopack)0.766s1.017s0.000s1.032s0.266s591.20x
💻 LocalNext.js (Turbopack)1.042s1.631s0.000s1.634s0.592s371.63x
💻 LocalExpress1.344s (+9.7% 🔺)2.016s (~)0.000s (-30.0% 🟢)2.018s (~)0.674s302.10x
💻 LocalNitro1.373s (+12.3% 🔺)2.016s (~)0.000s (+66.7% 🔺)2.018s (~)0.645s302.15x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeTTFBSlurpWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Nitro3.957s (+29.7% 🔺)5.319s (+21.1% 🔺)0.000s (+18.2% 🔺)5.894s (+22.6% 🔺)1.937s111.00x
▲ VercelNext.js (Turbopack)3.981s (-60.9% 🟢)5.285s (-54.1% 🟢)0.000s (+Infinity% 🔺)5.843s (-51.5% 🟢)1.862s111.01x
▲ VercelExpress⚠️missing-----

🔍 Observability: Nitro | Next.js (Turbopack)

fan-out fan-in 10 streams (1MB each)

💻 Local Development

WorldFrameworkWorkflow TimeTTFBSlurpWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Nitro1.278s (-28.7% 🟢)2.029s (-5.2% 🟢)0.000s (-100.0% 🟢)2.049s (-5.7% 🟢)0.771s301.00x
🐘 PostgresExpress1.384s (-21.9% 🟢)2.029s (-6.8% 🟢)0.000s (+Infinity% 🔺)2.044s (-7.1% 🟢)0.659s301.08x
🐘 PostgresNext.js (Turbopack)1.500s2.145s0.000s2.153s0.654s281.17x
💻 LocalNext.js (Turbopack)2.061s2.523s0.000s2.528s0.467s241.61x
💻 LocalExpress3.109s (-10.3% 🟢)3.781s (-6.3% 🟢)0.001s (-21.9% 🟢)3.784s (-6.3% 🟢)0.675s162.43x
💻 LocalNitro3.183s (-6.0% 🟢)3.903s (-3.2%)0.000s (-18.0% 🟢)3.906s (-3.2%)0.723s162.49x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeTTFBSlurpWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Nitro5.980s (+46.1% 🔺)7.651s (+42.4% 🔺)0.000s (-54.2% 🟢)8.181s (+41.2% 🔺)2.201s81.00x
▲ VercelNext.js (Turbopack)6.105s (+8.7% 🔺)7.426s (+6.4% 🔺)0.000s (~)7.952s (+5.5% 🔺)1.847s81.02x
▲ VercelExpress⚠️missing-----

🔍 Observability: Nitro | Next.js (Turbopack)

Summary

Fastest Framework by World

Winner determined by most benchmark wins

World🥇 Fastest FrameworkWins
💻 LocalNext.js (Turbopack)12/21
🐘 PostgresNitro17/21
▲ VercelNitro14/21
Fastest World by Framework

Winner determined by most benchmark wins

Framework🥇 Fastest WorldWins
Express🐘 Postgres17/21
Next.js (Turbopack)🐘 Postgres11/21
Nitro🐘 Postgres19/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)

📋 View full workflow run


Some benchmark jobs failed:

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

Check the workflow run for details.

@github-actions

github-actionsBot commented May 22, 2026

Copy link
Copy Markdown
Contributor

🧪 E2E Test Results

Some tests failed

Summary

PassedFailedSkippedTotal
❌ ▲ Vercel Production121932191441
❌ 💻 Local Development161052191834
❌ 📦 Local Production161412191834
❌ 🐘 Local Postgres150212001703
✅ 🪟 Windows13100131
❌ 📋 Other7401176917
Total68161110337860

❌ Failed Tests

▲ Vercel Production (3 failed)

nitro (1 failed):

  • outputStreamInsideStepWorkflow - getWritable() called inside step functions | wrun_01KS7ZJGW73X7CKYTEQBGY87E5 | 🔍 observability

nuxt (1 failed):

  • hookWorkflow is not resumable via public webhook endpoint | wrun_01KS7ZJ3YXC4E16B345GAMR59H | 🔍 observability

sveltekit (1 failed):

💻 Local Development (5 failed)

astro-stable (2 failed):

  • fibonacciWorkflow - recursive workflow composition via start() | wrun_01KS7ZKMSDH7F5ZJHKWBKZDJPQ
  • AbortController abortExternalSignalInFlightWorkflow: external abort fires mid-flight, propagates to nested steps

nextjs-webpack-stable-lazy-discovery-disabled (1 failed):

  • error handling error propagation step errors basic step error preserves message and stack trace

nuxt-stable (1 failed):

  • AbortController abortExternalSignalInFlightWorkflow: external abort fires mid-flight, propagates to nested steps

sveltekit-stable (1 failed):

  • AbortController abortExternalSignalInFlightWorkflow: external abort fires mid-flight, propagates to nested steps
📦 Local Production (1 failed)

nuxt-stable (1 failed):

  • error handling error propagation step errors basic step error preserves message and stack trace
🐘 Local Postgres (1 failed)

nuxt-stable (1 failed):

  • error handling error propagation step errors basic step error preserves message and stack trace
📋 Other (1 failed)

e2e-local-dev-tanstack-start- (1 failed):

  • AbortController abortExternalSignalInFlightWorkflow: external abort fires mid-flight, propagates to nested steps

Details by Category

❌ ▲ Vercel Production
AppPassedFailedSkipped
✅ astro105026
✅ example105026
✅ express105026
✅ fastify105026
✅ hono105026
✅ nextjs-turbopack12902
✅ nextjs-webpack12902
❌ nitro104126
❌ nuxt104126
❌ sveltekit12317
✅ vite105026
❌ 💻 Local Development
AppPassedFailedSkipped
❌ astro-stable104225
✅ express-stable106025
✅ fastify-stable106025
✅ hono-stable106025
✅ nextjs-turbopack-canary112019
✅ nextjs-turbopack-stable-lazy-discovery-disabled13100
✅ nextjs-turbopack-stable-lazy-discovery-enabled13100
✅ nextjs-webpack-canary112019
❌ nextjs-webpack-stable-lazy-discovery-disabled13010
✅ nextjs-webpack-stable-lazy-discovery-enabled13100
✅ nitro-stable106025
❌ nuxt-stable105125
❌ sveltekit-stable12416
✅ vite-stable106025
❌ 📦 Local Production
AppPassedFailedSkipped
✅ astro-stable106025
✅ express-stable106025
✅ fastify-stable106025
✅ hono-stable106025
✅ nextjs-turbopack-canary112019
✅ nextjs-turbopack-stable-lazy-discovery-disabled13100
✅ nextjs-turbopack-stable-lazy-discovery-enabled13100
✅ nextjs-webpack-canary112019
✅ nextjs-webpack-stable-lazy-discovery-disabled13100
✅ nextjs-webpack-stable-lazy-discovery-enabled13100
✅ nitro-stable106025
❌ nuxt-stable105125
✅ sveltekit-stable12506
✅ vite-stable106025
❌ 🐘 Local Postgres
AppPassedFailedSkipped
✅ astro-stable106025
✅ express-stable106025
✅ fastify-stable106025
✅ hono-stable106025
✅ nextjs-turbopack-stable-lazy-discovery-disabled13100
✅ nextjs-turbopack-stable-lazy-discovery-enabled13100
✅ nextjs-webpack-canary112019
✅ nextjs-webpack-stable-lazy-discovery-disabled13100
✅ nextjs-webpack-stable-lazy-discovery-enabled13100
✅ nitro-stable106025
❌ nuxt-stable105125
✅ sveltekit-stable12506
✅ vite-stable106025
✅ 🪟 Windows
AppPassedFailedSkipped
✅ nextjs-turbopack13100
❌ 📋 Other
AppPassedFailedSkipped
✅ e2e-local-dev-nest-stable106025
❌ e2e-local-dev-tanstack-start-105125
✅ e2e-local-postgres-nest-stable106025
✅ e2e-local-postgres-tanstack-start-106025
✅ e2e-local-prod-nest-stable106025
✅ e2e-local-prod-tanstack-start-106025
✅ e2e-vercel-prod-tanstack-start105026

📋 View full workflow run


Some E2E test jobs failed:

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

Check the workflow run for details.

@VaguelySerious

Copy link
Copy Markdown
MemberAuthor

CI verdict

Speedup is real. Per-workbench wall-clock dropped roughly 3×: from ~14–17 min on main to ~5–6 min here. Vitest's own run is even more dramatic — express went from ~15 min to 203s (~4–5× on the actual test phase).

But correctness regressed. 25 checks failed: every `E2E Vercel Prod Tests (*)` matrix entry except `nitro`, plus several `E2E Local Dev/Prod/Postgres` jobs and `E2E Windows Tests`.

Failure pattern

Every failure I sampled is the same shape: `Error: Test timed out in 60000ms`. The which tests fail varies per workbench — i.e. it's not a single broken test:

WorkbenchFailed test
expresserror handling > catchability > FatalError can be caught… (×2)
nuxtabortThrowIfAbortedWorkflow, step throw round-trips FatalError
astroabortDeterministicBranchFromStepWorkflow, abortListenerWorkflow, cross-file step error
honoFatalError fails immediately without retries
tanstack-startabortHookOrderingWorkflow (×2), abortReasonWorkflow
fastifyabortHookOrderingWorkflow [hook-first-hook-first]
sveltekitFatalError fails immediately without retries
viteabortDeterministicBranchWorkflow, abortVoidSleepTimeoutWorkflow

The cluster is real though: AbortController + error-handling tests, which all do multiple sequential workflow round-trips and historically run close to the 60s budget.

Diagnosis

Default vitest `maxConcurrency` is 5. With five workflow runs hammering one workbench in parallel, the heavier multi-round-trip tests appear to queue past the per-test 60s budget. Local-Postgres/Prod/Dev also fail, so it's not Vercel-specific — workflow-server (local) and the in-process world also slow under contention.

Proposed next steps (separate PRs)

Holding off on bundling point (5) (shortening `sleep()` durations) into this PR — the suite isn't healthy enough to add another variable.

Three angles, in order of cheapest:

  1. Bump default `testTimeout` from 60s to ~180s (in `vitest.config.ts`). Cheap, but masks the queueing rather than fixing it. May allow individual tests to silently regress to 3× their real cost.
  2. Lower `maxConcurrency` to 3 (config-level or `describe.concurrent(name, { concurrent: 3 }, ...)`). Keeps most of the speedup, eases contention. Best first attempt IMO.
  3. Mark the error-handling and AbortController describes `.sequential` and leave the rest concurrent. Finest-grained, preserves max parallelism elsewhere, but requires per-block annotations.

Leaving this PR as draft for now. Happy to iterate as a follow-up.

The e2e suite tracked failed-run diagnostics via two module-level
mutables (`trackedRuns` and `currentTestName`) that every beforeEach
reset. Under describe.concurrent that races: when a test times out,
the displayed diagnostic typically belongs to whichever sibling task
most recently called setupRunTracking(). That's why PR #2083's first
CI run showed an unrelated `errorStepCrossFile` run as the
"diagnostic" for the FatalError catchability test that actually
timed out — leaving us blind to the real failure cause.
Switch to a `Map<taskId, PerTaskState>` keyed off
`getCurrentTest().id`, with the entry dropped via onTestFinished to
keep the map bounded. Also unconditionally log
`[trackRun] <test> → <runId>` so we can still correlate runs from
stdout even when diagnostics fetching itself stalls past the test's
deadline.
…Error type in readJSON
The previous writeExclusive used `fs.writeFile(path, data, { flag: 'wx' })`,
which is atomic for the file *creation* (O_CREAT|O_EXCL) but NOT for the
subsequent data write. A concurrent reader could catch the file with
zero bytes (or just `{`) before the writer's content reached the page
cache.
This race had been silently papered over by `readHookTokenClaim`, which
swallows any SyntaxError as "no claim exists" — which is wrong: a partial
write means a claim *does* exist, just not yet flushed, and treating it as
absent lets duplicate hook tokens through. PR #2083's concurrent e2e run
also surfaced the same race on runs/<id>.json creation, producing
size=0 byte run files in CI logs.
Fix: write content to a temp file first, then `link(2)` it atomically
into place. POSIX link is atomic and fails with EEXIST if the target
already exists, preserving the exclusive-claim semantics. Readers now
either see ENOENT or the fully-populated inode — never a half-written
file.
Also preserve the SyntaxError type when annotating readJSON failures
with file path / size / content snippet, so callers like
readHookTokenClaim that intentionally swallow `error instanceof SyntaxError`
continue to work (the previous diagnostic threw a plain Error and broke
them).
Vitest's default `maxConcurrency` of 5 saturates the workbench
preview deploys and the in-process world runtime under
`describe.concurrent`, pushing load-heavy tests (fibonacci tree
spawn, multi-step abort sequencing, AbortSignal.any composition)
into their per-test deadlines on PR #2083's run 4 even though
the same tests pass cleanly when run sequentially.
Drop to 3 in the root vitest config (only `test:e2e` and `bench`
consume it — per-package configs are untouched). The Vercel-deploy
matrix went from ~14-17min sequential → ~5-6min at concurrency 5;
trading some of that headroom for stability is the right call now
that the isolation/correctness fixes have landed.
Dropping vitest's default concurrency cap from 5 to 3 (commit c7060b1)
didn't reduce the remaining e2e flake count — runs 3/4 at default
concurrency=5 and run 5 at concurrency=3 all landed on exactly 15
failures. The remaining failures are external-network and source-map
class issues (httpbin/postman-echo abort tests, nuxt/nextjs-webpack
sourcemap assertions, fibonacci tree spawn timeouts) that lower test
parallelism doesn't address. Keeping the speedup is more valuable than
the small load reduction.
@VaguelySerious

Copy link
Copy Markdown
MemberAuthor

Update

Three substantive fixes layered on, then one revert. Final state of the PR:

What was fixed

  1. Per-task diagnostic isolation (a115774) — setupRunTracking tracked failed-run diagnostics via two module-level mutables (trackedRuns, currentTestName) that every beforeEach reset. Under describe.concurrent, every test clobbered every other's state, so the failing test's diagnostic typically showed an unrelated sibling's workflow. Switched to Map<taskId, PerTaskState> keyed off getCurrentTest().id. Also unconditionally log [trackRun] <test> → <runId> so even when diagnostics fetching stalls past a 60s timeout, the per-test runId is still in stdout for backend correlation.
  2. writeExclusive content atomicity (2fce0bb) — fs.writeFile(path, data, { flag: 'wx' }) is atomic for the file creation (O_CREAT|O_EXCL) but not for the data write that follows. A concurrent reader could observe a half-written file (e.g. just {), which the hook-token-claim path was silently swallowing as "no claim exists" — a real correctness bug. Run-creation hit the same race and produced size=0 run JSON files surfaced in CI logs. Switched to write-then-link(2): write data to a temp file first, then atomically link it into place. POSIX link is atomic and fails with EEXIST if the target exists, preserving the exclusive-claim semantics. Readers now either see ENOENT or the fully-populated inode.
  3. readJSON SyntaxError annotation (same commit) — annotates partial-read SyntaxError with file path + size + content snippet, preserving the SyntaxError type so existing handlers like readHookTokenClaim's instanceof SyntaxError catch still work.

What was tried and reverted

  • maxConcurrency: 3 (c7060b1 → reverted in 6e264eb) — runs 3/4 at concurrency=5 and run 5 at concurrency=3 all landed on exactly 15 e2e failures. The remaining flakes don't respond to lower test parallelism, so the cap traded speedup for nothing.

What's left (out of scope for this PR)

  • Sourcemap assertion failures on nuxt and nextjs-webpack: expect(stack).not.toContain('99_e2e.ts') fires because hasStepSourceMaps() reports false but the stack does contain the .ts filename. Pre-existing test/runtime mismatch — independent of concurrency, just hits more often now that more workflows run per CI invocation. Needs separate investigation into the source-map detection logic.
  • External-network flakes on abort tests (abortFetchInFlightWorkflow, abortFetchUncaughtWorkflow, etc.): httpbin/postman-echo delay endpoints intermittently return early from GH Actions runners. [tests] Fix abort-fetch e2e flake #2081 added a fallback URL pair; under heavier concurrent load both upstreams flake. The proper fix is a per-workbench /api/delay/:n route.
  • Fibonacci tree spawn timeouts on Vercel Prod: fibonacciWorkflow with fib(6) spawns a tree of child runs; under concurrent load this saturates the workflow scheduler past the test's 180s budget. Could be addressed with a smaller fib(N), a longer budget, or marking that single test .sequential.

Verdict

The actual concurrency-introduced isolation/correctness bugs are fixed. Concurrency speedup holds (~3× job wall-clock; ~4–5× pure vitest run). The remaining failures are pre-existing issues that the concurrency change has made more visible but doesn't itself cause. Happy to keep this draft until those classes are tackled, or to merge with a green CI rerun depending on appetite.

Sign up for freeto 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

@VaguelySerious
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Remove or un-stick sticky/fixed headers that block content\n(function() {\n function unstick() {\n document.querySelectorAll('header, nav, [role=\"banner\"], .header, .navbar, .sticky, .fixed-top, [style*=\"position: fixed\"], [style*=\"position:sticky\"]').forEach(function(el) {\n if (el.style.position === 'fixed' || el.style.position === 'sticky' || \n getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') {\n el.style.position = 'static';\n el.style.top = 'auto';\n el.style.zIndex = 'auto';\n }\n });\n }\n \n unstick();\n \n var observer = new MutationObserver(unstick);\n observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] });\n})();", "Kill Sticky Headers"); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

[tests] Re-enable concurrent e2e suite - #2083

Closed
VaguelySerious wants to merge 6 commits into
mainfrom
peter/enable-e2e-concurrency
Closed

[tests] Re-enable concurrent e2e suite#2083
VaguelySerious wants to merge 6 commits into
mainfrom
peter/enable-e2e-concurrency

Conversation

@VaguelySerious

Copy link
Copy Markdown
Member

Summary

  • Re-enables describe.concurrent in packages/core/e2e/e2e.test.ts, which was made sequential in 78048e0 ("no concurrency") with a TODO to re-enable.
  • Each workbench currently runs ~87 e2e tests serially against its preview deploy; this is the dominant ~15-minute cost per matrix entry.
  • This is a draft to see which (if any) tests are still flake-prone under concurrency after recent fixes (e.g. the abort-fetch class of flakes in [tests] Fix abort-fetch e2e flake #2081).

Test plan

  • All workbench matrix entries (E2E Vercel Prod) pass
  • Compare wall-clock vs. recent main runs
  • If failures cluster around specific tests, mark them .sequential rather than reverting the whole flip

Notes

If concurrency exposes real isolation bugs (shared identifiers, cross-test state), I'll fix those before unwinding the draft state. If it's only known-flaky externals, those should already have been addressed in #2081 / follow-ups.

🤖 Generated with Claude Code

Flips packages/core/e2e/e2e.test.ts back to describe.concurrent. The
suite was made sequential ("temporarily disabling concurrent tests to
avoid flakiness") and never re-enabled. Each workbench now runs ~87
tests serially against its preview deploy, dominating CI wall-clock.
Putting this up as a draft to see which (if any) tests are still
flake-prone under concurrency, after recent fixes to the abort-fetch
class of flakes.
@vercel

vercelBot commented May 22, 2026

Copy link
Copy Markdown
Contributor

@changeset-bot

changeset-botBot commented May 22, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: c7060b1

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

This PR includes changesets to release 18 packages
NameType
@workflow/world-localPatch
@workflow/cliPatch
@workflow/corePatch
@workflow/vitestPatch
@workflow/world-postgresPatch
workflowPatch
@workflow/world-testingPatch
@workflow/buildersPatch
@workflow/nextPatch
@workflow/nitroPatch
@workflow/web-sharedPatch
@workflow/webPatch
@workflow/astroPatch
@workflow/nestPatch
@workflow/rollupPatch
@workflow/sveltekitPatch
@workflow/vitePatch
@workflow/nuxtPatch

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-actionsBot commented May 22, 2026

Copy link
Copy Markdown
Contributor

📊 Benchmark Results

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

workflow with no steps

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
💻 Local🥇 Nitro0.032s (-26.0% 🟢)1.006s (~)0.974s101.00x
💻 LocalExpress0.032s (-26.9% 🟢)1.006s (~)0.974s101.02x
💻 LocalNext.js (Turbopack)0.034s1.005s0.972s101.06x
🐘 PostgresExpress0.050s (-13.1% 🟢)1.012s (~)0.961s101.58x
🐘 PostgresNitro0.051s (-46.3% 🟢)1.013s (-2.9%)0.962s101.60x
🐘 PostgresNext.js (Turbopack)0.056s1.011s0.955s101.77x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Next.js (Turbopack)0.306s (+21.6% 🔺)2.546s (+9.1% 🔺)2.240s101.00x
▲ VercelNitro0.426s (+4.0%)2.554s (+1.8%)2.128s101.39x
▲ VercelExpress⚠️missing----

🔍 Observability: Next.js (Turbopack) | Nitro

workflow with 1 step

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
💻 Local🥇 Nitro1.076s (-4.9%)2.007s (~)0.931s101.00x
💻 LocalExpress1.079s (-4.1%)2.007s (~)0.928s101.00x
🐘 PostgresNitro1.082s (-5.1% 🟢)2.008s (~)0.926s101.01x
🐘 PostgresExpress1.087s (-5.2% 🟢)2.009s (~)0.922s101.01x
💻 LocalNext.js (Turbopack)1.089s2.005s0.916s101.01x
🐘 PostgresNext.js (Turbopack)1.117s2.008s0.891s101.04x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Nitro1.806s (-53.6% 🟢)3.829s (-35.2% 🟢)2.023s101.00x
▲ VercelNext.js (Turbopack)1.906s (-6.3% 🟢)3.724s (-2.8%)1.818s101.06x
▲ VercelExpress⚠️missing----

🔍 Observability: Nitro | Next.js (Turbopack)

workflow with 10 sequential steps

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Nitro10.387s (-4.4%)11.018s (~)0.630s31.00x
💻 LocalExpress10.434s (-4.5%)11.023s (~)0.590s31.00x
💻 LocalNitro10.434s (-4.7%)11.024s (~)0.590s31.00x
🐘 PostgresExpress10.435s (-4.8%)11.014s (~)0.579s31.00x
💻 LocalNext.js (Turbopack)10.493s11.021s0.528s31.01x
🐘 PostgresNext.js (Turbopack)10.702s11.020s0.318s31.03x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Nitro13.961s (-41.2% 🟢)16.231s (-35.4% 🟢)2.269s21.00x
▲ VercelNext.js (Turbopack)14.589s (-15.8% 🟢)16.525s (-14.8% 🟢)1.936s21.04x
▲ VercelExpress⚠️missing----

🔍 Observability: Nitro | Next.js (Turbopack)

workflow with 25 sequential steps

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Nitro13.428s (-8.0% 🟢)14.016s (-6.7% 🟢)0.588s51.00x
🐘 PostgresExpress13.500s (-7.4% 🟢)14.019s (-6.7% 🟢)0.518s51.01x
💻 LocalExpress13.527s (-9.6% 🟢)14.027s (-6.7% 🟢)0.500s51.01x
💻 LocalNitro13.592s (-9.8% 🟢)14.027s (-12.5% 🟢)0.435s51.01x
💻 LocalNext.js (Turbopack)13.761s14.025s0.264s51.02x
🐘 PostgresNext.js (Turbopack)14.104s15.019s0.915s41.05x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Nitro21.896s (-66.0% 🟢)23.781s (-64.3% 🟢)1.885s31.00x
▲ VercelNext.js (Turbopack)22.228s (-57.7% 🟢)24.764s (-54.7% 🟢)2.536s31.02x
▲ VercelExpress⚠️missing----

🔍 Observability: Nitro | Next.js (Turbopack)

workflow with 50 sequential steps

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Nitro11.854s (-15.1% 🟢)12.015s (-16.0% 🟢)0.161s81.00x
💻 LocalNitro11.923s (-29.0% 🟢)12.021s (-29.4% 🟢)0.098s81.01x
🐘 PostgresExpress11.984s (-14.4% 🟢)12.017s (-17.7% 🟢)0.033s81.01x
💻 LocalExpress12.021s (-27.6% 🟢)12.399s (-27.2% 🟢)0.379s81.01x
💻 LocalNext.js (Turbopack)12.282s13.022s0.740s71.04x
🐘 PostgresNext.js (Turbopack)13.164s14.015s0.851s71.11x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Nitro31.366s (-92.6% 🟢)33.410s (-92.1% 🟢)2.044s31.00x
▲ VercelNext.js (Turbopack)32.382s (-91.8% 🟢)34.323s (-91.3% 🟢)1.940s31.03x
▲ VercelExpress⚠️missing----

🔍 Observability: Nitro | Next.js (Turbopack)

Promise.all with 10 concurrent steps

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Nitro1.158s (-9.2% 🟢)2.009s (~)0.851s151.00x
🐘 PostgresExpress1.160s (-8.0% 🟢)2.008s (~)0.848s151.00x
💻 LocalNitro1.172s (-28.2% 🟢)2.006s (-3.3%)0.835s151.01x
💻 LocalExpress1.179s (-20.8% 🟢)2.007s (~)0.828s151.02x
💻 LocalNext.js (Turbopack)1.212s2.005s0.793s151.05x
🐘 PostgresNext.js (Turbopack)1.218s2.008s0.790s151.05x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Nitro2.877s (+2.1%)4.515s (+4.5%)1.638s81.00x
▲ VercelNext.js (Turbopack)2.967s (-12.7% 🟢)4.728s (-4.2%)1.761s71.03x
▲ VercelExpress⚠️missing----

🔍 Observability: Nitro | Next.js (Turbopack)

Promise.all with 25 concurrent steps

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Express1.212s (-48.6% 🟢)2.007s (-33.3% 🟢)0.795s151.00x
🐘 PostgresNitro1.224s (-47.9% 🟢)2.009s (-33.2% 🟢)0.785s151.01x
🐘 PostgresNext.js (Turbopack)1.336s2.007s0.671s151.10x
💻 LocalNext.js (Turbopack)1.524s2.004s0.480s151.26x
💻 LocalNitro1.744s (-44.5% 🟢)2.007s (-48.3% 🟢)0.264s151.44x
💻 LocalExpress1.835s (-37.8% 🟢)2.140s (-38.0% 🟢)0.304s151.51x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Next.js (Turbopack)4.146s (-41.6% 🟢)6.056s (-32.0% 🟢)1.910s51.00x
▲ VercelNitro5.247s (+29.5% 🔺)7.231s (+22.1% 🔺)1.984s51.27x
▲ VercelExpress⚠️missing----

🔍 Observability: Next.js (Turbopack) | Nitro

Promise.all with 50 concurrent steps

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Express1.339s (-61.6% 🟢)2.007s (-49.9% 🟢)0.669s151.00x
🐘 PostgresNitro1.358s (-61.0% 🟢)2.007s (-49.9% 🟢)0.649s151.01x
🐘 PostgresNext.js (Turbopack)1.617s2.007s0.390s151.21x
💻 LocalNext.js (Turbopack)3.177s3.453s0.276s92.37x
💻 LocalExpress5.435s (-34.8% 🟢)6.214s (-31.2% 🟢)0.779s54.06x
💻 LocalNitro5.468s (-34.5% 🟢)6.012s (-33.3% 🟢)0.544s54.08x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Nitro6.778s (+92.3% 🔺)8.495s (+53.5% 🔺)1.716s41.00x
▲ VercelNext.js (Turbopack)8.699s (-2.4%)11.031s (+0.6%)2.332s31.28x
▲ VercelExpress⚠️missing----

🔍 Observability: Nitro | Next.js (Turbopack)

Promise.race with 10 concurrent steps

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Express1.142s (-9.1% 🟢)2.009s (~)0.866s151.00x
🐘 PostgresNitro1.152s (-8.3% 🟢)2.008s (~)0.856s151.01x
🐘 PostgresNext.js (Turbopack)1.209s2.008s0.800s151.06x
💻 LocalNext.js (Turbopack)1.209s2.004s0.795s151.06x
💻 LocalNitro1.417s (-24.1% 🟢)2.006s (-14.3% 🟢)0.590s151.24x
💻 LocalExpress1.427s (-24.7% 🟢)2.006s (-15.1% 🟢)0.580s151.25x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Next.js (Turbopack)2.873s (-2.0%)4.428s (-4.6%)1.555s71.00x
▲ VercelNitro2.925s (+18.9% 🔺)5.006s (+20.1% 🔺)2.081s61.02x
▲ VercelExpress⚠️missing----

🔍 Observability: Next.js (Turbopack) | Nitro

Promise.race with 25 concurrent steps

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Nitro1.214s (-48.1% 🟢)2.008s (-33.3% 🟢)0.794s151.00x
🐘 PostgresExpress1.226s (-47.6% 🟢)2.009s (-33.3% 🟢)0.783s151.01x
🐘 PostgresNext.js (Turbopack)1.343s2.009s0.666s151.11x
💻 LocalNext.js (Turbopack)1.579s2.005s0.426s151.30x
💻 LocalNitro2.034s (-33.6% 🟢)2.510s (-35.4% 🟢)0.475s121.68x
💻 LocalExpress2.057s (-34.3% 🟢)2.508s (-33.3% 🟢)0.451s121.69x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Nitro3.368s (+4.2%)5.319s (+4.8%)1.951s61.00x
▲ VercelNext.js (Turbopack)4.111s (+30.8% 🔺)5.838s (+29.1% 🔺)1.726s61.22x
▲ VercelExpress⚠️missing----

🔍 Observability: Nitro | Next.js (Turbopack)

Promise.race with 50 concurrent steps

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Nitro1.316s (-62.2% 🟢)2.009s (-49.9% 🟢)0.693s151.00x
🐘 PostgresExpress1.334s (-61.9% 🟢)2.007s (-50.0% 🟢)0.673s151.01x
🐘 PostgresNext.js (Turbopack)1.644s2.008s0.364s151.25x
💻 LocalNext.js (Turbopack)3.454s4.012s0.558s82.62x
💻 LocalExpress5.760s (-34.6% 🟢)6.216s (-33.0% 🟢)0.456s54.38x
💻 LocalNitro5.993s (-34.5% 🟢)6.513s (-35.0% 🟢)0.521s64.55x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Next.js (Turbopack)5.982s (-11.5% 🟢)7.562s (-11.5% 🟢)1.580s41.00x
▲ VercelNitro6.436s (+26.4% 🔺)8.067s (+18.3% 🔺)1.632s41.08x
▲ VercelExpress⚠️missing----

🔍 Observability: Next.js (Turbopack) | Nitro

workflow with 10 sequential data payload steps (10KB)

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Nitro0.428s (-47.8% 🟢)1.006s (~)0.578s601.00x
🐘 PostgresExpress0.467s (-44.3% 🟢)1.006s (-1.7%)0.539s601.09x
💻 LocalNitro0.493s (-49.7% 🟢)1.022s (-6.6% 🟢)0.529s591.15x
💻 LocalExpress0.517s (-47.4% 🟢)1.022s (-5.0% 🟢)0.504s591.21x
💻 LocalNext.js (Turbopack)0.615s1.021s0.406s591.44x
🐘 PostgresNext.js (Turbopack)0.675s1.006s0.332s601.58x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Nitro5.901s (-73.2% 🟢)7.712s (-67.9% 🟢)1.811s81.00x
▲ VercelNext.js (Turbopack)6.006s (-58.6% 🟢)7.961s (-50.5% 🟢)1.956s81.02x
▲ VercelExpress⚠️missing----

🔍 Observability: Nitro | Next.js (Turbopack)

workflow with 25 sequential data payload steps (10KB)

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Nitro0.995s (-48.4% 🟢)1.350s (-35.7% 🟢)0.355s671.00x
🐘 PostgresExpress1.188s (-39.9% 🟢)1.922s (-14.9% 🟢)0.734s471.19x
💻 LocalNitro1.220s (-59.8% 🟢)2.006s (-46.6% 🟢)0.786s451.23x
💻 LocalExpress1.260s (-58.2% 🟢)2.007s (-44.0% 🟢)0.746s451.27x
💻 LocalNext.js (Turbopack)1.421s2.004s0.583s451.43x
🐘 PostgresNext.js (Turbopack)1.660s2.008s0.347s451.67x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Nitro16.140s (-59.1% 🟢)18.101s (-56.2% 🟢)1.961s61.00x
▲ VercelNext.js (Turbopack)16.668s (-66.5% 🟢)19.003s (-63.3% 🟢)2.335s51.03x
▲ VercelExpress⚠️missing----

🔍 Observability: Nitro | Next.js (Turbopack)

workflow with 50 sequential data payload steps (10KB)

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Nitro1.994s (-51.4% 🟢)2.335s (-49.3% 🟢)0.341s521.00x
🐘 PostgresExpress2.106s (-47.2% 🟢)2.697s (-38.3% 🟢)0.591s451.06x
💻 LocalNitro2.786s (-70.0% 🟢)3.058s (-69.5% 🟢)0.272s401.40x
💻 LocalExpress2.801s (-69.6% 🟢)3.033s (-69.7% 🟢)0.231s401.41x
🐘 PostgresNext.js (Turbopack)3.212s4.009s0.797s301.61x
💻 LocalNext.js (Turbopack)3.466s4.074s0.608s301.74x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Nitro27.620s (-71.5% 🟢)29.816s (-69.7% 🟢)2.196s51.00x
▲ VercelNext.js (Turbopack)32.787s (-69.4% 🟢)35.186s (-67.7% 🟢)2.399s41.19x
▲ VercelExpress⚠️missing----

🔍 Observability: Nitro | Next.js (Turbopack)

workflow with 10 concurrent data payload steps (10KB)

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Nitro0.190s (-33.1% 🟢)1.006s (~)0.816s601.00x
🐘 PostgresExpress0.198s (-30.0% 🟢)1.023s (+1.5%)0.825s591.04x
🐘 PostgresNext.js (Turbopack)0.230s1.006s0.776s601.21x
💻 LocalNext.js (Turbopack)0.337s1.003s0.666s601.78x
💻 LocalExpress0.381s (-32.0% 🟢)1.004s (~)0.623s602.01x
💻 LocalNitro0.399s (-34.0% 🟢)1.004s (-1.7%)0.605s602.11x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Nitro2.453s (+47.7% 🔺)4.362s (+30.2% 🔺)1.909s141.00x
▲ VercelNext.js (Turbopack)2.813s (+39.1% 🔺)4.846s (+27.8% 🔺)2.034s131.15x
▲ VercelExpress⚠️missing----

🔍 Observability: Nitro | Next.js (Turbopack)

workflow with 25 concurrent data payload steps (10KB)

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Nitro0.284s (-42.8% 🟢)1.006s (~)0.722s901.00x
🐘 PostgresExpress0.308s (-39.6% 🟢)1.006s (~)0.698s901.08x
🐘 PostgresNext.js (Turbopack)0.435s1.006s0.571s901.53x
💻 LocalNext.js (Turbopack)1.433s2.028s0.595s455.05x
💻 LocalExpress2.159s (-14.1% 🟢)2.715s (-9.8% 🟢)0.556s347.60x
💻 LocalNitro2.171s (-14.5% 🟢)2.884s (-4.1%)0.713s327.65x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Nitro5.296s (+64.2% 🔺)6.881s (+42.7% 🔺)1.585s141.00x
▲ VercelNext.js (Turbopack)6.720s (+90.1% 🔺)8.608s (+65.8% 🔺)1.888s111.27x
▲ VercelExpress⚠️missing----

🔍 Observability: Nitro | Next.js (Turbopack)

workflow with 50 concurrent data payload steps (10KB)

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Nitro0.583s (-26.2% 🟢)1.006s (~)0.422s1201.00x
🐘 PostgresExpress0.652s (-20.4% 🟢)1.006s (-1.1%)0.355s1201.12x
🐘 PostgresNext.js (Turbopack)0.890s1.078s0.188s1121.53x
💻 LocalNext.js (Turbopack)6.974s7.706s0.731s1611.96x
💻 LocalNitro10.196s (-8.9% 🟢)10.777s (-7.6% 🟢)0.581s1217.48x
💻 LocalExpress10.526s (-5.9% 🟢)11.031s (-7.6% 🟢)0.505s1118.04x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Next.js (Turbopack)15.606s (+51.1% 🔺)17.743s (+44.4% 🔺)2.137s71.00x
▲ VercelNitro16.197s (+109.7% 🔺)18.211s (+93.7% 🔺)2.014s71.04x
▲ VercelExpress⚠️missing----

🔍 Observability: Next.js (Turbopack) | Nitro

Stream Benchmarks(includes TTFB metrics)
workflow with stream

💻 Local Development

WorldFrameworkWorkflow TimeTTFBSlurpWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Nitro1.128s (+450.3% 🔺)2.001s (+100.2% 🔺)0.001s (-33.3% 🟢)2.010s (+98.8% 🔺)0.882s101.00x
💻 LocalNext.js (Turbopack)1.129s2.002s0.006s2.011s0.882s101.00x
💻 LocalNitro1.133s (+430.0% 🔺)2.005s (+99.6% 🔺)0.013s (+4.0%)2.020s (+98.3% 🔺)0.888s101.00x
💻 LocalExpress1.138s (+471.7% 🔺)2.006s (+99.7% 🔺)0.013s (+5.8% 🔺)2.021s (+98.5% 🔺)0.883s101.01x
🐘 PostgresExpress1.145s (+458.3% 🔺)2.001s (+100.4% 🔺)0.001s (-18.8% 🟢)2.010s (+98.7% 🔺)0.865s101.01x
🐘 PostgresNext.js (Turbopack)1.201s2.001s0.002s2.010s0.809s101.06x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeTTFBSlurpWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Next.js (Turbopack)2.444s (-64.3% 🟢)3.510s (-59.4% 🟢)2.280s (+260.8% 🔺)6.380s (-34.8% 🟢)3.936s101.00x
▲ VercelNitro2.490s (-35.0% 🟢)3.601s (-31.8% 🟢)13.675s (+1742.7% 🔺)17.941s (+176.7% 🔺)15.451s101.02x
▲ VercelExpress⚠️missing-----

🔍 Observability: Next.js (Turbopack) | Nitro

stream pipeline with 5 transform steps (1MB)

💻 Local Development

WorldFrameworkWorkflow TimeTTFBSlurpWall TimeOverheadSamplesvs Fastest
💻 Local🥇 Next.js (Turbopack)1.475s2.007s0.006s2.015s0.540s301.00x
🐘 PostgresNitro1.494s (+139.3% 🔺)2.002s (+98.9% 🔺)0.004s (+6.4% 🔺)2.027s (+98.3% 🔺)0.534s301.01x
💻 LocalNitro1.541s (+83.7% 🔺)2.013s (+98.9% 🔺)0.010s (+4.9%)2.025s (+81.5% 🔺)0.484s301.04x
🐘 PostgresExpress1.543s (+144.9% 🔺)2.004s (+99.1% 🔺)0.004s (+3.6%)2.028s (+98.2% 🔺)0.484s301.05x
💻 LocalExpress1.549s (+104.6% 🔺)2.013s (+95.7% 🔺)0.010s (+9.4% 🔺)2.026s (+94.8% 🔺)0.476s301.05x
🐘 PostgresNext.js (Turbopack)1.678s2.010s0.004s2.025s0.347s301.14x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeTTFBSlurpWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Next.js (Turbopack)5.989s (-64.6% 🟢)7.269s (-60.1% 🟢)0.284s (+34.4% 🔺)8.226s (-56.6% 🟢)2.237s81.00x
▲ VercelNitro6.280s (-78.7% 🟢)7.470s (-75.8% 🟢)0.267s (+138.6% 🔺)8.303s (-73.9% 🟢)2.023s81.05x
▲ VercelExpress⚠️missing-----

🔍 Observability: Next.js (Turbopack) | Nitro

10 parallel streams (1MB each)

💻 Local Development

WorldFrameworkWorkflow TimeTTFBSlurpWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Nitro0.640s (-34.0% 🟢)1.012s (-18.8% 🟢)0.000s (+22.0% 🔺)1.030s (-18.1% 🟢)0.391s591.00x
🐘 PostgresExpress0.664s (-30.9% 🟢)1.011s (-20.9% 🟢)0.000s (-61.0% 🟢)1.032s (-21.0% 🟢)0.369s591.04x
🐘 PostgresNext.js (Turbopack)0.766s1.017s0.000s1.032s0.266s591.20x
💻 LocalNext.js (Turbopack)1.042s1.631s0.000s1.634s0.592s371.63x
💻 LocalExpress1.344s (+9.7% 🔺)2.016s (~)0.000s (-30.0% 🟢)2.018s (~)0.674s302.10x
💻 LocalNitro1.373s (+12.3% 🔺)2.016s (~)0.000s (+66.7% 🔺)2.018s (~)0.645s302.15x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeTTFBSlurpWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Nitro3.957s (+29.7% 🔺)5.319s (+21.1% 🔺)0.000s (+18.2% 🔺)5.894s (+22.6% 🔺)1.937s111.00x
▲ VercelNext.js (Turbopack)3.981s (-60.9% 🟢)5.285s (-54.1% 🟢)0.000s (+Infinity% 🔺)5.843s (-51.5% 🟢)1.862s111.01x
▲ VercelExpress⚠️missing-----

🔍 Observability: Nitro | Next.js (Turbopack)

fan-out fan-in 10 streams (1MB each)

💻 Local Development

WorldFrameworkWorkflow TimeTTFBSlurpWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Nitro1.278s (-28.7% 🟢)2.029s (-5.2% 🟢)0.000s (-100.0% 🟢)2.049s (-5.7% 🟢)0.771s301.00x
🐘 PostgresExpress1.384s (-21.9% 🟢)2.029s (-6.8% 🟢)0.000s (+Infinity% 🔺)2.044s (-7.1% 🟢)0.659s301.08x
🐘 PostgresNext.js (Turbopack)1.500s2.145s0.000s2.153s0.654s281.17x
💻 LocalNext.js (Turbopack)2.061s2.523s0.000s2.528s0.467s241.61x
💻 LocalExpress3.109s (-10.3% 🟢)3.781s (-6.3% 🟢)0.001s (-21.9% 🟢)3.784s (-6.3% 🟢)0.675s162.43x
💻 LocalNitro3.183s (-6.0% 🟢)3.903s (-3.2%)0.000s (-18.0% 🟢)3.906s (-3.2%)0.723s162.49x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeTTFBSlurpWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Nitro5.980s (+46.1% 🔺)7.651s (+42.4% 🔺)0.000s (-54.2% 🟢)8.181s (+41.2% 🔺)2.201s81.00x
▲ VercelNext.js (Turbopack)6.105s (+8.7% 🔺)7.426s (+6.4% 🔺)0.000s (~)7.952s (+5.5% 🔺)1.847s81.02x
▲ VercelExpress⚠️missing-----

🔍 Observability: Nitro | Next.js (Turbopack)

Summary

Fastest Framework by World

Winner determined by most benchmark wins

World🥇 Fastest FrameworkWins
💻 LocalNext.js (Turbopack)12/21
🐘 PostgresNitro17/21
▲ VercelNitro14/21
Fastest World by Framework

Winner determined by most benchmark wins

Framework🥇 Fastest WorldWins
Express🐘 Postgres17/21
Next.js (Turbopack)🐘 Postgres11/21
Nitro🐘 Postgres19/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)

📋 View full workflow run


Some benchmark jobs failed:

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

Check the workflow run for details.

@github-actions

github-actionsBot commented May 22, 2026

Copy link
Copy Markdown
Contributor

🧪 E2E Test Results

Some tests failed

Summary

PassedFailedSkippedTotal
❌ ▲ Vercel Production121932191441
❌ 💻 Local Development161052191834
❌ 📦 Local Production161412191834
❌ 🐘 Local Postgres150212001703
✅ 🪟 Windows13100131
❌ 📋 Other7401176917
Total68161110337860

❌ Failed Tests

▲ Vercel Production (3 failed)

nitro (1 failed):

  • outputStreamInsideStepWorkflow - getWritable() called inside step functions | wrun_01KS7ZJGW73X7CKYTEQBGY87E5 | 🔍 observability

nuxt (1 failed):

  • hookWorkflow is not resumable via public webhook endpoint | wrun_01KS7ZJ3YXC4E16B345GAMR59H | 🔍 observability

sveltekit (1 failed):

💻 Local Development (5 failed)

astro-stable (2 failed):

  • fibonacciWorkflow - recursive workflow composition via start() | wrun_01KS7ZKMSDH7F5ZJHKWBKZDJPQ
  • AbortController abortExternalSignalInFlightWorkflow: external abort fires mid-flight, propagates to nested steps

nextjs-webpack-stable-lazy-discovery-disabled (1 failed):

  • error handling error propagation step errors basic step error preserves message and stack trace

nuxt-stable (1 failed):

  • AbortController abortExternalSignalInFlightWorkflow: external abort fires mid-flight, propagates to nested steps

sveltekit-stable (1 failed):

  • AbortController abortExternalSignalInFlightWorkflow: external abort fires mid-flight, propagates to nested steps
📦 Local Production (1 failed)

nuxt-stable (1 failed):

  • error handling error propagation step errors basic step error preserves message and stack trace
🐘 Local Postgres (1 failed)

nuxt-stable (1 failed):

  • error handling error propagation step errors basic step error preserves message and stack trace
📋 Other (1 failed)

e2e-local-dev-tanstack-start- (1 failed):

  • AbortController abortExternalSignalInFlightWorkflow: external abort fires mid-flight, propagates to nested steps

Details by Category

❌ ▲ Vercel Production
AppPassedFailedSkipped
✅ astro105026
✅ example105026
✅ express105026
✅ fastify105026
✅ hono105026
✅ nextjs-turbopack12902
✅ nextjs-webpack12902
❌ nitro104126
❌ nuxt104126
❌ sveltekit12317
✅ vite105026
❌ 💻 Local Development
AppPassedFailedSkipped
❌ astro-stable104225
✅ express-stable106025
✅ fastify-stable106025
✅ hono-stable106025
✅ nextjs-turbopack-canary112019
✅ nextjs-turbopack-stable-lazy-discovery-disabled13100
✅ nextjs-turbopack-stable-lazy-discovery-enabled13100
✅ nextjs-webpack-canary112019
❌ nextjs-webpack-stable-lazy-discovery-disabled13010
✅ nextjs-webpack-stable-lazy-discovery-enabled13100
✅ nitro-stable106025
❌ nuxt-stable105125
❌ sveltekit-stable12416
✅ vite-stable106025
❌ 📦 Local Production
AppPassedFailedSkipped
✅ astro-stable106025
✅ express-stable106025
✅ fastify-stable106025
✅ hono-stable106025
✅ nextjs-turbopack-canary112019
✅ nextjs-turbopack-stable-lazy-discovery-disabled13100
✅ nextjs-turbopack-stable-lazy-discovery-enabled13100
✅ nextjs-webpack-canary112019
✅ nextjs-webpack-stable-lazy-discovery-disabled13100
✅ nextjs-webpack-stable-lazy-discovery-enabled13100
✅ nitro-stable106025
❌ nuxt-stable105125
✅ sveltekit-stable12506
✅ vite-stable106025
❌ 🐘 Local Postgres
AppPassedFailedSkipped
✅ astro-stable106025
✅ express-stable106025
✅ fastify-stable106025
✅ hono-stable106025
✅ nextjs-turbopack-stable-lazy-discovery-disabled13100
✅ nextjs-turbopack-stable-lazy-discovery-enabled13100
✅ nextjs-webpack-canary112019
✅ nextjs-webpack-stable-lazy-discovery-disabled13100
✅ nextjs-webpack-stable-lazy-discovery-enabled13100
✅ nitro-stable106025
❌ nuxt-stable105125
✅ sveltekit-stable12506
✅ vite-stable106025
✅ 🪟 Windows
AppPassedFailedSkipped
✅ nextjs-turbopack13100
❌ 📋 Other
AppPassedFailedSkipped
✅ e2e-local-dev-nest-stable106025
❌ e2e-local-dev-tanstack-start-105125
✅ e2e-local-postgres-nest-stable106025
✅ e2e-local-postgres-tanstack-start-106025
✅ e2e-local-prod-nest-stable106025
✅ e2e-local-prod-tanstack-start-106025
✅ e2e-vercel-prod-tanstack-start105026

📋 View full workflow run


Some E2E test jobs failed:

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

Check the workflow run for details.

@VaguelySerious

Copy link
Copy Markdown
MemberAuthor

CI verdict

Speedup is real. Per-workbench wall-clock dropped roughly 3×: from ~14–17 min on main to ~5–6 min here. Vitest's own run is even more dramatic — express went from ~15 min to 203s (~4–5× on the actual test phase).

But correctness regressed. 25 checks failed: every `E2E Vercel Prod Tests (*)` matrix entry except `nitro`, plus several `E2E Local Dev/Prod/Postgres` jobs and `E2E Windows Tests`.

Failure pattern

Every failure I sampled is the same shape: `Error: Test timed out in 60000ms`. The which tests fail varies per workbench — i.e. it's not a single broken test:

WorkbenchFailed test
expresserror handling > catchability > FatalError can be caught… (×2)
nuxtabortThrowIfAbortedWorkflow, step throw round-trips FatalError
astroabortDeterministicBranchFromStepWorkflow, abortListenerWorkflow, cross-file step error
honoFatalError fails immediately without retries
tanstack-startabortHookOrderingWorkflow (×2), abortReasonWorkflow
fastifyabortHookOrderingWorkflow [hook-first-hook-first]
sveltekitFatalError fails immediately without retries
viteabortDeterministicBranchWorkflow, abortVoidSleepTimeoutWorkflow

The cluster is real though: AbortController + error-handling tests, which all do multiple sequential workflow round-trips and historically run close to the 60s budget.

Diagnosis

Default vitest `maxConcurrency` is 5. With five workflow runs hammering one workbench in parallel, the heavier multi-round-trip tests appear to queue past the per-test 60s budget. Local-Postgres/Prod/Dev also fail, so it's not Vercel-specific — workflow-server (local) and the in-process world also slow under contention.

Proposed next steps (separate PRs)

Holding off on bundling point (5) (shortening `sleep()` durations) into this PR — the suite isn't healthy enough to add another variable.

Three angles, in order of cheapest:

  1. Bump default `testTimeout` from 60s to ~180s (in `vitest.config.ts`). Cheap, but masks the queueing rather than fixing it. May allow individual tests to silently regress to 3× their real cost.
  2. Lower `maxConcurrency` to 3 (config-level or `describe.concurrent(name, { concurrent: 3 }, ...)`). Keeps most of the speedup, eases contention. Best first attempt IMO.
  3. Mark the error-handling and AbortController describes `.sequential` and leave the rest concurrent. Finest-grained, preserves max parallelism elsewhere, but requires per-block annotations.

Leaving this PR as draft for now. Happy to iterate as a follow-up.

The e2e suite tracked failed-run diagnostics via two module-level
mutables (`trackedRuns` and `currentTestName`) that every beforeEach
reset. Under describe.concurrent that races: when a test times out,
the displayed diagnostic typically belongs to whichever sibling task
most recently called setupRunTracking(). That's why PR #2083's first
CI run showed an unrelated `errorStepCrossFile` run as the
"diagnostic" for the FatalError catchability test that actually
timed out — leaving us blind to the real failure cause.
Switch to a `Map<taskId, PerTaskState>` keyed off
`getCurrentTest().id`, with the entry dropped via onTestFinished to
keep the map bounded. Also unconditionally log
`[trackRun] <test> → <runId>` so we can still correlate runs from
stdout even when diagnostics fetching itself stalls past the test's
deadline.
…Error type in readJSON
The previous writeExclusive used `fs.writeFile(path, data, { flag: 'wx' })`,
which is atomic for the file *creation* (O_CREAT|O_EXCL) but NOT for the
subsequent data write. A concurrent reader could catch the file with
zero bytes (or just `{`) before the writer's content reached the page
cache.
This race had been silently papered over by `readHookTokenClaim`, which
swallows any SyntaxError as "no claim exists" — which is wrong: a partial
write means a claim *does* exist, just not yet flushed, and treating it as
absent lets duplicate hook tokens through. PR #2083's concurrent e2e run
also surfaced the same race on runs/<id>.json creation, producing
size=0 byte run files in CI logs.
Fix: write content to a temp file first, then `link(2)` it atomically
into place. POSIX link is atomic and fails with EEXIST if the target
already exists, preserving the exclusive-claim semantics. Readers now
either see ENOENT or the fully-populated inode — never a half-written
file.
Also preserve the SyntaxError type when annotating readJSON failures
with file path / size / content snippet, so callers like
readHookTokenClaim that intentionally swallow `error instanceof SyntaxError`
continue to work (the previous diagnostic threw a plain Error and broke
them).
Vitest's default `maxConcurrency` of 5 saturates the workbench
preview deploys and the in-process world runtime under
`describe.concurrent`, pushing load-heavy tests (fibonacci tree
spawn, multi-step abort sequencing, AbortSignal.any composition)
into their per-test deadlines on PR #2083's run 4 even though
the same tests pass cleanly when run sequentially.
Drop to 3 in the root vitest config (only `test:e2e` and `bench`
consume it — per-package configs are untouched). The Vercel-deploy
matrix went from ~14-17min sequential → ~5-6min at concurrency 5;
trading some of that headroom for stability is the right call now
that the isolation/correctness fixes have landed.
Dropping vitest's default concurrency cap from 5 to 3 (commit c7060b1)
didn't reduce the remaining e2e flake count — runs 3/4 at default
concurrency=5 and run 5 at concurrency=3 all landed on exactly 15
failures. The remaining failures are external-network and source-map
class issues (httpbin/postman-echo abort tests, nuxt/nextjs-webpack
sourcemap assertions, fibonacci tree spawn timeouts) that lower test
parallelism doesn't address. Keeping the speedup is more valuable than
the small load reduction.
@VaguelySerious

Copy link
Copy Markdown
MemberAuthor

Update

Three substantive fixes layered on, then one revert. Final state of the PR:

What was fixed

  1. Per-task diagnostic isolation (a115774) — setupRunTracking tracked failed-run diagnostics via two module-level mutables (trackedRuns, currentTestName) that every beforeEach reset. Under describe.concurrent, every test clobbered every other's state, so the failing test's diagnostic typically showed an unrelated sibling's workflow. Switched to Map<taskId, PerTaskState> keyed off getCurrentTest().id. Also unconditionally log [trackRun] <test> → <runId> so even when diagnostics fetching stalls past a 60s timeout, the per-test runId is still in stdout for backend correlation.
  2. writeExclusive content atomicity (2fce0bb) — fs.writeFile(path, data, { flag: 'wx' }) is atomic for the file creation (O_CREAT|O_EXCL) but not for the data write that follows. A concurrent reader could observe a half-written file (e.g. just {), which the hook-token-claim path was silently swallowing as "no claim exists" — a real correctness bug. Run-creation hit the same race and produced size=0 run JSON files surfaced in CI logs. Switched to write-then-link(2): write data to a temp file first, then atomically link it into place. POSIX link is atomic and fails with EEXIST if the target exists, preserving the exclusive-claim semantics. Readers now either see ENOENT or the fully-populated inode.
  3. readJSON SyntaxError annotation (same commit) — annotates partial-read SyntaxError with file path + size + content snippet, preserving the SyntaxError type so existing handlers like readHookTokenClaim's instanceof SyntaxError catch still work.

What was tried and reverted

  • maxConcurrency: 3 (c7060b1 → reverted in 6e264eb) — runs 3/4 at concurrency=5 and run 5 at concurrency=3 all landed on exactly 15 e2e failures. The remaining flakes don't respond to lower test parallelism, so the cap traded speedup for nothing.

What's left (out of scope for this PR)

  • Sourcemap assertion failures on nuxt and nextjs-webpack: expect(stack).not.toContain('99_e2e.ts') fires because hasStepSourceMaps() reports false but the stack does contain the .ts filename. Pre-existing test/runtime mismatch — independent of concurrency, just hits more often now that more workflows run per CI invocation. Needs separate investigation into the source-map detection logic.
  • External-network flakes on abort tests (abortFetchInFlightWorkflow, abortFetchUncaughtWorkflow, etc.): httpbin/postman-echo delay endpoints intermittently return early from GH Actions runners. [tests] Fix abort-fetch e2e flake #2081 added a fallback URL pair; under heavier concurrent load both upstreams flake. The proper fix is a per-workbench /api/delay/:n route.
  • Fibonacci tree spawn timeouts on Vercel Prod: fibonacciWorkflow with fib(6) spawns a tree of child runs; under concurrent load this saturates the workflow scheduler past the test's 180s budget. Could be addressed with a smaller fib(N), a longer budget, or marking that single test .sequential.

Verdict

The actual concurrency-introduced isolation/correctness bugs are fixed. Concurrency speedup holds (~3× job wall-clock; ~4–5× pure vitest run). The remaining failures are pre-existing issues that the concurrency change has made more visible but doesn't itself cause. Happy to keep this draft until those classes are tackled, or to merge with a green CI rerun depending on appetite.

Sign up for freeto 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

@VaguelySerious
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Universal Dark Mode - works on any site\n(function() {\n var enabled = true;\n \n function applyDarkMode() {\n if (!enabled) return;\n \n // Create style element if it doesn't exist\n var style = document.getElementById('universal-dark-mode-style');\n if (!style) {\n style = document.createElement('style');\n style.id = 'universal-dark-mode-style';\n document.head.appendChild(style);\n }\n \n // Dark mode CSS - inverts colors but preserves images/video\n style.textContent = '\n /* Invert everything except media */\n html {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #1a1a2e !important;\n }\n \n /* Restore images, videos, iframes, canvas */\n img, video, iframe, canvas, svg, picture, [style*=\"background-image\"] {\n filter: invert(1) hue-rotate(180deg) !important;\n }\n \n /* Preserve specific elements that should not be inverted */\n .no-dark-mode, .no-dark-mode *,\n [data-theme=\"light\"], [data-theme=\"light\"],\n .ace_editor, .ace_editor *,\n .CodeMirror, .CodeMirror *,\n .monaco-editor, .monaco-editor *,\n .markdown-body pre, .markdown-body pre *,\n .highlight, .highlight *,\n pre code, pre code * {\n filter: none !important;\n }\n \n /* Fix common UI elements */\n .modal, .popup, .dropdown-menu, .tooltip, .popover {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #2d2d44 !important;\n border-color: #444 !important;\n }\n \n /* Scrollbars */\n ::-webkit-scrollbar { background: #1a1a2e !important; }\n ::-webkit-scrollbar-thumb { background: #444 !important; }\n ::-webkit-scrollbar-thumb:hover { background: #555 !important; }\n \n /* Selection */\n ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ';\n }\n \n function removeDarkMode() {\n var style = document.getElementById('universal-dark-mode-style');\n if (style) style.remove();\n }\n \n // Toggle with Alt+Shift+D\n document.addEventListener('keydown', function(e) {\n if (e.altKey && e.shiftKey && e.key === 'D') {\n e.preventDefault();\n enabled = !enabled;\n if (enabled) {\n applyDarkMode();\n console.log('[Universal Dark Mode] Enabled');\n } else {\n removeDarkMode();\n console.log('[Universal Dark Mode] Disabled');\n }\n }\n });\n \n // Apply on load\n applyDarkMode();\n \n // Re-apply on dynamic content\n var observer = new MutationObserver(function(mutations) {\n if (enabled && !document.getElementById('universal-dark-mode-style')) {\n applyDarkMode();\n }\n });\n observer.observe(document.head, { childList: true });\n \n console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle');\n})();", "Universal Dark Mode"); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })();
Skip to content

[tests] Re-enable concurrent e2e suite - #2083

Closed
VaguelySerious wants to merge 6 commits into
mainfrom
peter/enable-e2e-concurrency
Closed

[tests] Re-enable concurrent e2e suite#2083
VaguelySerious wants to merge 6 commits into
mainfrom
peter/enable-e2e-concurrency

Conversation

@VaguelySerious

Copy link
Copy Markdown
Member

Summary

  • Re-enables describe.concurrent in packages/core/e2e/e2e.test.ts, which was made sequential in 78048e0 ("no concurrency") with a TODO to re-enable.
  • Each workbench currently runs ~87 e2e tests serially against its preview deploy; this is the dominant ~15-minute cost per matrix entry.
  • This is a draft to see which (if any) tests are still flake-prone under concurrency after recent fixes (e.g. the abort-fetch class of flakes in [tests] Fix abort-fetch e2e flake #2081).

Test plan

  • All workbench matrix entries (E2E Vercel Prod) pass
  • Compare wall-clock vs. recent main runs
  • If failures cluster around specific tests, mark them .sequential rather than reverting the whole flip

Notes

If concurrency exposes real isolation bugs (shared identifiers, cross-test state), I'll fix those before unwinding the draft state. If it's only known-flaky externals, those should already have been addressed in #2081 / follow-ups.

🤖 Generated with Claude Code

Flips packages/core/e2e/e2e.test.ts back to describe.concurrent. The
suite was made sequential ("temporarily disabling concurrent tests to
avoid flakiness") and never re-enabled. Each workbench now runs ~87
tests serially against its preview deploy, dominating CI wall-clock.
Putting this up as a draft to see which (if any) tests are still
flake-prone under concurrency, after recent fixes to the abort-fetch
class of flakes.
@vercel

vercelBot commented May 22, 2026

Copy link
Copy Markdown
Contributor

@changeset-bot

changeset-botBot commented May 22, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: c7060b1

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

This PR includes changesets to release 18 packages
NameType
@workflow/world-localPatch
@workflow/cliPatch
@workflow/corePatch
@workflow/vitestPatch
@workflow/world-postgresPatch
workflowPatch
@workflow/world-testingPatch
@workflow/buildersPatch
@workflow/nextPatch
@workflow/nitroPatch
@workflow/web-sharedPatch
@workflow/webPatch
@workflow/astroPatch
@workflow/nestPatch
@workflow/rollupPatch
@workflow/sveltekitPatch
@workflow/vitePatch
@workflow/nuxtPatch

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-actionsBot commented May 22, 2026

Copy link
Copy Markdown
Contributor

📊 Benchmark Results

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

workflow with no steps

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
💻 Local🥇 Nitro0.032s (-26.0% 🟢)1.006s (~)0.974s101.00x
💻 LocalExpress0.032s (-26.9% 🟢)1.006s (~)0.974s101.02x
💻 LocalNext.js (Turbopack)0.034s1.005s0.972s101.06x
🐘 PostgresExpress0.050s (-13.1% 🟢)1.012s (~)0.961s101.58x
🐘 PostgresNitro0.051s (-46.3% 🟢)1.013s (-2.9%)0.962s101.60x
🐘 PostgresNext.js (Turbopack)0.056s1.011s0.955s101.77x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Next.js (Turbopack)0.306s (+21.6% 🔺)2.546s (+9.1% 🔺)2.240s101.00x
▲ VercelNitro0.426s (+4.0%)2.554s (+1.8%)2.128s101.39x
▲ VercelExpress⚠️missing----

🔍 Observability: Next.js (Turbopack) | Nitro

workflow with 1 step

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
💻 Local🥇 Nitro1.076s (-4.9%)2.007s (~)0.931s101.00x
💻 LocalExpress1.079s (-4.1%)2.007s (~)0.928s101.00x
🐘 PostgresNitro1.082s (-5.1% 🟢)2.008s (~)0.926s101.01x
🐘 PostgresExpress1.087s (-5.2% 🟢)2.009s (~)0.922s101.01x
💻 LocalNext.js (Turbopack)1.089s2.005s0.916s101.01x
🐘 PostgresNext.js (Turbopack)1.117s2.008s0.891s101.04x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Nitro1.806s (-53.6% 🟢)3.829s (-35.2% 🟢)2.023s101.00x
▲ VercelNext.js (Turbopack)1.906s (-6.3% 🟢)3.724s (-2.8%)1.818s101.06x
▲ VercelExpress⚠️missing----

🔍 Observability: Nitro | Next.js (Turbopack)

workflow with 10 sequential steps

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Nitro10.387s (-4.4%)11.018s (~)0.630s31.00x
💻 LocalExpress10.434s (-4.5%)11.023s (~)0.590s31.00x
💻 LocalNitro10.434s (-4.7%)11.024s (~)0.590s31.00x
🐘 PostgresExpress10.435s (-4.8%)11.014s (~)0.579s31.00x
💻 LocalNext.js (Turbopack)10.493s11.021s0.528s31.01x
🐘 PostgresNext.js (Turbopack)10.702s11.020s0.318s31.03x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Nitro13.961s (-41.2% 🟢)16.231s (-35.4% 🟢)2.269s21.00x
▲ VercelNext.js (Turbopack)14.589s (-15.8% 🟢)16.525s (-14.8% 🟢)1.936s21.04x
▲ VercelExpress⚠️missing----

🔍 Observability: Nitro | Next.js (Turbopack)

workflow with 25 sequential steps

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Nitro13.428s (-8.0% 🟢)14.016s (-6.7% 🟢)0.588s51.00x
🐘 PostgresExpress13.500s (-7.4% 🟢)14.019s (-6.7% 🟢)0.518s51.01x
💻 LocalExpress13.527s (-9.6% 🟢)14.027s (-6.7% 🟢)0.500s51.01x
💻 LocalNitro13.592s (-9.8% 🟢)14.027s (-12.5% 🟢)0.435s51.01x
💻 LocalNext.js (Turbopack)13.761s14.025s0.264s51.02x
🐘 PostgresNext.js (Turbopack)14.104s15.019s0.915s41.05x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Nitro21.896s (-66.0% 🟢)23.781s (-64.3% 🟢)1.885s31.00x
▲ VercelNext.js (Turbopack)22.228s (-57.7% 🟢)24.764s (-54.7% 🟢)2.536s31.02x
▲ VercelExpress⚠️missing----

🔍 Observability: Nitro | Next.js (Turbopack)

workflow with 50 sequential steps

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Nitro11.854s (-15.1% 🟢)12.015s (-16.0% 🟢)0.161s81.00x
💻 LocalNitro11.923s (-29.0% 🟢)12.021s (-29.4% 🟢)0.098s81.01x
🐘 PostgresExpress11.984s (-14.4% 🟢)12.017s (-17.7% 🟢)0.033s81.01x
💻 LocalExpress12.021s (-27.6% 🟢)12.399s (-27.2% 🟢)0.379s81.01x
💻 LocalNext.js (Turbopack)12.282s13.022s0.740s71.04x
🐘 PostgresNext.js (Turbopack)13.164s14.015s0.851s71.11x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Nitro31.366s (-92.6% 🟢)33.410s (-92.1% 🟢)2.044s31.00x
▲ VercelNext.js (Turbopack)32.382s (-91.8% 🟢)34.323s (-91.3% 🟢)1.940s31.03x
▲ VercelExpress⚠️missing----

🔍 Observability: Nitro | Next.js (Turbopack)

Promise.all with 10 concurrent steps

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Nitro1.158s (-9.2% 🟢)2.009s (~)0.851s151.00x
🐘 PostgresExpress1.160s (-8.0% 🟢)2.008s (~)0.848s151.00x
💻 LocalNitro1.172s (-28.2% 🟢)2.006s (-3.3%)0.835s151.01x
💻 LocalExpress1.179s (-20.8% 🟢)2.007s (~)0.828s151.02x
💻 LocalNext.js (Turbopack)1.212s2.005s0.793s151.05x
🐘 PostgresNext.js (Turbopack)1.218s2.008s0.790s151.05x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Nitro2.877s (+2.1%)4.515s (+4.5%)1.638s81.00x
▲ VercelNext.js (Turbopack)2.967s (-12.7% 🟢)4.728s (-4.2%)1.761s71.03x
▲ VercelExpress⚠️missing----

🔍 Observability: Nitro | Next.js (Turbopack)

Promise.all with 25 concurrent steps

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Express1.212s (-48.6% 🟢)2.007s (-33.3% 🟢)0.795s151.00x
🐘 PostgresNitro1.224s (-47.9% 🟢)2.009s (-33.2% 🟢)0.785s151.01x
🐘 PostgresNext.js (Turbopack)1.336s2.007s0.671s151.10x
💻 LocalNext.js (Turbopack)1.524s2.004s0.480s151.26x
💻 LocalNitro1.744s (-44.5% 🟢)2.007s (-48.3% 🟢)0.264s151.44x
💻 LocalExpress1.835s (-37.8% 🟢)2.140s (-38.0% 🟢)0.304s151.51x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Next.js (Turbopack)4.146s (-41.6% 🟢)6.056s (-32.0% 🟢)1.910s51.00x
▲ VercelNitro5.247s (+29.5% 🔺)7.231s (+22.1% 🔺)1.984s51.27x
▲ VercelExpress⚠️missing----

🔍 Observability: Next.js (Turbopack) | Nitro

Promise.all with 50 concurrent steps

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Express1.339s (-61.6% 🟢)2.007s (-49.9% 🟢)0.669s151.00x
🐘 PostgresNitro1.358s (-61.0% 🟢)2.007s (-49.9% 🟢)0.649s151.01x
🐘 PostgresNext.js (Turbopack)1.617s2.007s0.390s151.21x
💻 LocalNext.js (Turbopack)3.177s3.453s0.276s92.37x
💻 LocalExpress5.435s (-34.8% 🟢)6.214s (-31.2% 🟢)0.779s54.06x
💻 LocalNitro5.468s (-34.5% 🟢)6.012s (-33.3% 🟢)0.544s54.08x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Nitro6.778s (+92.3% 🔺)8.495s (+53.5% 🔺)1.716s41.00x
▲ VercelNext.js (Turbopack)8.699s (-2.4%)11.031s (+0.6%)2.332s31.28x
▲ VercelExpress⚠️missing----

🔍 Observability: Nitro | Next.js (Turbopack)

Promise.race with 10 concurrent steps

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Express1.142s (-9.1% 🟢)2.009s (~)0.866s151.00x
🐘 PostgresNitro1.152s (-8.3% 🟢)2.008s (~)0.856s151.01x
🐘 PostgresNext.js (Turbopack)1.209s2.008s0.800s151.06x
💻 LocalNext.js (Turbopack)1.209s2.004s0.795s151.06x
💻 LocalNitro1.417s (-24.1% 🟢)2.006s (-14.3% 🟢)0.590s151.24x
💻 LocalExpress1.427s (-24.7% 🟢)2.006s (-15.1% 🟢)0.580s151.25x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Next.js (Turbopack)2.873s (-2.0%)4.428s (-4.6%)1.555s71.00x
▲ VercelNitro2.925s (+18.9% 🔺)5.006s (+20.1% 🔺)2.081s61.02x
▲ VercelExpress⚠️missing----

🔍 Observability: Next.js (Turbopack) | Nitro

Promise.race with 25 concurrent steps

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Nitro1.214s (-48.1% 🟢)2.008s (-33.3% 🟢)0.794s151.00x
🐘 PostgresExpress1.226s (-47.6% 🟢)2.009s (-33.3% 🟢)0.783s151.01x
🐘 PostgresNext.js (Turbopack)1.343s2.009s0.666s151.11x
💻 LocalNext.js (Turbopack)1.579s2.005s0.426s151.30x
💻 LocalNitro2.034s (-33.6% 🟢)2.510s (-35.4% 🟢)0.475s121.68x
💻 LocalExpress2.057s (-34.3% 🟢)2.508s (-33.3% 🟢)0.451s121.69x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Nitro3.368s (+4.2%)5.319s (+4.8%)1.951s61.00x
▲ VercelNext.js (Turbopack)4.111s (+30.8% 🔺)5.838s (+29.1% 🔺)1.726s61.22x
▲ VercelExpress⚠️missing----

🔍 Observability: Nitro | Next.js (Turbopack)

Promise.race with 50 concurrent steps

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Nitro1.316s (-62.2% 🟢)2.009s (-49.9% 🟢)0.693s151.00x
🐘 PostgresExpress1.334s (-61.9% 🟢)2.007s (-50.0% 🟢)0.673s151.01x
🐘 PostgresNext.js (Turbopack)1.644s2.008s0.364s151.25x
💻 LocalNext.js (Turbopack)3.454s4.012s0.558s82.62x
💻 LocalExpress5.760s (-34.6% 🟢)6.216s (-33.0% 🟢)0.456s54.38x
💻 LocalNitro5.993s (-34.5% 🟢)6.513s (-35.0% 🟢)0.521s64.55x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Next.js (Turbopack)5.982s (-11.5% 🟢)7.562s (-11.5% 🟢)1.580s41.00x
▲ VercelNitro6.436s (+26.4% 🔺)8.067s (+18.3% 🔺)1.632s41.08x
▲ VercelExpress⚠️missing----

🔍 Observability: Next.js (Turbopack) | Nitro

workflow with 10 sequential data payload steps (10KB)

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Nitro0.428s (-47.8% 🟢)1.006s (~)0.578s601.00x
🐘 PostgresExpress0.467s (-44.3% 🟢)1.006s (-1.7%)0.539s601.09x
💻 LocalNitro0.493s (-49.7% 🟢)1.022s (-6.6% 🟢)0.529s591.15x
💻 LocalExpress0.517s (-47.4% 🟢)1.022s (-5.0% 🟢)0.504s591.21x
💻 LocalNext.js (Turbopack)0.615s1.021s0.406s591.44x
🐘 PostgresNext.js (Turbopack)0.675s1.006s0.332s601.58x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Nitro5.901s (-73.2% 🟢)7.712s (-67.9% 🟢)1.811s81.00x
▲ VercelNext.js (Turbopack)6.006s (-58.6% 🟢)7.961s (-50.5% 🟢)1.956s81.02x
▲ VercelExpress⚠️missing----

🔍 Observability: Nitro | Next.js (Turbopack)

workflow with 25 sequential data payload steps (10KB)

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Nitro0.995s (-48.4% 🟢)1.350s (-35.7% 🟢)0.355s671.00x
🐘 PostgresExpress1.188s (-39.9% 🟢)1.922s (-14.9% 🟢)0.734s471.19x
💻 LocalNitro1.220s (-59.8% 🟢)2.006s (-46.6% 🟢)0.786s451.23x
💻 LocalExpress1.260s (-58.2% 🟢)2.007s (-44.0% 🟢)0.746s451.27x
💻 LocalNext.js (Turbopack)1.421s2.004s0.583s451.43x
🐘 PostgresNext.js (Turbopack)1.660s2.008s0.347s451.67x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Nitro16.140s (-59.1% 🟢)18.101s (-56.2% 🟢)1.961s61.00x
▲ VercelNext.js (Turbopack)16.668s (-66.5% 🟢)19.003s (-63.3% 🟢)2.335s51.03x
▲ VercelExpress⚠️missing----

🔍 Observability: Nitro | Next.js (Turbopack)

workflow with 50 sequential data payload steps (10KB)

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Nitro1.994s (-51.4% 🟢)2.335s (-49.3% 🟢)0.341s521.00x
🐘 PostgresExpress2.106s (-47.2% 🟢)2.697s (-38.3% 🟢)0.591s451.06x
💻 LocalNitro2.786s (-70.0% 🟢)3.058s (-69.5% 🟢)0.272s401.40x
💻 LocalExpress2.801s (-69.6% 🟢)3.033s (-69.7% 🟢)0.231s401.41x
🐘 PostgresNext.js (Turbopack)3.212s4.009s0.797s301.61x
💻 LocalNext.js (Turbopack)3.466s4.074s0.608s301.74x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Nitro27.620s (-71.5% 🟢)29.816s (-69.7% 🟢)2.196s51.00x
▲ VercelNext.js (Turbopack)32.787s (-69.4% 🟢)35.186s (-67.7% 🟢)2.399s41.19x
▲ VercelExpress⚠️missing----

🔍 Observability: Nitro | Next.js (Turbopack)

workflow with 10 concurrent data payload steps (10KB)

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Nitro0.190s (-33.1% 🟢)1.006s (~)0.816s601.00x
🐘 PostgresExpress0.198s (-30.0% 🟢)1.023s (+1.5%)0.825s591.04x
🐘 PostgresNext.js (Turbopack)0.230s1.006s0.776s601.21x
💻 LocalNext.js (Turbopack)0.337s1.003s0.666s601.78x
💻 LocalExpress0.381s (-32.0% 🟢)1.004s (~)0.623s602.01x
💻 LocalNitro0.399s (-34.0% 🟢)1.004s (-1.7%)0.605s602.11x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Nitro2.453s (+47.7% 🔺)4.362s (+30.2% 🔺)1.909s141.00x
▲ VercelNext.js (Turbopack)2.813s (+39.1% 🔺)4.846s (+27.8% 🔺)2.034s131.15x
▲ VercelExpress⚠️missing----

🔍 Observability: Nitro | Next.js (Turbopack)

workflow with 25 concurrent data payload steps (10KB)

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Nitro0.284s (-42.8% 🟢)1.006s (~)0.722s901.00x
🐘 PostgresExpress0.308s (-39.6% 🟢)1.006s (~)0.698s901.08x
🐘 PostgresNext.js (Turbopack)0.435s1.006s0.571s901.53x
💻 LocalNext.js (Turbopack)1.433s2.028s0.595s455.05x
💻 LocalExpress2.159s (-14.1% 🟢)2.715s (-9.8% 🟢)0.556s347.60x
💻 LocalNitro2.171s (-14.5% 🟢)2.884s (-4.1%)0.713s327.65x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Nitro5.296s (+64.2% 🔺)6.881s (+42.7% 🔺)1.585s141.00x
▲ VercelNext.js (Turbopack)6.720s (+90.1% 🔺)8.608s (+65.8% 🔺)1.888s111.27x
▲ VercelExpress⚠️missing----

🔍 Observability: Nitro | Next.js (Turbopack)

workflow with 50 concurrent data payload steps (10KB)

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Nitro0.583s (-26.2% 🟢)1.006s (~)0.422s1201.00x
🐘 PostgresExpress0.652s (-20.4% 🟢)1.006s (-1.1%)0.355s1201.12x
🐘 PostgresNext.js (Turbopack)0.890s1.078s0.188s1121.53x
💻 LocalNext.js (Turbopack)6.974s7.706s0.731s1611.96x
💻 LocalNitro10.196s (-8.9% 🟢)10.777s (-7.6% 🟢)0.581s1217.48x
💻 LocalExpress10.526s (-5.9% 🟢)11.031s (-7.6% 🟢)0.505s1118.04x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Next.js (Turbopack)15.606s (+51.1% 🔺)17.743s (+44.4% 🔺)2.137s71.00x
▲ VercelNitro16.197s (+109.7% 🔺)18.211s (+93.7% 🔺)2.014s71.04x
▲ VercelExpress⚠️missing----

🔍 Observability: Next.js (Turbopack) | Nitro

Stream Benchmarks(includes TTFB metrics)
workflow with stream

💻 Local Development

WorldFrameworkWorkflow TimeTTFBSlurpWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Nitro1.128s (+450.3% 🔺)2.001s (+100.2% 🔺)0.001s (-33.3% 🟢)2.010s (+98.8% 🔺)0.882s101.00x
💻 LocalNext.js (Turbopack)1.129s2.002s0.006s2.011s0.882s101.00x
💻 LocalNitro1.133s (+430.0% 🔺)2.005s (+99.6% 🔺)0.013s (+4.0%)2.020s (+98.3% 🔺)0.888s101.00x
💻 LocalExpress1.138s (+471.7% 🔺)2.006s (+99.7% 🔺)0.013s (+5.8% 🔺)2.021s (+98.5% 🔺)0.883s101.01x
🐘 PostgresExpress1.145s (+458.3% 🔺)2.001s (+100.4% 🔺)0.001s (-18.8% 🟢)2.010s (+98.7% 🔺)0.865s101.01x
🐘 PostgresNext.js (Turbopack)1.201s2.001s0.002s2.010s0.809s101.06x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeTTFBSlurpWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Next.js (Turbopack)2.444s (-64.3% 🟢)3.510s (-59.4% 🟢)2.280s (+260.8% 🔺)6.380s (-34.8% 🟢)3.936s101.00x
▲ VercelNitro2.490s (-35.0% 🟢)3.601s (-31.8% 🟢)13.675s (+1742.7% 🔺)17.941s (+176.7% 🔺)15.451s101.02x
▲ VercelExpress⚠️missing-----

🔍 Observability: Next.js (Turbopack) | Nitro

stream pipeline with 5 transform steps (1MB)

💻 Local Development

WorldFrameworkWorkflow TimeTTFBSlurpWall TimeOverheadSamplesvs Fastest
💻 Local🥇 Next.js (Turbopack)1.475s2.007s0.006s2.015s0.540s301.00x
🐘 PostgresNitro1.494s (+139.3% 🔺)2.002s (+98.9% 🔺)0.004s (+6.4% 🔺)2.027s (+98.3% 🔺)0.534s301.01x
💻 LocalNitro1.541s (+83.7% 🔺)2.013s (+98.9% 🔺)0.010s (+4.9%)2.025s (+81.5% 🔺)0.484s301.04x
🐘 PostgresExpress1.543s (+144.9% 🔺)2.004s (+99.1% 🔺)0.004s (+3.6%)2.028s (+98.2% 🔺)0.484s301.05x
💻 LocalExpress1.549s (+104.6% 🔺)2.013s (+95.7% 🔺)0.010s (+9.4% 🔺)2.026s (+94.8% 🔺)0.476s301.05x
🐘 PostgresNext.js (Turbopack)1.678s2.010s0.004s2.025s0.347s301.14x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeTTFBSlurpWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Next.js (Turbopack)5.989s (-64.6% 🟢)7.269s (-60.1% 🟢)0.284s (+34.4% 🔺)8.226s (-56.6% 🟢)2.237s81.00x
▲ VercelNitro6.280s (-78.7% 🟢)7.470s (-75.8% 🟢)0.267s (+138.6% 🔺)8.303s (-73.9% 🟢)2.023s81.05x
▲ VercelExpress⚠️missing-----

🔍 Observability: Next.js (Turbopack) | Nitro

10 parallel streams (1MB each)

💻 Local Development

WorldFrameworkWorkflow TimeTTFBSlurpWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Nitro0.640s (-34.0% 🟢)1.012s (-18.8% 🟢)0.000s (+22.0% 🔺)1.030s (-18.1% 🟢)0.391s591.00x
🐘 PostgresExpress0.664s (-30.9% 🟢)1.011s (-20.9% 🟢)0.000s (-61.0% 🟢)1.032s (-21.0% 🟢)0.369s591.04x
🐘 PostgresNext.js (Turbopack)0.766s1.017s0.000s1.032s0.266s591.20x
💻 LocalNext.js (Turbopack)1.042s1.631s0.000s1.634s0.592s371.63x
💻 LocalExpress1.344s (+9.7% 🔺)2.016s (~)0.000s (-30.0% 🟢)2.018s (~)0.674s302.10x
💻 LocalNitro1.373s (+12.3% 🔺)2.016s (~)0.000s (+66.7% 🔺)2.018s (~)0.645s302.15x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeTTFBSlurpWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Nitro3.957s (+29.7% 🔺)5.319s (+21.1% 🔺)0.000s (+18.2% 🔺)5.894s (+22.6% 🔺)1.937s111.00x
▲ VercelNext.js (Turbopack)3.981s (-60.9% 🟢)5.285s (-54.1% 🟢)0.000s (+Infinity% 🔺)5.843s (-51.5% 🟢)1.862s111.01x
▲ VercelExpress⚠️missing-----

🔍 Observability: Nitro | Next.js (Turbopack)

fan-out fan-in 10 streams (1MB each)

💻 Local Development

WorldFrameworkWorkflow TimeTTFBSlurpWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Nitro1.278s (-28.7% 🟢)2.029s (-5.2% 🟢)0.000s (-100.0% 🟢)2.049s (-5.7% 🟢)0.771s301.00x
🐘 PostgresExpress1.384s (-21.9% 🟢)2.029s (-6.8% 🟢)0.000s (+Infinity% 🔺)2.044s (-7.1% 🟢)0.659s301.08x
🐘 PostgresNext.js (Turbopack)1.500s2.145s0.000s2.153s0.654s281.17x
💻 LocalNext.js (Turbopack)2.061s2.523s0.000s2.528s0.467s241.61x
💻 LocalExpress3.109s (-10.3% 🟢)3.781s (-6.3% 🟢)0.001s (-21.9% 🟢)3.784s (-6.3% 🟢)0.675s162.43x
💻 LocalNitro3.183s (-6.0% 🟢)3.903s (-3.2%)0.000s (-18.0% 🟢)3.906s (-3.2%)0.723s162.49x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeTTFBSlurpWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Nitro5.980s (+46.1% 🔺)7.651s (+42.4% 🔺)0.000s (-54.2% 🟢)8.181s (+41.2% 🔺)2.201s81.00x
▲ VercelNext.js (Turbopack)6.105s (+8.7% 🔺)7.426s (+6.4% 🔺)0.000s (~)7.952s (+5.5% 🔺)1.847s81.02x
▲ VercelExpress⚠️missing-----

🔍 Observability: Nitro | Next.js (Turbopack)

Summary

Fastest Framework by World

Winner determined by most benchmark wins

World🥇 Fastest FrameworkWins
💻 LocalNext.js (Turbopack)12/21
🐘 PostgresNitro17/21
▲ VercelNitro14/21
Fastest World by Framework

Winner determined by most benchmark wins

Framework🥇 Fastest WorldWins
Express🐘 Postgres17/21
Next.js (Turbopack)🐘 Postgres11/21
Nitro🐘 Postgres19/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)

📋 View full workflow run


Some benchmark jobs failed:

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

Check the workflow run for details.

@github-actions

github-actionsBot commented May 22, 2026

Copy link
Copy Markdown
Contributor

🧪 E2E Test Results

Some tests failed

Summary

PassedFailedSkippedTotal
❌ ▲ Vercel Production121932191441
❌ 💻 Local Development161052191834
❌ 📦 Local Production161412191834
❌ 🐘 Local Postgres150212001703
✅ 🪟 Windows13100131
❌ 📋 Other7401176917
Total68161110337860

❌ Failed Tests

▲ Vercel Production (3 failed)

nitro (1 failed):

  • outputStreamInsideStepWorkflow - getWritable() called inside step functions | wrun_01KS7ZJGW73X7CKYTEQBGY87E5 | 🔍 observability

nuxt (1 failed):

  • hookWorkflow is not resumable via public webhook endpoint | wrun_01KS7ZJ3YXC4E16B345GAMR59H | 🔍 observability

sveltekit (1 failed):

💻 Local Development (5 failed)

astro-stable (2 failed):

  • fibonacciWorkflow - recursive workflow composition via start() | wrun_01KS7ZKMSDH7F5ZJHKWBKZDJPQ
  • AbortController abortExternalSignalInFlightWorkflow: external abort fires mid-flight, propagates to nested steps

nextjs-webpack-stable-lazy-discovery-disabled (1 failed):

  • error handling error propagation step errors basic step error preserves message and stack trace

nuxt-stable (1 failed):

  • AbortController abortExternalSignalInFlightWorkflow: external abort fires mid-flight, propagates to nested steps

sveltekit-stable (1 failed):

  • AbortController abortExternalSignalInFlightWorkflow: external abort fires mid-flight, propagates to nested steps
📦 Local Production (1 failed)

nuxt-stable (1 failed):

  • error handling error propagation step errors basic step error preserves message and stack trace
🐘 Local Postgres (1 failed)

nuxt-stable (1 failed):

  • error handling error propagation step errors basic step error preserves message and stack trace
📋 Other (1 failed)

e2e-local-dev-tanstack-start- (1 failed):

  • AbortController abortExternalSignalInFlightWorkflow: external abort fires mid-flight, propagates to nested steps

Details by Category

❌ ▲ Vercel Production
AppPassedFailedSkipped
✅ astro105026
✅ example105026
✅ express105026
✅ fastify105026
✅ hono105026
✅ nextjs-turbopack12902
✅ nextjs-webpack12902
❌ nitro104126
❌ nuxt104126
❌ sveltekit12317
✅ vite105026
❌ 💻 Local Development
AppPassedFailedSkipped
❌ astro-stable104225
✅ express-stable106025
✅ fastify-stable106025
✅ hono-stable106025
✅ nextjs-turbopack-canary112019
✅ nextjs-turbopack-stable-lazy-discovery-disabled13100
✅ nextjs-turbopack-stable-lazy-discovery-enabled13100
✅ nextjs-webpack-canary112019
❌ nextjs-webpack-stable-lazy-discovery-disabled13010
✅ nextjs-webpack-stable-lazy-discovery-enabled13100
✅ nitro-stable106025
❌ nuxt-stable105125
❌ sveltekit-stable12416
✅ vite-stable106025
❌ 📦 Local Production
AppPassedFailedSkipped
✅ astro-stable106025
✅ express-stable106025
✅ fastify-stable106025
✅ hono-stable106025
✅ nextjs-turbopack-canary112019
✅ nextjs-turbopack-stable-lazy-discovery-disabled13100
✅ nextjs-turbopack-stable-lazy-discovery-enabled13100
✅ nextjs-webpack-canary112019
✅ nextjs-webpack-stable-lazy-discovery-disabled13100
✅ nextjs-webpack-stable-lazy-discovery-enabled13100
✅ nitro-stable106025
❌ nuxt-stable105125
✅ sveltekit-stable12506
✅ vite-stable106025
❌ 🐘 Local Postgres
AppPassedFailedSkipped
✅ astro-stable106025
✅ express-stable106025
✅ fastify-stable106025
✅ hono-stable106025
✅ nextjs-turbopack-stable-lazy-discovery-disabled13100
✅ nextjs-turbopack-stable-lazy-discovery-enabled13100
✅ nextjs-webpack-canary112019
✅ nextjs-webpack-stable-lazy-discovery-disabled13100
✅ nextjs-webpack-stable-lazy-discovery-enabled13100
✅ nitro-stable106025
❌ nuxt-stable105125
✅ sveltekit-stable12506
✅ vite-stable106025
✅ 🪟 Windows
AppPassedFailedSkipped
✅ nextjs-turbopack13100
❌ 📋 Other
AppPassedFailedSkipped
✅ e2e-local-dev-nest-stable106025
❌ e2e-local-dev-tanstack-start-105125
✅ e2e-local-postgres-nest-stable106025
✅ e2e-local-postgres-tanstack-start-106025
✅ e2e-local-prod-nest-stable106025
✅ e2e-local-prod-tanstack-start-106025
✅ e2e-vercel-prod-tanstack-start105026

📋 View full workflow run


Some E2E test jobs failed:

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

Check the workflow run for details.

@VaguelySerious

Copy link
Copy Markdown
MemberAuthor

CI verdict

Speedup is real. Per-workbench wall-clock dropped roughly 3×: from ~14–17 min on main to ~5–6 min here. Vitest's own run is even more dramatic — express went from ~15 min to 203s (~4–5× on the actual test phase).

But correctness regressed. 25 checks failed: every `E2E Vercel Prod Tests (*)` matrix entry except `nitro`, plus several `E2E Local Dev/Prod/Postgres` jobs and `E2E Windows Tests`.

Failure pattern

Every failure I sampled is the same shape: `Error: Test timed out in 60000ms`. The which tests fail varies per workbench — i.e. it's not a single broken test:

WorkbenchFailed test
expresserror handling > catchability > FatalError can be caught… (×2)
nuxtabortThrowIfAbortedWorkflow, step throw round-trips FatalError
astroabortDeterministicBranchFromStepWorkflow, abortListenerWorkflow, cross-file step error
honoFatalError fails immediately without retries
tanstack-startabortHookOrderingWorkflow (×2), abortReasonWorkflow
fastifyabortHookOrderingWorkflow [hook-first-hook-first]
sveltekitFatalError fails immediately without retries
viteabortDeterministicBranchWorkflow, abortVoidSleepTimeoutWorkflow

The cluster is real though: AbortController + error-handling tests, which all do multiple sequential workflow round-trips and historically run close to the 60s budget.

Diagnosis

Default vitest `maxConcurrency` is 5. With five workflow runs hammering one workbench in parallel, the heavier multi-round-trip tests appear to queue past the per-test 60s budget. Local-Postgres/Prod/Dev also fail, so it's not Vercel-specific — workflow-server (local) and the in-process world also slow under contention.

Proposed next steps (separate PRs)

Holding off on bundling point (5) (shortening `sleep()` durations) into this PR — the suite isn't healthy enough to add another variable.

Three angles, in order of cheapest:

  1. Bump default `testTimeout` from 60s to ~180s (in `vitest.config.ts`). Cheap, but masks the queueing rather than fixing it. May allow individual tests to silently regress to 3× their real cost.
  2. Lower `maxConcurrency` to 3 (config-level or `describe.concurrent(name, { concurrent: 3 }, ...)`). Keeps most of the speedup, eases contention. Best first attempt IMO.
  3. Mark the error-handling and AbortController describes `.sequential` and leave the rest concurrent. Finest-grained, preserves max parallelism elsewhere, but requires per-block annotations.

Leaving this PR as draft for now. Happy to iterate as a follow-up.

The e2e suite tracked failed-run diagnostics via two module-level
mutables (`trackedRuns` and `currentTestName`) that every beforeEach
reset. Under describe.concurrent that races: when a test times out,
the displayed diagnostic typically belongs to whichever sibling task
most recently called setupRunTracking(). That's why PR #2083's first
CI run showed an unrelated `errorStepCrossFile` run as the
"diagnostic" for the FatalError catchability test that actually
timed out — leaving us blind to the real failure cause.
Switch to a `Map<taskId, PerTaskState>` keyed off
`getCurrentTest().id`, with the entry dropped via onTestFinished to
keep the map bounded. Also unconditionally log
`[trackRun] <test> → <runId>` so we can still correlate runs from
stdout even when diagnostics fetching itself stalls past the test's
deadline.
…Error type in readJSON
The previous writeExclusive used `fs.writeFile(path, data, { flag: 'wx' })`,
which is atomic for the file *creation* (O_CREAT|O_EXCL) but NOT for the
subsequent data write. A concurrent reader could catch the file with
zero bytes (or just `{`) before the writer's content reached the page
cache.
This race had been silently papered over by `readHookTokenClaim`, which
swallows any SyntaxError as "no claim exists" — which is wrong: a partial
write means a claim *does* exist, just not yet flushed, and treating it as
absent lets duplicate hook tokens through. PR #2083's concurrent e2e run
also surfaced the same race on runs/<id>.json creation, producing
size=0 byte run files in CI logs.
Fix: write content to a temp file first, then `link(2)` it atomically
into place. POSIX link is atomic and fails with EEXIST if the target
already exists, preserving the exclusive-claim semantics. Readers now
either see ENOENT or the fully-populated inode — never a half-written
file.
Also preserve the SyntaxError type when annotating readJSON failures
with file path / size / content snippet, so callers like
readHookTokenClaim that intentionally swallow `error instanceof SyntaxError`
continue to work (the previous diagnostic threw a plain Error and broke
them).
Vitest's default `maxConcurrency` of 5 saturates the workbench
preview deploys and the in-process world runtime under
`describe.concurrent`, pushing load-heavy tests (fibonacci tree
spawn, multi-step abort sequencing, AbortSignal.any composition)
into their per-test deadlines on PR #2083's run 4 even though
the same tests pass cleanly when run sequentially.
Drop to 3 in the root vitest config (only `test:e2e` and `bench`
consume it — per-package configs are untouched). The Vercel-deploy
matrix went from ~14-17min sequential → ~5-6min at concurrency 5;
trading some of that headroom for stability is the right call now
that the isolation/correctness fixes have landed.
Dropping vitest's default concurrency cap from 5 to 3 (commit c7060b1)
didn't reduce the remaining e2e flake count — runs 3/4 at default
concurrency=5 and run 5 at concurrency=3 all landed on exactly 15
failures. The remaining failures are external-network and source-map
class issues (httpbin/postman-echo abort tests, nuxt/nextjs-webpack
sourcemap assertions, fibonacci tree spawn timeouts) that lower test
parallelism doesn't address. Keeping the speedup is more valuable than
the small load reduction.
@VaguelySerious

Copy link
Copy Markdown
MemberAuthor

Update

Three substantive fixes layered on, then one revert. Final state of the PR:

What was fixed

  1. Per-task diagnostic isolation (a115774) — setupRunTracking tracked failed-run diagnostics via two module-level mutables (trackedRuns, currentTestName) that every beforeEach reset. Under describe.concurrent, every test clobbered every other's state, so the failing test's diagnostic typically showed an unrelated sibling's workflow. Switched to Map<taskId, PerTaskState> keyed off getCurrentTest().id. Also unconditionally log [trackRun] <test> → <runId> so even when diagnostics fetching stalls past a 60s timeout, the per-test runId is still in stdout for backend correlation.
  2. writeExclusive content atomicity (2fce0bb) — fs.writeFile(path, data, { flag: 'wx' }) is atomic for the file creation (O_CREAT|O_EXCL) but not for the data write that follows. A concurrent reader could observe a half-written file (e.g. just {), which the hook-token-claim path was silently swallowing as "no claim exists" — a real correctness bug. Run-creation hit the same race and produced size=0 run JSON files surfaced in CI logs. Switched to write-then-link(2): write data to a temp file first, then atomically link it into place. POSIX link is atomic and fails with EEXIST if the target exists, preserving the exclusive-claim semantics. Readers now either see ENOENT or the fully-populated inode.
  3. readJSON SyntaxError annotation (same commit) — annotates partial-read SyntaxError with file path + size + content snippet, preserving the SyntaxError type so existing handlers like readHookTokenClaim's instanceof SyntaxError catch still work.

What was tried and reverted

  • maxConcurrency: 3 (c7060b1 → reverted in 6e264eb) — runs 3/4 at concurrency=5 and run 5 at concurrency=3 all landed on exactly 15 e2e failures. The remaining flakes don't respond to lower test parallelism, so the cap traded speedup for nothing.

What's left (out of scope for this PR)

  • Sourcemap assertion failures on nuxt and nextjs-webpack: expect(stack).not.toContain('99_e2e.ts') fires because hasStepSourceMaps() reports false but the stack does contain the .ts filename. Pre-existing test/runtime mismatch — independent of concurrency, just hits more often now that more workflows run per CI invocation. Needs separate investigation into the source-map detection logic.
  • External-network flakes on abort tests (abortFetchInFlightWorkflow, abortFetchUncaughtWorkflow, etc.): httpbin/postman-echo delay endpoints intermittently return early from GH Actions runners. [tests] Fix abort-fetch e2e flake #2081 added a fallback URL pair; under heavier concurrent load both upstreams flake. The proper fix is a per-workbench /api/delay/:n route.
  • Fibonacci tree spawn timeouts on Vercel Prod: fibonacciWorkflow with fib(6) spawns a tree of child runs; under concurrent load this saturates the workflow scheduler past the test's 180s budget. Could be addressed with a smaller fib(N), a longer budget, or marking that single test .sequential.

Verdict

The actual concurrency-introduced isolation/correctness bugs are fixed. Concurrency speedup holds (~3× job wall-clock; ~4–5× pure vitest run). The remaining failures are pre-existing issues that the concurrency change has made more visible but doesn't itself cause. Happy to keep this draft until those classes are tackled, or to merge with a green CI rerun depending on appetite.

Sign up for freeto 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

@VaguelySerious