') + ')', 'gi'); if (regex.test(text)) { found = true; var frag = document.createDocumentFragment(); var parts = text.split(regex); parts.forEach(function(part, i) { if (i % 2 === 0) { frag.appendChild(document.createTextNode(part)); } else { var span = document.createElement('span'); span.className = 'userscript-highlight'; span.textContent = part; frag.appendChild(span); } }); node.parentNode.replaceChild(frag, node); } }); } else if (node.nodeType === 1 && node.childNodes) { // element var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT']; if (!skipTags.includes(node.tagName)) { Array.from(node.childNodes).forEach(highlight); } } } highlight(document.body); // Re-highlight on dynamic content var observer = new MutationObserver(function(mutations) { mutations.forEach(function(m) { m.addedNodes.forEach(function(node) { if (node.nodeType === 1 || node.nodeType === 3) highlight(node); }); }); }); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ', 'i'); if (__m === '*' || __re.test(location.href)) { // Strip utm_, fbclid, gclid, etc. from all links on page (function() { var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content', 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid', 'ref', 'ref_src', 'source', 'medium', 'campaign']; function cleanUrl(url) { try { var u = new URL(url, window.location.origin); var changed = false; trackingParams.forEach(function(p) { if (u.searchParams.has(p)) { u.searchParams.delete(p); changed = true; } }); return changed ? u.toString() : url; } catch (e) { return url; } } function cleanLinks() { document.querySelectorAll('a[href]').forEach(function(a) { var clean = cleanUrl(a.href); if (clean !== a.href) a.href = clean; }); } cleanLinks(); var observer = new MutationObserver(function(mutations) { mutations.forEach(function(m) { m.addedNodes.forEach(function(node) { if (node.nodeType === 1) { if (node.tagName === 'A') cleanLinks(); node.querySelectorAll('a[href]').forEach(function(a) { var clean = cleanUrl(a.href); if (clean !== a.href) a.href = clean; }); } }); }); }); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + ', 'i'); if (__m === '*' || __re.test(location.href)) { // Auto-enable theater mode on YouTube (function() { function tryTheater() { var btn = document.querySelector('button[aria-label="Theater mode"], ytd-player #player button[title="Theater mode"]'); if (btn && !btn.classList.contains('activated')) { btn.click(); } } // Try immediately tryTheater(); // Try after navigation (SPA) var lastUrl = location.href; setInterval(function() { if (location.href !== lastUrl) { lastUrl = location.href; setTimeout(tryTheater, 500); } }, 1000); // Also try on player load var observer = new MutationObserver(tryTheater); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ', 'i'); if (__m === '*' || __re.test(location.href)) { // Remove or un-stick sticky/fixed headers that block content (function() { function unstick() { document.querySelectorAll('header, nav, [role="banner"], .header, .navbar, .sticky, .fixed-top, [style*="position: fixed"], [style*="position:sticky"]').forEach(function(el) { if (el.style.position === 'fixed' || el.style.position === 'sticky' || getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') { el.style.position = 'static'; el.style.top = 'auto'; el.style.zIndex = 'auto'; } }); } unstick(); var observer = new MutationObserver(unstick); observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] }); })(); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); })(); [core] Retry stream reopen against the reconnect budget by VaguelySerious · Pull Request #2334 · vercel/workflow · GitHub
Skip to content

[core] Retry stream reopen against the reconnect budget - #2334

Merged
VaguelySerious merged 1 commit into
mainfrom
peter/reconnect-budgeted-reopen
Jun 22, 2026
Merged

[core] Retry stream reopen against the reconnect budget#2334
VaguelySerious merged 1 commit into
mainfrom
peter/reconnect-budgeted-reopen

Conversation

@VaguelySerious

@VaguelySeriousVaguelySerious commented Jun 10, 2026

Copy link
Copy Markdown
Member

Follow-up to #2318 (now merged), addressing @TooTallNate's review note on it.

#2318's forward-port made object-stream reconnect retry on reader.read() errors, but a transient failure of the reopen itselfconnect() throwing inside reconnect() because the server is briefly unavailable during the reconnect window — was fatal. That's exactly the blip the wrapper exists to survive.

This makes the reopen retry against the reconnect budget instead of erroring the stream: each reopen attempt counts against the consecutive-failure cap (and the absolute backstop), so a server that stays down still terminates the stream, but a momentary blip no longer kills it. The cancel/reset of buffered state moves ahead of the retry loop so every attempt resumes from the same frame index.

Test

New case in reconnecting-framed-stream.test.ts: a reopen that throws once then succeeds recovers transparently (the failed reopen never surfaces to the consumer; resume index is unchanged). All 12 reconnect tests pass.

Notes

  • Scoped to @workflow/core; user-facing behavior is unchanged (object streams reconnect) — this hardens the path against the blip it targets.
  • Same sequencing as the stable line ([core] Move stream reconnect logic to getReadable level #1847): the reconnect path only triggers once the coordinated server-side change errors timed-out connections instead of closing them.

🤖 Generated with Claude Code

@changeset-bot

changeset-botBot commented Jun 10, 2026

Copy link
Copy Markdown

⚠️ No Changeset found

Latest commit: 620bd3e

Merging this PR will not cause a version bump for any packages. If these changes should not result in a new version, you're good to go. If these changes should result in a version bump, you need to add a changeset.

This PR includes no changesets

When changesets are added to this PR, you'll see the packages that this PR includes changesets for and the associated semver types

Click here to learn what changesets are, and how to add one.

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

@vercel

vercelBot commented Jun 10, 2026

Copy link
Copy Markdown
Contributor

@VaguelySerious
VaguelySeriousforce-pushed the peter/reconnect-budgeted-reopen branch from 1ec49b1 to 19a31ceCompareJune 11, 2026 07:15
@github-actions

github-actionsBot commented Jun 11, 2026

Copy link
Copy Markdown
Contributor

🧪 E2E Test Results

All tests passed

Summary

PassedFailedSkippedTotal
✅ ▲ Vercel Production144202301672
✅ 💻 Local Development160502191824
✅ 📦 Local Production160502191824
✅ 🐘 Local Postgres159302311824
✅ 🪟 Windows15200152
✅ 📋 Other88501791064
Total7282010788360

Details by Category

✅ ▲ Vercel Production
AppPassedFailedSkipped
✅ astro125027
✅ example125027
✅ express125027
✅ fastify125027
✅ hono125027
✅ nextjs-turbopack14903
✅ nextjs-webpack14903
✅ nitro125027
✅ nuxt125027
✅ sveltekit14408
✅ vite125027
✅ 💻 Local Development
AppPassedFailedSkipped
✅ astro-stable127025
✅ express-stable127025
✅ fastify-stable127025
✅ hono-stable127025
✅ nextjs-turbopack-canary133019
✅ nextjs-turbopack-stable15200
✅ nextjs-webpack-canary133019
✅ nextjs-webpack-stable15200
✅ nitro-stable127025
✅ nuxt-stable127025
✅ sveltekit-stable14606
✅ vite-stable127025
✅ 📦 Local Production
AppPassedFailedSkipped
✅ astro-stable127025
✅ express-stable127025
✅ fastify-stable127025
✅ hono-stable127025
✅ nextjs-turbopack-canary133019
✅ nextjs-turbopack-stable15200
✅ nextjs-webpack-canary133019
✅ nextjs-webpack-stable15200
✅ nitro-stable127025
✅ nuxt-stable127025
✅ sveltekit-stable14606
✅ vite-stable127025
✅ 🐘 Local Postgres
AppPassedFailedSkipped
✅ astro-stable126026
✅ express-stable126026
✅ fastify-stable126026
✅ hono-stable126026
✅ nextjs-turbopack-canary132020
✅ nextjs-turbopack-stable15101
✅ nextjs-webpack-canary132020
✅ nextjs-webpack-stable15101
✅ nitro-stable126026
✅ nuxt-stable126026
✅ sveltekit-stable14507
✅ vite-stable126026
✅ 🪟 Windows
AppPassedFailedSkipped
✅ nextjs-turbopack15200
✅ 📋 Other
AppPassedFailedSkipped
✅ e2e-local-dev-nest-stable127025
✅ e2e-local-dev-tanstack-start-127025
✅ e2e-local-postgres-nest-stable126026
✅ e2e-local-postgres-tanstack-start-126026
✅ e2e-local-prod-nest-stable127025
✅ e2e-local-prod-tanstack-start-127025
✅ e2e-vercel-prod-tanstack-start125027

📋 View full workflow run

@github-actions

github-actionsBot commented Jun 11, 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.044s (+7.1% 🔺)1.005s (~)0.962s101.00x
💻 LocalExpress0.047s (+14.1% 🔺)1.006s (~)0.959s101.07x
💻 LocalNext.js (Turbopack)0.047s (-2.1%)1.006s (~)0.959s101.07x
🐘 PostgresNext.js (Turbopack)0.059s (+1.2%)1.013s (~)0.954s101.34x
🐘 PostgresExpress0.061s (-0.8%)1.012s (~)0.951s101.39x
🐘 PostgresNitro0.065s (+2.2%)1.013s (~)0.948s101.47x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Next.js (Turbopack)0.457s (+41.2% 🔺)2.296s (-17.7% 🟢)1.840s101.00x
▲ VercelExpress⚠️missing----
▲ VercelNitro⚠️missing----

🔍 Observability: Next.js (Turbopack)

workflow with 1 step

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
💻 Local🥇 Nitro1.075s (-1.2%)2.006s (~)0.931s101.00x
💻 LocalExpress1.078s (-2.0%)2.007s (~)0.928s101.00x
💻 LocalNext.js (Turbopack)1.086s (-0.5%)2.006s (~)0.920s101.01x
🐘 PostgresNext.js (Turbopack)1.093s (-0.9%)2.009s (~)0.917s101.02x
🐘 PostgresExpress1.094s (-0.8%)2.010s (~)0.916s101.02x
🐘 PostgresNitro1.095s (-0.8%)2.010s (~)0.914s101.02x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Next.js (Turbopack)1.655s (+4.9%)3.675s (-1.1%)2.019s101.00x
▲ VercelExpress⚠️missing----
▲ VercelNitro⚠️missing----

🔍 Observability: Next.js (Turbopack)

workflow with 10 sequential steps

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
💻 Local🥇 Nitro10.417s (-0.9%)11.022s (~)0.605s31.00x
💻 LocalExpress10.468s (~)11.023s (~)0.554s31.00x
🐘 PostgresExpress10.480s (-0.6%)11.020s (~)0.540s31.01x
🐘 PostgresNitro10.491s (-0.5%)11.021s (~)0.530s31.01x
🐘 PostgresNext.js (Turbopack)10.513s (-0.6%)11.016s (~)0.504s31.01x
💻 LocalNext.js (Turbopack)10.514s (~)11.023s (~)0.509s31.01x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Next.js (Turbopack)12.539s (-4.9%)15.101s (-1.8%)2.562s21.00x
▲ VercelExpress⚠️missing----
▲ VercelNitro⚠️missing----

🔍 Observability: Next.js (Turbopack)

workflow with 25 sequential steps

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
💻 Local🥇 Nitro13.564s (-0.9%)14.029s (~)0.465s51.00x
💻 LocalExpress13.591s (-2.9%)14.028s (-1.4%)0.437s51.00x
💻 LocalNext.js (Turbopack)13.592s (-0.8%)14.027s (~)0.435s51.00x
🐘 PostgresExpress13.659s (-0.9%)14.019s (~)0.361s51.01x
🐘 PostgresNitro13.771s (-1.0%)14.019s (~)0.248s51.02x
🐘 PostgresNext.js (Turbopack)13.798s (-0.9%)14.021s (~)0.224s51.02x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Next.js (Turbopack)19.058s (-4.4%)21.009s (-5.4% 🟢)1.951s31.00x
▲ VercelExpress⚠️missing----
▲ VercelNitro⚠️missing----

🔍 Observability: Next.js (Turbopack)

workflow with 50 sequential steps

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
💻 Local🥇 Nitro12.061s (-1.9%)13.025s (~)0.964s71.00x
💻 LocalExpress12.113s (-3.8%)13.026s (~)0.913s71.00x
🐘 PostgresExpress12.166s (-2.3%)13.017s (~)0.851s71.01x
💻 LocalNext.js (Turbopack)12.241s (-1.7%)13.025s (~)0.784s71.01x
🐘 PostgresNext.js (Turbopack)12.248s (-2.3%)13.017s (~)0.769s71.02x
🐘 PostgresNitro12.321s (-0.7%)13.018s (~)0.697s71.02x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Next.js (Turbopack)25.689s (+2.1%)27.565s (~)1.877s41.00x
▲ VercelExpress⚠️missing----
▲ VercelNitro⚠️missing----

🔍 Observability: Next.js (Turbopack)

Promise.all with 10 concurrent steps

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Express1.187s (-0.7%)2.008s (~)0.821s151.00x
🐘 PostgresNitro1.195s (+0.6%)2.009s (~)0.814s151.01x
🐘 PostgresNext.js (Turbopack)1.214s (+4.8%)2.008s (~)0.794s151.02x
💻 LocalExpress1.394s (+13.3% 🔺)2.006s (~)0.613s151.17x
💻 LocalNitro1.424s (+18.5% 🔺)2.006s (~)0.582s151.20x
💻 LocalNext.js (Turbopack)1.434s (+4.3%)2.006s (~)0.572s151.21x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Next.js (Turbopack)4.266s (+64.5% 🔺)6.390s (+44.3% 🔺)2.124s51.00x
▲ VercelExpress⚠️missing----
▲ VercelNitro⚠️missing----

🔍 Observability: Next.js (Turbopack)

Promise.all with 25 concurrent steps

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Express1.304s (~)2.508s (+20.9% 🔺)1.204s121.00x
🐘 PostgresNext.js (Turbopack)1.323s (+5.7% 🔺)2.918s (+45.4% 🔺)1.595s111.01x
🐘 PostgresNitro1.328s (+4.7%)2.593s (+29.2% 🔺)1.264s121.02x
💻 LocalExpress2.300s (+13.0% 🔺)2.735s (+9.1% 🔺)0.435s111.76x
💻 LocalNitro2.460s (+22.1% 🔺)3.008s (+20.0% 🔺)0.548s101.89x
💻 LocalNext.js (Turbopack)2.487s (+6.1% 🔺)3.109s (+6.6% 🔺)0.621s101.91x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Next.js (Turbopack)3.275s (+2.8%)5.194s (+1.3%)1.919s61.00x
▲ VercelExpress⚠️missing----
▲ VercelNitro⚠️missing----

🔍 Observability: Next.js (Turbopack)

Promise.all with 50 concurrent steps

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Express1.603s (-3.3%)4.013s (+6.7% 🔺)2.410s81.00x
🐘 PostgresNitro1.644s (+13.4% 🔺)4.138s (+12.5% 🔺)2.494s81.03x
🐘 PostgresNext.js (Turbopack)2.738s (+84.8% 🔺)5.681s (+46.2% 🔺)2.942s61.71x
💻 LocalNext.js (Turbopack)5.297s (-20.8% 🟢)5.847s (-21.2% 🟢)0.550s63.31x
💻 LocalNitro5.316s (+18.6% 🔺)6.015s (+20.0% 🔺)0.699s53.32x
💻 LocalExpress6.647s (+15.2% 🔺)7.217s (+12.5% 🔺)0.570s54.15x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Next.js (Turbopack)5.528s (+57.8% 🔺)7.331s (+34.9% 🔺)1.803s51.00x
▲ VercelExpress⚠️missing----
▲ VercelNitro⚠️missing----

🔍 Observability: Next.js (Turbopack)

Promise.race with 10 concurrent steps

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Next.js (Turbopack)1.181s (+0.7%)2.008s (~)0.827s151.00x
🐘 PostgresExpress1.182s (-1.1%)2.008s (~)0.825s151.00x
🐘 PostgresNitro1.190s (-0.7%)2.008s (~)0.818s151.01x
💻 LocalNitro1.395s (+13.5% 🔺)2.006s (~)0.611s151.18x
💻 LocalNext.js (Turbopack)1.425s (+5.5% 🔺)2.007s (~)0.582s151.21x
💻 LocalExpress1.457s (+18.5% 🔺)2.007s (~)0.550s151.23x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Next.js (Turbopack)3.588s (+67.7% 🔺)6.076s (+49.8% 🔺)2.488s61.00x
▲ VercelExpress⚠️missing----
▲ VercelNitro⚠️missing----

🔍 Observability: Next.js (Turbopack)

Promise.race with 25 concurrent steps

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Next.js (Turbopack)1.294s (+2.4%)2.918s (+40.6% 🔺)1.623s111.00x
🐘 PostgresNitro1.315s (+3.1%)2.675s (+33.2% 🔺)1.360s121.02x
🐘 PostgresExpress1.327s (+2.5%)2.471s (+23.0% 🔺)1.144s131.02x
💻 LocalExpress2.459s (+20.7% 🔺)2.735s (~)0.276s111.90x
💻 LocalNext.js (Turbopack)2.463s (+1.9%)3.210s (+10.0% 🔺)0.746s101.90x
💻 LocalNitro2.556s (+27.2% 🔺)3.009s (+30.0% 🔺)0.453s101.97x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Next.js (Turbopack)4.369s (+64.0% 🔺)6.146s (+47.6% 🔺)1.777s61.00x
▲ VercelExpress⚠️missing----
▲ VercelNitro⚠️missing----

🔍 Observability: Next.js (Turbopack)

Promise.race with 50 concurrent steps

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Express1.603s (+12.0% 🔺)4.136s (+10.0% 🔺)2.533s81.00x
🐘 PostgresNitro1.653s (+13.5% 🔺)4.298s (+14.3% 🔺)2.645s71.03x
🐘 PostgresNext.js (Turbopack)3.002s (+104.4% 🔺)6.016s (+54.8% 🔺)3.014s51.87x
💻 LocalExpress5.877s (-1.4%)6.419s (-5.8% 🟢)0.543s53.67x
💻 LocalNitro5.971s (+20.8% 🔺)6.614s (+20.0% 🔺)0.643s53.73x
💻 LocalNext.js (Turbopack)6.125s (-9.8% 🟢)6.620s (-11.9% 🟢)0.496s53.82x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Next.js (Turbopack)5.685s (+71.9% 🔺)8.141s (+54.8% 🔺)2.457s41.00x
▲ VercelExpress⚠️missing----
▲ VercelNitro⚠️missing----

🔍 Observability: Next.js (Turbopack)

workflow with 10 sequential data payload steps (10KB)

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Express0.523s (-9.2% 🟢)1.006s (-1.7%)0.483s601.00x
🐘 PostgresNext.js (Turbopack)0.541s (-4.3%)1.006s (-1.7%)0.465s601.03x
🐘 PostgresNitro0.557s (-1.0%)1.023s (~)0.467s591.06x
💻 LocalNitro0.575s (+0.7%)1.022s (~)0.447s591.10x
💻 LocalExpress0.579s (-5.1% 🟢)1.040s (+3.4%)0.460s581.11x
💻 LocalNext.js (Turbopack)0.608s (-0.5%)1.022s (~)0.414s591.16x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Next.js (Turbopack)4.153s (+0.5%)6.226s (~)2.073s101.00x
▲ VercelExpress⚠️missing----
▲ VercelNitro⚠️missing----

🔍 Observability: Next.js (Turbopack)

workflow with 25 sequential data payload steps (10KB)

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Express1.282s (-3.3%)2.007s (~)0.726s451.00x
🐘 PostgresNext.js (Turbopack)1.297s (+1.7%)2.008s (~)0.711s451.01x
🐘 PostgresNitro1.340s (+5.2% 🔺)2.007s (~)0.667s451.05x
💻 LocalExpress1.381s (-11.4% 🟢)2.006s (-1.2%)0.625s451.08x
💻 LocalNitro1.381s (~)2.006s (~)0.626s451.08x
💻 LocalNext.js (Turbopack)1.496s (+1.4%)2.006s (~)0.510s451.17x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Next.js (Turbopack)12.674s (+54.8% 🔺)14.707s (+47.1% 🔺)2.033s71.00x
▲ VercelExpress⚠️missing----
▲ VercelNitro⚠️missing----

🔍 Observability: Next.js (Turbopack)

workflow with 50 sequential data payload steps (10KB)

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Next.js (Turbopack)2.631s (+3.2%)3.008s (~)0.378s401.00x
🐘 PostgresExpress2.646s (~)3.167s (+3.5%)0.520s381.01x
🐘 PostgresNitro2.684s (+1.2%)3.111s (-0.8%)0.427s391.02x
💻 LocalExpress2.972s (-8.6% 🟢)3.466s (-13.6% 🟢)0.493s351.13x
💻 LocalNitro3.164s (+5.6% 🔺)3.758s (+9.4% 🔺)0.594s321.20x
💻 LocalNext.js (Turbopack)3.227s (+0.7%)4.009s (+0.8%)0.782s301.23x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Next.js (Turbopack)28.262s (+74.5% 🔺)30.788s (+67.2% 🔺)2.526s41.00x
▲ VercelExpress⚠️missing----
▲ VercelNitro⚠️missing----

🔍 Observability: Next.js (Turbopack)

workflow with 10 concurrent data payload steps (10KB)

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Next.js (Turbopack)0.162s (-9.3% 🟢)1.006s (~)0.844s601.00x
🐘 PostgresExpress0.207s (-5.4% 🟢)1.006s (~)0.799s601.28x
🐘 PostgresNitro0.218s (+2.8%)1.006s (~)0.789s601.34x
💻 LocalNitro0.436s (+29.3% 🔺)1.004s (~)0.568s602.69x
💻 LocalExpress0.464s (+36.0% 🔺)1.005s (~)0.541s602.86x
💻 LocalNext.js (Turbopack)0.615s (+1.6%)1.022s (+1.7%)0.407s593.79x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Next.js (Turbopack)2.325s (+76.6% 🔺)4.028s (+35.4% 🔺)1.703s151.00x
▲ VercelExpress⚠️missing----
▲ VercelNitro⚠️missing----

🔍 Observability: Next.js (Turbopack)

workflow with 25 concurrent data payload steps (10KB)

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Next.js (Turbopack)0.276s (+3.0%)1.006s (~)0.730s901.00x
🐘 PostgresNitro0.328s (+1.6%)1.017s (+1.1%)0.689s891.19x
🐘 PostgresExpress0.329s (+2.6%)1.017s (+1.1%)0.688s891.19x
💻 LocalNitro2.085s (+3.0%)2.638s (+6.9% 🔺)0.553s357.56x
💻 LocalExpress2.206s (+12.8% 🔺)2.853s (+15.6% 🔺)0.647s327.99x
💻 LocalNext.js (Turbopack)2.731s (+2.4%)3.181s (+3.4%)0.451s299.89x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Next.js (Turbopack)2.715s (+21.1% 🔺)4.354s (+4.0%)1.639s211.00x
▲ VercelExpress⚠️missing----
▲ VercelNitro⚠️missing----

🔍 Observability: Next.js (Turbopack)

workflow with 50 concurrent data payload steps (10KB)

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Next.js (Turbopack)0.498s (+3.4%)3.168s (+4.4%)2.670s381.00x
🐘 PostgresExpress0.524s (+6.3% 🔺)1.059s (+4.4%)0.535s1141.05x
🐘 PostgresNitro0.525s (~)1.050s (~)0.525s1151.05x
💻 LocalExpress9.109s (-6.9% 🟢)10.025s (-5.6% 🟢)0.916s1218.29x
💻 LocalNitro9.369s (~)10.362s (+2.5%)0.993s1218.81x
💻 LocalNext.js (Turbopack)10.518s (+4.5%)11.665s (+4.9%)1.147s1121.11x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Next.js (Turbopack)4.926s (+39.0% 🔺)7.042s (+30.0% 🔺)2.116s181.00x
▲ VercelExpress⚠️missing----
▲ VercelNitro⚠️missing----

🔍 Observability: Next.js (Turbopack)

Stream Benchmarks(includes TTFB metrics)
workflow with stream

💻 Local Development

WorldFrameworkWorkflow TimeTTFBSlurpWall TimeOverheadSamplesvs Fastest
💻 Local🥇 Express1.136s (-3.2%)2.004s (~)0.011s (-14.6% 🟢)2.018s (~)0.882s101.00x
💻 LocalNext.js (Turbopack)1.147s (-0.6%)1.967s (-1.8%)0.013s (+1.6%)2.020s (~)0.873s101.01x
💻 LocalNitro1.153s (~)2.005s (~)0.011s (+15.2% 🔺)2.018s (~)0.865s101.01x
🐘 PostgresNitro1.161s (-0.7%)2.000s (~)0.001s (-42.1% 🟢)2.010s (~)0.849s101.02x
🐘 PostgresNext.js (Turbopack)1.167s (~)2.000s (~)0.001s (+8.3% 🔺)2.012s (~)0.846s101.03x
🐘 PostgresExpress1.171s (-0.7%)2.000s (~)0.001s (-21.4% 🟢)2.010s (~)0.840s101.03x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeTTFBSlurpWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Next.js (Turbopack)2.665s (+26.3% 🔺)4.089s (+20.9% 🔺)4.264s (+157.0% 🔺)9.031s (+63.0% 🔺)6.366s101.00x
▲ VercelExpress⚠️missing-----
▲ VercelNitro⚠️missing-----

🔍 Observability: Next.js (Turbopack)

stream pipeline with 5 transform steps (1MB)

💻 Local Development

WorldFrameworkWorkflow TimeTTFBSlurpWall TimeOverheadSamplesvs Fastest
💻 Local🥇 Express1.508s (-4.8%)2.009s (~)0.015s (+17.5% 🔺)2.026s (~)0.518s301.00x
💻 LocalNitro1.514s (-4.1%)2.010s (~)0.012s (-3.3%)2.025s (~)0.511s301.00x
🐘 PostgresExpress1.549s (-2.3%)2.003s (~)0.005s (~)2.025s (~)0.476s301.03x
🐘 PostgresNitro1.557s (-5.2% 🟢)2.007s (-1.5%)0.005s (-1.3%)2.027s (-1.5%)0.470s301.03x
💻 LocalNext.js (Turbopack)1.570s (-3.0%)1.970s (-1.9%)0.012s (-3.7%)2.027s (~)0.458s301.04x
🐘 PostgresNext.js (Turbopack)1.583s (-1.1%)2.010s (~)0.005s (+10.8% 🔺)2.026s (~)0.443s301.05x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeTTFBSlurpWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Next.js (Turbopack)7.875s (+32.6% 🔺)9.608s (+26.4% 🔺)0.423s (+28.3% 🔺)10.612s (+25.4% 🔺)2.737s61.00x
▲ VercelExpress⚠️missing-----
▲ VercelNitro⚠️missing-----

🔍 Observability: Next.js (Turbopack)

10 parallel streams (1MB each)

💻 Local Development

WorldFrameworkWorkflow TimeTTFBSlurpWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Nitro0.779s (+1.3%)1.068s (-3.3%)0.000s (-100.0% 🟢)1.085s (-3.0%)0.306s561.00x
🐘 PostgresExpress0.784s (+2.6%)1.064s (~)0.000s (NaN%)1.085s (+0.5%)0.301s561.01x
🐘 PostgresNext.js (Turbopack)1.017s (+33.5% 🔺)1.428s (+31.3% 🔺)0.000s (+171.4% 🔺)1.436s (+31.1% 🔺)0.420s421.30x
💻 LocalNitro1.180s (-13.6% 🟢)1.948s (-3.2%)0.000s (-43.5% 🟢)1.951s (-3.2%)0.771s311.51x
💻 LocalExpress1.255s (-5.1% 🟢)1.981s (-1.6%)0.000s (-38.4% 🟢)1.984s (-1.6%)0.729s311.61x
💻 LocalNext.js (Turbopack)1.365s (-5.8% 🟢)1.980s (-1.6%)0.000s (+55.6% 🔺)2.016s (~)0.651s301.75x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeTTFBSlurpWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Next.js (Turbopack)4.454s (+5.3% 🔺)5.787s (-6.9% 🟢)0.000s (-100.0% 🟢)6.349s (-6.8% 🟢)1.895s101.00x
▲ VercelExpress⚠️missing-----
▲ VercelNitro⚠️missing-----

🔍 Observability: Next.js (Turbopack)

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

💻 Local Development

WorldFrameworkWorkflow TimeTTFBSlurpWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Nitro1.720s (+3.9%)2.343s (+5.8% 🔺)0.000s (+3.8%)2.361s (+5.8% 🔺)0.641s261.00x
🐘 PostgresExpress1.833s (+19.8% 🔺)2.495s (+18.8% 🔺)0.000s (+133.3% 🔺)2.510s (+16.6% 🔺)0.677s241.07x
🐘 PostgresNext.js (Turbopack)2.234s (+17.1% 🔺)2.697s (+6.0% 🔺)0.000s (NaN%)2.706s (+6.1% 🔺)0.472s231.30x
💻 LocalNitro3.301s (-5.7% 🟢)3.965s (~)0.001s (-44.4% 🟢)3.969s (~)0.668s161.92x
💻 LocalExpress3.507s (-3.7%)4.089s (-3.3%)0.000s (-62.5% 🟢)4.093s (-3.3%)0.585s162.04x
💻 LocalNext.js (Turbopack)3.613s (-12.3% 🟢)3.989s (-13.3% 🟢)0.001s (-48.1% 🟢)4.036s (-12.4% 🟢)0.423s152.10x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeTTFBSlurpWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Next.js (Turbopack)5.382s (+1.6%)6.698s (-1.3%)0.000s (+Infinity% 🔺)7.334s (~)1.952s91.00x
▲ VercelExpress⚠️missing-----
▲ VercelNitro⚠️missing-----

🔍 Observability: Next.js (Turbopack)

Summary

Fastest Framework by World

Winner determined by most benchmark wins

World🥇 Fastest FrameworkWins
💻 LocalNitro11/21
🐘 PostgresExpress10/21
▲ VercelNext.js (Turbopack)21/21
Fastest World by Framework

Winner determined by most benchmark wins

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

@VaguelySerious
VaguelySerious requested a review from a team as a code ownerJune 11, 2026 09:10
try {
await connect();
return;
} catch {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[P1] Resolve failed stream correctness benchmark before merging

The PR’s Benchmark Vercel (nextjs-turbopack) check failed in packages/core/e2e/bench.bench.ts with Stream correctness failure: expected 1048576 bytes but got 917504. Because this PR changes stream reconnect behavior, a stream-truncation correctness failure on the Vercel benchmark is a blocker until the job reruns green or is clearly triaged as unrelated/flaky.

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(AI) Triaged as unrelated to this PR's change, and re-run triggered.

Root cause: the failure is a silent truncationtotalBytes came up 128 KiB short (917504 vs 1048576) while the stream was treated as cleanly done. That only happens on the clean-EOF path (result.donecontroller.close()). This PR touches exactly one function, reconnect(), which is reached only on a read error, never on clean EOF — it makes the reopen more resilient (retry-against-budget) and cannot cause a stream to end early. So this change is not on the code path that produced the failure.

What it actually is: the pre-existing max-duration truncation on the live read — the server hits its 2-min stream cap, closes the body cleanly, and the reader sees EOF mid-stream. It surfaced on the slower nextjs-turbopack Vercel lane (the express and nitro-v3 Vercel lanes, and all Local/Postgres lanes, passed in the same run) because that lane's 1 MiB pipeline crosses the 2-min window. This is precisely the bug the sibling PRs fix — world-vercel reading from the v3 endpoint (#2424) against the server erroring the body on timeout so the reader reconnects.

I've re-run the failed benchmark jobs; will confirm green here. "Performance Benchmarks" is a non-required workflow regardless.

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(AI) Confirmed green: after rebasing onto current main (the failing run was 11 days / 91 commits stale), Benchmark Vercel (nextjs-turbopack) now passes, along with Unit Tests (ubuntu + windows) and Build Packages. As expected this was the timing-surfaced v2 max-duration truncation rather than anything in this PR's reopen-retry change.

Addresses review feedback on #2318: a transient failure of the reopen
itself (`connect()` throwing inside `reconnect()`) was fatal — only
`reader.read()` errors were retried. A brief server unavailability during
the reconnect window is exactly the blip this wrapper exists to survive,
so count it against the reconnect budget and retry rather than erroring
the stream. Budget exhaustion (a server that stays down) still terminates.
Adds a test: a reopen that throws once then succeeds recovers transparently.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@VaguelySerious
VaguelySeriousforce-pushed the peter/reconnect-budgeted-reopen branch from 19a31ce to 620bd3eCompareJune 22, 2026 20:59
@VaguelySerious
VaguelySerious enabled auto-merge (squash) June 22, 2026 21:35
@VaguelySerious
VaguelySerious merged commit cfafddd into mainJun 22, 2026
177 of 183 checks passed
@VaguelySerious
VaguelySerious deleted the peter/reconnect-budgeted-reopen branch June 22, 2026 21:52
github-actionsBot added a commit that referenced this pull request Jun 22, 2026
Signed-off-by: Peter Wielander <mittgfu@gmail.com>
@github-actions

Copy link
Copy Markdown
Contributor

Backport PR opened against stable: #2574. (backport job run)

VaguelySerious added a commit that referenced this pull request Jun 22, 2026
…et (#2574)
Co-authored-by: Peter Wielander <mittgfu@gmail.com>
pranaygp added a commit that referenced this pull request Jun 23, 2026
…testing
* origin/main:
Trace /flow route initialization (#2592)
Version Packages (beta) (#2591)
feat(web): add trace step shortcut helper (#2582)
[web-shared] reskin json viewer (no duplicates, better colours and navigation) (#2434)
Send occurredAt with workflow events (#2580)
docs: use actual eve logo and tidy OSS nav dropdown (#2586)
Display occurredAt in trace details (#2581)
fix(next): discover root entrypoints (#2564)
[core] Turbo: skip the unused run_started event-log preload (#2569)
fix(next): prewarm SWC plugin cache (#2538)
[world-vercel] Use v3 stream endpoint (supports transparent reconnect on timeout) (#2424)
[core] Retry stream reopen against the reconnect budget (#2334)
Add Platformatic World to worlds-manifest.json (#1450)
docs: derive section landing-page cards from the page tree + lint drift (#2567)
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.

2 participants

@VaguelySerious@karthikscale3