Skip to content

refactor: make stage advancing logic more generic#94349

Merged
lubieowoce merged 1 commit into
canaryfrom
lubieowoce/stage-controller-generic
Jun 3, 2026
Merged

refactor: make stage advancing logic more generic#94349
lubieowoce merged 1 commit into
canaryfrom
lubieowoce/stage-controller-generic

Conversation

@lubieowoce
Copy link
Copy Markdown
Member

@lubieowoce lubieowoce commented Jun 2, 2026

In #93801, we'll be adding 4 new stages. StagedRenderingController already contains a lot of duplication around advancing stages, and i want to avoid adding to that, so this PR does some cleanup.

Previously, for a stage, we had

  • resolve<name>Stage - triggers listener callbacks and resolves the promise for the stage
    • <name>StageListeners - added via onStage, triggered when advancing to the stage but before resolving the promise
    • <name>StagePromise - resolved when we enter the stage (or rejected on abort/abandon)
  • sometimes, <name>EndTime

these behaviors are all now packaged up into StageTrigger, which can be "fired" (when advancing to a stage) or "cancelled" (when aborting/abandoning) and tracks the time.

In addition, we now have RENDER_STAGE_ADVANCE_ORDER, an array that defines the order in which advanceable stages (i.e. excluding Before/Abandoned) should happen. We can loop through this to advance stages without relying on a huge switch statement with fallthroughs. Re-working the logic into this shape will be handy in the future, when we want each route param to have its own stage, and the set of stages is no longer statically known.

@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented Jun 2, 2026

Tests Passed

Commit: 2eeac8e

@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented Jun 2, 2026

Stats from current PR

✅ No significant changes detected

📊 All Metrics
📖 Metrics Glossary

Dev Server Metrics:

  • Listen = TCP port starts accepting connections
  • First Request = HTTP server returns successful response
  • Cold = Fresh build (no cache)
  • Warm = With cached build artifacts

Build Metrics:

  • Fresh = Clean build (no .next directory)
  • Cached = With existing .next directory

Change Thresholds:

  • Time: Changes < 50ms AND < 10%, OR < 2% are insignificant
  • Size: Changes < 1KB AND < 1% are insignificant
  • All other changes are flagged to catch regressions

⚡ Dev Server

Metric Canary PR Change Trend
Cold (Listen) 811ms 811ms ██▁██
Cold (Ready in log) 775ms 774ms ▇▆▁▇▇
Cold (First Request) 1.168s 1.167s ▄▂▁▅▅
Warm (Listen) 811ms 811ms ██▁██
Warm (Ready in log) 776ms 776ms ▇▆▁██
Warm (First Request) 577ms 582ms ▇▃▁█▇
📦 Dev Server (Webpack) (Legacy)

📦 Dev Server (Webpack)

Metric Canary PR Change Trend
Cold (Listen) 810ms 810ms █████
Cold (Ready in log) 784ms 785ms ██▆▇▆
Cold (First Request) 3.288s 3.300s ▅█▃▃▃
Warm (Listen) 810ms 810ms █████
Warm (Ready in log) 784ms 785ms ██▆▇▆
Warm (First Request) 3.286s 3.320s ▅█▄▂▃

⚡ Production Builds

Metric Canary PR Change Trend
Fresh Build 5.035s 5.060s ▅▃▁▅▅
Cached Build 5.054s 5.091s ▇▄▁▆▇
📦 Production Builds (Webpack) (Legacy)

📦 Production Builds (Webpack)

Metric Canary PR Change Trend
Fresh Build 23.911s 23.823s ▄▅▁▁▃
Cached Build 24.052s 24.071s ▄▂▁▁▁
node_modules Size 508 MB 508 MB █████
📦 Bundle Sizes

Bundle Sizes

⚡ Turbopack

Client

Main Bundles
Canary PR Change
04hm05ar7kldw.js gzip 5.73 kB N/A -
08eoeyxjgkcg-.js gzip 71 kB N/A -
08wo8crzyq6vd.js gzip 159 B N/A -
0cz1d0mv5g_q7.js gzip 39.4 kB 39.4 kB
0dvitrl5zg37g.js gzip 8.82 kB N/A -
0sf7ysou-72zd.js gzip 8.71 kB N/A -
10-wxe5rnc8w4.js gzip 156 B N/A -
157abun3hwc_s.js gzip 10.3 kB N/A -
1elt1qium-r2m.css gzip 115 B 115 B
1fir9z67zbzhj.js gzip 156 B N/A -
1jpaub6y8xlfr.js gzip 2.3 kB N/A -
1ot0mvscrc_uf.js gzip 233 B N/A -
1pwdlnd2apq1-.js gzip 169 B N/A -
1vd-btw8knlq8.js gzip 156 B N/A -
2_m3xv2uq3sjc.js gzip 1.46 kB N/A -
20s37uade9if-.js gzip 154 B N/A -
21gk5ltqgd84s.js gzip 65.6 kB N/A -
24y34mwgrkqp4.js gzip 8.78 kB N/A -
28ty09pswlre7.js gzip 155 B N/A -
2ajcnrb7u4z-2.js gzip 156 B N/A -
2c-fd4y1zozz8.js gzip 8.79 kB N/A -
2d7416h_xd36x.js gzip 8.71 kB N/A -
2lyuhit6rn8fy.js gzip 9.44 kB N/A -
2q0gr8wfr3jwl.js gzip 8.77 kB N/A -
2rr4cc2-omm4l.js gzip 151 B N/A -
2t9e75oz6r0zp.js gzip 8.76 kB N/A -
2tu4ozix-297g.js gzip 7.61 kB N/A -
2uku_olcn15b7.js gzip 8.79 kB N/A -
2wdfehtb6jwpt.js gzip 159 B N/A -
2x075h1zduvno.js gzip 156 B N/A -
30r8mm-46bdqy.js gzip 220 B 220 B
340x4rsgf3u0g.js gzip 13.8 kB N/A -
3a3ds3ahws_02.js gzip 155 B N/A -
3c1jdxkzlb8oq.js gzip 12.9 kB N/A -
3inab2jybr4k9.js gzip 450 B N/A -
3jkm5tdjvaf_q.js gzip 13.1 kB N/A -
3lkaoispiaba2.js gzip 154 B N/A -
3msl-qf82evq0.js gzip 50.5 kB N/A -
3mt67agm5wp40.js gzip 10.6 kB N/A -
3saabek4kohwi.js gzip 10 kB N/A -
4189xmby9yu1p.js gzip 13.6 kB N/A -
turbopack-03..xgxj.js gzip 4.23 kB N/A -
turbopack-0l..b7dv.js gzip 4.23 kB N/A -
turbopack-0m..9wuj.js gzip 4.23 kB N/A -
turbopack-0n..o209.js gzip 4.24 kB N/A -
turbopack-1w..493-.js gzip 4.23 kB N/A -
turbopack-24..ebz0.js gzip 4.23 kB N/A -
turbopack-25..qcev.js gzip 4.23 kB N/A -
turbopack-2d..x4fs.js gzip 4.23 kB N/A -
turbopack-2m..w4cs.js gzip 4.23 kB N/A -
turbopack-2o..0_jd.js gzip 4.23 kB N/A -
turbopack-2t..kx-7.js gzip 4.23 kB N/A -
turbopack-2v..f6e0.js gzip 4.23 kB N/A -
turbopack-2z..du16.js gzip 4.21 kB N/A -
turbopack-41..kszv.js gzip 4.23 kB N/A -
0_i7nqgx23st7.js gzip N/A 10 kB -
0_ue9iqdenfn5.js gzip N/A 169 B -
06puhytyxk31p.js gzip N/A 8.82 kB -
07xlq-b31idw9.js gzip N/A 50.5 kB -
0a_4hmdwd-3ry.js gzip N/A 153 B -
0bu428s3duhqp.js gzip N/A 7.61 kB -
0j42f9zonj0wd.js gzip N/A 13 kB -
0jpt_hm78rtws.js gzip N/A 155 B -
0m34gln_kt4fg.js gzip N/A 5.73 kB -
0n2gf9b36q_in.js gzip N/A 152 B -
144ni8t6ydmli.js gzip N/A 156 B -
19b4xchn6cihb.js gzip N/A 157 B -
1g3q1ww01thnl.js gzip N/A 2.3 kB -
1hraqxuiymq6v.js gzip N/A 8.79 kB -
1l9un1sl77287.js gzip N/A 1.46 kB -
1r7l_zy2yrasy.js gzip N/A 154 B -
2147zgtf14z-q.js gzip N/A 234 B -
23bz3xsg-5-1s.js gzip N/A 8.71 kB -
24wnwxg8azxgr.js gzip N/A 156 B -
27441mytv7pbm.js gzip N/A 9.43 kB -
2cjkwjgm1zcfs.js gzip N/A 8.71 kB -
2hl4t97p3y84-.js gzip N/A 156 B -
2mwnolbec-x0c.js gzip N/A 71 kB -
2scd8zaoyb8md.js gzip N/A 8.79 kB -
2st_qs6p_9us0.js gzip N/A 13.1 kB -
2yq3gaznuimnq.js gzip N/A 156 B -
2zdlhgg2czq_1.js gzip N/A 65.6 kB -
2zo2exm1d8qj1.js gzip N/A 13.6 kB -
30oszfu8bgqbc.js gzip N/A 13.9 kB -
33414okov1b1o.js gzip N/A 162 B -
34-oyj6z4526_.js gzip N/A 160 B -
3hn75zuxly9az.js gzip N/A 10.3 kB -
3hqh7m128tvsn.js gzip N/A 8.77 kB -
3hqti_t-zy1x4.js gzip N/A 449 B -
3mnawenie1flm.js gzip N/A 8.76 kB -
3ubsozlu6zs38.js gzip N/A 10.6 kB -
43iwfqjnx1cy_.js gzip N/A 8.78 kB -
44juzboy2rnti.js gzip N/A 156 B -
turbopack-0b..hy8d.js gzip N/A 4.22 kB -
turbopack-0o..qwq4.js gzip N/A 4.23 kB -
turbopack-0r..9y5r.js gzip N/A 4.24 kB -
turbopack-1i..cyhh.js gzip N/A 4.23 kB -
turbopack-1l..wovj.js gzip N/A 4.23 kB -
turbopack-1m..ozz4.js gzip N/A 4.23 kB -
turbopack-2-..vqc6.js gzip N/A 4.23 kB -
turbopack-20.._dps.js gzip N/A 4.21 kB -
turbopack-25..a8eb.js gzip N/A 4.23 kB -
turbopack-2e..4jps.js gzip N/A 4.23 kB -
turbopack-2l..r-5s.js gzip N/A 4.23 kB -
turbopack-2r..ps2o.js gzip N/A 4.23 kB -
turbopack-2y..3akc.js gzip N/A 4.23 kB -
turbopack-45..yr-0.js gzip N/A 4.22 kB -
Total 470 kB 470 kB ⚠️ +84 B

Server

Middleware
Canary PR Change
middleware-b..fest.js gzip 723 B 728 B
Total 723 B 728 B ⚠️ +5 B
Build Details
Build Manifests
Canary PR Change
_buildManifest.js gzip 433 B 433 B
Total 433 B 433 B

📦 Webpack

Client

Main Bundles
Canary PR Change
2258-HASH.js gzip 61.7 kB N/A -
2266-HASH.js gzip 4.69 kB N/A -
3317.HASH.js gzip 169 B N/A -
4866-HASH.js gzip 5.64 kB N/A -
9e302639-HASH.js gzip 62.8 kB N/A -
framework-HASH.js gzip 59.5 kB 59.5 kB
main-app-HASH.js gzip 256 B 254 B
main-HASH.js gzip 39.9 kB 39.9 kB
webpack-HASH.js gzip 1.68 kB 1.68 kB
175fd0fd-HASH.js gzip N/A 62.8 kB -
2596-HASH.js gzip N/A 5.63 kB -
34-HASH.js gzip N/A 61.7 kB -
5691.HASH.js gzip N/A 169 B -
9156-HASH.js gzip N/A 4.68 kB -
Total 236 kB 236 kB ✅ -15 B
Polyfills
Canary PR Change
polyfills-HASH.js gzip 39.4 kB 39.4 kB
Total 39.4 kB 39.4 kB
Pages
Canary PR Change
_app-HASH.js gzip 193 B 193 B
_error-HASH.js gzip 181 B 182 B
css-HASH.js gzip 334 B 332 B
dynamic-HASH.js gzip 1.79 kB 1.81 kB
edge-ssr-HASH.js gzip 255 B 255 B
head-HASH.js gzip 351 B 348 B
hooks-HASH.js gzip 385 B 384 B
image-HASH.js gzip 580 B 580 B
index-HASH.js gzip 257 B 259 B
link-HASH.js gzip 2.51 kB 2.52 kB
routerDirect..HASH.js gzip 318 B 319 B
script-HASH.js gzip 387 B 386 B
withRouter-HASH.js gzip 316 B 316 B
1afbb74e6ecf..834.css gzip 106 B 106 B
Total 7.97 kB 7.99 kB ⚠️ +19 B

Server

Edge SSR
Canary PR Change
edge-ssr.js gzip 126 kB 126 kB
page.js gzip 277 kB 272 kB 🟢 5.28 kB (-2%)
Total 404 kB 398 kB ✅ -5.5 kB
Middleware
Canary PR Change
middleware-b..fest.js gzip 618 B 615 B
middleware-r..fest.js gzip 155 B 155 B
middleware.js gzip 44.3 kB 44.8 kB
edge-runtime..pack.js gzip 842 B 842 B
Total 46 kB 46.4 kB ⚠️ +436 B
Build Details
Build Manifests
Canary PR Change
_buildManifest.js gzip 719 B 717 B
Total 719 B 717 B ✅ -2 B
Build Cache
Canary PR Change
0.pack gzip 4.5 MB 4.5 MB
index.pack gzip 115 kB 115 kB
index.pack.old gzip 115 kB 113 kB 🟢 1.78 kB (-2%)
Total 4.73 MB 4.73 MB ✅ -6.62 kB

🔄 Shared (bundler-independent)

Runtimes
Canary PR Change
app-page-exp...dev.js gzip 353 kB 353 kB
app-page-exp..prod.js gzip 196 kB 196 kB
app-page-tur...dev.js gzip 353 kB 353 kB
app-page-tur..prod.js gzip 196 kB 196 kB
app-page-tur...dev.js gzip 349 kB 349 kB
app-page-tur..prod.js gzip 194 kB 194 kB
app-page.run...dev.js gzip 350 kB 350 kB
app-page.run..prod.js gzip 194 kB 194 kB
app-route-ex...dev.js gzip 77.5 kB 77.5 kB
app-route-ex..prod.js gzip 52.9 kB 52.9 kB
app-route-tu...dev.js gzip 77.6 kB 77.6 kB
app-route-tu..prod.js gzip 52.9 kB 52.9 kB
app-route-tu...dev.js gzip 77.2 kB 77.2 kB
app-route-tu..prod.js gzip 52.7 kB 52.7 kB
app-route.ru...dev.js gzip 77.1 kB 77.1 kB
app-route.ru..prod.js gzip 52.7 kB 52.7 kB
dist_client_...dev.js gzip 324 B 324 B
dist_client_...dev.js gzip 326 B 326 B
dist_client_...dev.js gzip 318 B 318 B
dist_client_...dev.js gzip 317 B 317 B
pages-api-tu...dev.js gzip 44.3 kB 44.3 kB
pages-api-tu..prod.js gzip 33.8 kB 33.8 kB
pages-api.ru...dev.js gzip 44.3 kB 44.3 kB
pages-api.ru..prod.js gzip 33.7 kB 33.7 kB
pages-turbo....dev.js gzip 53.7 kB 53.7 kB
pages-turbo...prod.js gzip 39.4 kB 39.4 kB
pages.runtim...dev.js gzip 53.6 kB 53.6 kB
pages.runtim..prod.js gzip 39.4 kB 39.4 kB
server.runti..prod.js gzip 63.2 kB 63.2 kB
use-cache-pr...dev.js gzip 70 kB 70 kB
use-cache-pr...dev.js gzip 70 kB 70 kB
use-cache-pr...dev.js gzip 68.4 kB 68.4 kB
use-cache-pr...dev.js gzip 68.3 kB 68.3 kB
Total 3.39 MB 3.39 MB ⚠️ +448 B
📝 Changed Files (8 files)

Files with changes:

  • app-page-exp..ntime.dev.js
  • app-page-exp..time.prod.js
  • app-page-tur..ntime.dev.js
  • app-page-tur..time.prod.js
  • app-page-tur..ntime.dev.js
  • app-page-tur..time.prod.js
  • app-page.runtime.dev.js
  • app-page.runtime.prod.js
View diffs
app-page-exp..ntime.dev.js
failed to diff
app-page-exp..time.prod.js
failed to diff
app-page-tur..ntime.dev.js
failed to diff
app-page-tur..time.prod.js
failed to diff
app-page-tur..ntime.dev.js
failed to diff
app-page-tur..time.prod.js
failed to diff
app-page.runtime.dev.js
failed to diff
app-page.runtime.prod.js
failed to diff
📎 Tarball URL
https://vercel-packages.vercel.app/next/commits/2eeac8ecb03a942d6af2570f6d677d5cff9eb73c/next

Commit: 2eeac8e

@lubieowoce lubieowoce force-pushed the lubieowoce/stage-controller-generic branch 2 times, most recently from d515588 to bc5f88c Compare June 2, 2026 14:32
@lubieowoce lubieowoce marked this pull request as ready for review June 2, 2026 14:33
@lubieowoce lubieowoce requested review from gnoff and unstubbable June 2, 2026 14:33
Copy link
Copy Markdown
Member Author

lubieowoce commented Jun 2, 2026

@lubieowoce lubieowoce force-pushed the lubieowoce/stage-controller-generic branch from bc5f88c to a302387 Compare June 2, 2026 16:42
This was referenced Jun 2, 2026
@lubieowoce lubieowoce force-pushed the lubieowoce/stage-controller-generic branch from a302387 to 0f5dc1f Compare June 2, 2026 17:05
@lubieowoce lubieowoce force-pushed the lubieowoce/stage-controller-generic branch from 0f5dc1f to 2eeac8e Compare June 2, 2026 18:04
const endStage = getNextStage(RenderStage.Static)
stageController.onStage(endStage, () => {
reader.cancel()
})
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.

Is this pattern common? Should we model this as afterStage and let you pass in Static and it fires after you transition past this stage as opposed to on entering the next stage?

Copy link
Copy Markdown
Member Author

@lubieowoce lubieowoce Jun 3, 2026

Choose a reason for hiding this comment

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

i haven't seen it elsewhere (other than the node/web forks). but also importantly there's an implicit assumption here that we'll reach a stage after the target stage at all. here we know we will, because it's working off of a full staged render, but in other codepaths, if we're doing a prerender, we might not reach the target stage at all and would need to deal with that somehow. we'd probably need to to call something at the end of the task where we do this stage. anyway i wanted to avoid making API design decisions on that, so i didn't make this generic

@lubieowoce lubieowoce merged commit 2ac8c67 into canary Jun 3, 2026
296 of 299 checks passed
@lubieowoce lubieowoce deleted the lubieowoce/stage-controller-generic branch June 3, 2026 11:55
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants