Skip to content

[turbopack] Reduce persistence buffer lifetimes#92413

Open
lukesandberg wants to merge 2 commits into04-06-add_family_name_to_many_spansfrom
04-06-streaming_reads
Open

[turbopack] Reduce persistence buffer lifetimes#92413
lukesandberg wants to merge 2 commits into04-06-add_family_name_to_many_spansfrom
04-06-streaming_reads

Conversation

@lukesandberg
Copy link
Copy Markdown
Contributor

@lukesandberg lukesandberg commented Apr 6, 2026

What?

Refactor the task restore path to decode persisted bytes directly into the live in-memory TaskStorage, rather than first decoding into a scratch TaskStorage and then copying the fields across.

Also introduces batch_get_with — a streaming callback-based batch lookup that resolves and delivers each value immediately rather than accumulating all decoded values simultaneously.

Why?

The old path allocated a scratch TaskStorage per task, decoded into it, then called restore_from to copy persisted fields into the live task. For large batches (up to 50K tasks) this meant holding every decoded value in memory at once.

The new path:

  • Decodes directly into the live task, eliminating the scratch TaskStorage allocation per task
  • Drops each decoded block before moving to the next, so peak memory is O(1) value blocks instead of O(n)
  • Removes the generated restore_from / restore_meta_from / restore_data_from family of methods from the TaskStorage macro, which were the only consumers of this pattern

Also switches batch_get from returning Vec<Option<ArcBytes>> to a streaming batch_get_with(callback) interface, and replaces the ValueBuffer<'l> associated type on KeyValueDatabase with a concrete ArcBytes return type.

How?

  • turbo-persistence: add batch_get_with on TurboPersistence and KeyValueDatabase; batch_lookup_with on MetaFile now takes a FoundBitset and fires a callback per resolved entry rather than storing LookupValue in the cells vec
  • BackingStorage: lookup_data returns Option<ArcBytes> (raw bytes); batch_lookup_data takes a &mut dyn FnMut(usize, Option<&[u8]>) callback
  • kv_backing_storage: decode removed from the lookup path; callers decode directly using new_turbo_bincode_decoder
  • operation/mod.rs: new restore_batch helper handles single and batch cases uniformly; process_task closure decodes into live storage and fires prepared_task_callback inline without a second pass
  • task_storage_macro.rs: remove restore_from and related generated methods

Copy link
Copy Markdown
Contributor Author

lukesandberg commented Apr 6, 2026

Warning

This pull request is not mergeable via GitHub because a downstack PR is open. Once all requirements are satisfied, merge this PR as a stack on Graphite.
Learn more

This stack of pull requests is managed by Graphite. Learn more about stacking.

@codspeed-hq
Copy link
Copy Markdown

codspeed-hq bot commented Apr 6, 2026

Merging this PR will not alter performance

✅ 17 untouched benchmarks
⏩ 3 skipped benchmarks1


Comparing 04-06-streaming_reads (c275d99) with 04-06-add_family_name_to_many_spans (9c3aaf1)

Open in CodSpeed

Footnotes

  1. 3 benchmarks were skipped, so the baseline results were used instead. If they were deleted from the codebase, click here and archive them to remove them from the performance reports.

@lukesandberg lukesandberg changed the base branch from tracing_improvements to graphite-base/92413 April 6, 2026 17:57
@lukesandberg lukesandberg force-pushed the graphite-base/92413 branch from f6297db to 9c3aaf1 Compare April 6, 2026 17:57
@lukesandberg lukesandberg force-pushed the 04-06-streaming_reads branch from 2bc6c08 to 76294c3 Compare April 6, 2026 17:57
@lukesandberg lukesandberg changed the base branch from graphite-base/92413 to 04-06-add_family_name_to_many_spans April 6, 2026 17:58
@nextjs-bot
Copy link
Copy Markdown
Collaborator

nextjs-bot commented Apr 6, 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) 455ms 455ms ▅▅▅▁█
Cold (Ready in log) 443ms 443ms ▇█▂▁▂
Cold (First Request) 1.128s 1.088s ▆██▆▁
Warm (Listen) 457ms 457ms ▁▁██▁
Warm (Ready in log) 444ms 443ms ▇█▁▂▂
Warm (First Request) 339ms 343ms ▅▇▁▃▅
📦 Dev Server (Webpack) (Legacy)

📦 Dev Server (Webpack)

Metric Canary PR Change Trend
Cold (Listen) 456ms 455ms ▆▃▆▃▃
Cold (Ready in log) 433ms 433ms ▆▃▂▁▃
Cold (First Request) 1.929s 1.913s █▄▆▇▇
Warm (Listen) 456ms 455ms ▁▁▁▁▁
Warm (Ready in log) 434ms 434ms ▆▃▃▁▂
Warm (First Request) 1.927s 1.944s ▇▃▆▆▇

⚡ Production Builds

Metric Canary PR Change Trend
Fresh Build 3.854s 3.948s ▅█▅▇▆
Cached Build 3.930s 3.841s ▄▅█▅█
📦 Production Builds (Webpack) (Legacy)

📦 Production Builds (Webpack)

Metric Canary PR Change Trend
Fresh Build 14.370s 14.402s ▇▁▁▁▂
Cached Build 14.441s 14.500s █▃▂▁▂
node_modules Size 488 MB 488 MB ▅████
📦 Bundle Sizes

Bundle Sizes

⚡ Turbopack

Client

Main Bundles
Canary PR Change
02fkg8wfh0iju.js gzip 9.19 kB N/A -
050zwt5xh_0tx.js gzip 10.4 kB N/A -
06rvbj82bhyo0.js gzip 13 kB N/A -
087fzjd-gvlzv.js gzip 450 B N/A -
0cz1d0mv5g_q7.js gzip 39.4 kB 39.4 kB
0j9nyyebpzuve.js gzip 65.7 kB N/A -
0liwiknewtyj-.js gzip 155 B N/A -
0lz6rvqk4dq28.js gzip 157 B N/A -
0ppxcl_z43mad.js gzip 8.52 kB N/A -
0t-vypuopaopr.js gzip 156 B N/A -
0taq90fnaps1c.js gzip 154 B N/A -
0w-q2j65cf4d_.js gzip 70.8 kB N/A -
177wlvvlyngbh.js gzip 162 B N/A -
19oha6-znmkcv.js gzip 8.55 kB N/A -
1elt1qium-r2m.css gzip 115 B 115 B
1jiaxunj6aqjm.js gzip 154 B N/A -
1lwlgx9s5r-bl.js gzip 155 B N/A -
2_5rjb7lqxntf.js gzip 221 B 221 B
219prxwxgaalc.js gzip 7.61 kB N/A -
26elcgxnn9zjd.js gzip 8.52 kB N/A -
2900hudr6gvm0.js gzip 2.28 kB N/A -
2lv2js3kmdeho.js gzip 8.48 kB N/A -
2pcakafkxmy0y.js gzip 159 B N/A -
2rehygrd36hqv.js gzip 8.58 kB N/A -
2srwswih0m9_h.js gzip 13.3 kB N/A -
3-p9p9mheqhzx.js gzip 8.55 kB N/A -
31030bryqpolg.js gzip 8.53 kB N/A -
31dx5nmrzzuy7.js gzip 225 B N/A -
35jcqqltah41n.js gzip 161 B N/A -
3925v09gtu-5k.js gzip 49 kB N/A -
39x4zj5mjb4d_.js gzip 9.77 kB N/A -
3em05vpajqpwz.js gzip 169 B N/A -
3k-48b78ys_vy.js gzip 10.1 kB N/A -
3m7-5rfj0avoz.js gzip 12.9 kB N/A -
3s-7xts2e4w68.js gzip 158 B N/A -
3uqce_6sa526g.js gzip 8.47 kB N/A -
3yurjqk-sjs3y.js gzip 1.46 kB N/A -
40ybjx9c192n0.js gzip 13.8 kB N/A -
421vzwdt9j1b_.js gzip 5.62 kB N/A -
450cprx9a1he-.js gzip 157 B N/A -
458kx8xybb_ty.js gzip 157 B N/A -
turbopack-0g..vaan.js gzip 4.18 kB N/A -
turbopack-0x..zr41.js gzip 4.18 kB N/A -
turbopack-1d..d4y6.js gzip 4.18 kB N/A -
turbopack-1j..5f0x.js gzip 4.18 kB N/A -
turbopack-1q..mobr.js gzip 4.18 kB N/A -
turbopack-1x..zepa.js gzip 4.18 kB N/A -
turbopack-20..hso6.js gzip 4.17 kB N/A -
turbopack-24..svkv.js gzip 4.17 kB N/A -
turbopack-2i..2mpu.js gzip 4.18 kB N/A -
turbopack-2m..v5gs.js gzip 4.18 kB N/A -
turbopack-2t..mebg.js gzip 4.16 kB N/A -
turbopack-39..hd24.js gzip 4.19 kB N/A -
turbopack-3m..e-_l.js gzip 4.17 kB N/A -
turbopack-42..g8c-.js gzip 4.17 kB N/A -
03dgzoo-qf3sm.js gzip N/A 9.19 kB -
05tx5f25dlivn.js gzip N/A 8.53 kB -
085gix7vncldb.js gzip N/A 154 B -
08qxf1bxk2sy0.js gzip N/A 153 B -
0c7ez6p2qc57f.js gzip N/A 5.62 kB -
0duvj3qk5pvgn.js gzip N/A 13.8 kB -
0hvsdb3udqtqq.js gzip N/A 156 B -
0k12a08xkwlz-.js gzip N/A 157 B -
0m-34rm9w_wpm.js gzip N/A 7.6 kB -
0nlykkpfyyg2w.js gzip N/A 155 B -
0qnwuk92m8i7o.js gzip N/A 10.4 kB -
0r4wrn6n0ue2m.js gzip N/A 8.55 kB -
0rp0fodtbt_6m.js gzip N/A 8.52 kB -
0sfck-km4dl1k.js gzip N/A 8.47 kB -
0x0xuhmxzwkp8.js gzip N/A 8.47 kB -
1_t7k492adi7n.js gzip N/A 155 B -
1_xgyhyp7ql91.js gzip N/A 157 B -
1-wdvgxnzicj7.js gzip N/A 1.46 kB -
11u6nxujb2eg4.js gzip N/A 450 B -
15w1ub8e8wqfw.js gzip N/A 159 B -
1aeiaf859ymle.js gzip N/A 154 B -
1jv-o1_s-zmua.js gzip N/A 49 kB -
2in9gz64_b29q.js gzip N/A 168 B -
2k9ax08cjl2id.js gzip N/A 12.9 kB -
2lms6k76q5-6m.js gzip N/A 13.3 kB -
2qx4twi9i3xus.js gzip N/A 2.28 kB -
2srnqic6tvxxd.js gzip N/A 8.52 kB -
30beetijii86g.js gzip N/A 65.7 kB -
30l7m4nayp73a.js gzip N/A 8.55 kB -
33hhl7rvbciep.js gzip N/A 159 B -
38rr7d3kfutni.js gzip N/A 13 kB -
3b-ri3-nxw0g7.js gzip N/A 70.8 kB -
3c73-g0fvlevb.js gzip N/A 154 B -
3h_ecpiaatwgc.js gzip N/A 10.1 kB -
3ity0aahajapd.js gzip N/A 225 B -
3tn3pab2yum0u.js gzip N/A 155 B -
3wrhpuc-j1aw9.js gzip N/A 9.77 kB -
43mlw9dy_8f02.js gzip N/A 8.58 kB -
turbopack-08..n7x8.js gzip N/A 4.18 kB -
turbopack-0a..t8jc.js gzip N/A 4.18 kB -
turbopack-0a..nf4n.js gzip N/A 4.17 kB -
turbopack-0r..v9zz.js gzip N/A 4.18 kB -
turbopack-0z..a5r2.js gzip N/A 4.16 kB -
turbopack-1-..bedd.js gzip N/A 4.17 kB -
turbopack-1c..qjua.js gzip N/A 4.17 kB -
turbopack-1p..1_tg.js gzip N/A 4.17 kB -
turbopack-1q..-f4x.js gzip N/A 4.17 kB -
turbopack-2u..6dws.js gzip N/A 4.17 kB -
turbopack-2u..dzlg.js gzip N/A 4.18 kB -
turbopack-2y..u7ck.js gzip N/A 4.17 kB -
turbopack-30..s215.js gzip N/A 4.18 kB -
turbopack-44..868h.js gzip N/A 4.19 kB -
Total 464 kB 464 kB ✅ -60 B

Server

Middleware
Canary PR Change
middleware-b..fest.js gzip 719 B 714 B
Total 719 B 714 B ✅ -5 B
Build Details
Build Manifests
Canary PR Change
_buildManifest.js gzip 432 B 427 B 🟢 5 B (-1%)
Total 432 B 427 B ✅ -5 B

📦 Webpack

Client

Main Bundles
Canary PR Change
5528-HASH.js gzip 5.54 kB N/A -
6280-HASH.js gzip 60.7 kB N/A -
6335.HASH.js gzip 169 B N/A -
912-HASH.js gzip 4.59 kB N/A -
e8aec2e4-HASH.js gzip 62.8 kB N/A -
framework-HASH.js gzip 59.7 kB 59.7 kB
main-app-HASH.js gzip 255 B 254 B
main-HASH.js gzip 39.3 kB 39.2 kB
webpack-HASH.js gzip 1.68 kB 1.68 kB
262-HASH.js gzip N/A 4.59 kB -
2889.HASH.js gzip N/A 169 B -
5602-HASH.js gzip N/A 5.55 kB -
6948ada0-HASH.js gzip N/A 62.8 kB -
9544-HASH.js gzip N/A 61.4 kB -
Total 235 kB 235 kB ⚠️ +650 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 194 B 194 B
_error-HASH.js gzip 183 B 180 B 🟢 3 B (-2%)
css-HASH.js gzip 331 B 330 B
dynamic-HASH.js gzip 1.81 kB 1.81 kB
edge-ssr-HASH.js gzip 256 B 256 B
head-HASH.js gzip 351 B 352 B
hooks-HASH.js gzip 384 B 383 B
image-HASH.js gzip 580 B 581 B
index-HASH.js gzip 260 B 260 B
link-HASH.js gzip 2.51 kB 2.51 kB
routerDirect..HASH.js gzip 320 B 319 B
script-HASH.js gzip 386 B 386 B
withRouter-HASH.js gzip 315 B 315 B
1afbb74e6ecf..834.css gzip 106 B 106 B
Total 7.98 kB 7.98 kB ✅ -1 B

Server

Edge SSR
Canary PR Change
edge-ssr.js gzip 125 kB 126 kB
page.js gzip 273 kB 273 kB
Total 398 kB 398 kB ⚠️ +165 B
Middleware
Canary PR Change
middleware-b..fest.js gzip 618 B 616 B
middleware-r..fest.js gzip 156 B 155 B
middleware.js gzip 43.9 kB 44.1 kB
edge-runtime..pack.js gzip 842 B 842 B
Total 45.5 kB 45.8 kB ⚠️ +230 B
Build Details
Build Manifests
Canary PR Change
_buildManifest.js gzip 715 B 718 B
Total 715 B 718 B ⚠️ +3 B
Build Cache
Canary PR Change
0.pack gzip 4.38 MB 4.37 MB
index.pack gzip 114 kB 114 kB
index.pack.old gzip 115 kB 114 kB
Total 4.61 MB 4.6 MB ✅ -4.57 kB

🔄 Shared (bundler-independent)

Runtimes
Canary PR Change
app-page-exp...dev.js gzip 342 kB 342 kB
app-page-exp..prod.js gzip 189 kB 189 kB
app-page-tur...dev.js gzip 341 kB 341 kB
app-page-tur..prod.js gzip 189 kB 189 kB
app-page-tur...dev.js gzip 338 kB 338 kB
app-page-tur..prod.js gzip 187 kB 187 kB
app-page.run...dev.js gzip 338 kB 338 kB
app-page.run..prod.js gzip 187 kB 187 kB
app-route-ex...dev.js gzip 76.6 kB 76.6 kB
app-route-ex..prod.js gzip 52.2 kB 52.2 kB
app-route-tu...dev.js gzip 76.6 kB 76.6 kB
app-route-tu..prod.js gzip 52.2 kB 52.2 kB
app-route-tu...dev.js gzip 76.2 kB 76.2 kB
app-route-tu..prod.js gzip 52 kB 52 kB
app-route.ru...dev.js gzip 76.2 kB 76.2 kB
app-route.ru..prod.js gzip 52 kB 52 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 43.8 kB 43.8 kB
pages-api-tu..prod.js gzip 33.4 kB 33.4 kB
pages-api.ru...dev.js gzip 43.8 kB 43.8 kB
pages-api.ru..prod.js gzip 33.4 kB 33.4 kB
pages-turbo....dev.js gzip 53.2 kB 53.2 kB
pages-turbo...prod.js gzip 39 kB 39 kB
pages.runtim...dev.js gzip 53.2 kB 53.2 kB
pages.runtim..prod.js gzip 39 kB 39 kB
server.runti..prod.js gzip 62.8 kB 62.8 kB
Total 3.03 MB 3.03 MB ✅ -2 B
📎 Tarball URL
https://vercel-packages.vercel.app/next/commits/c275d99d5909c3ed4f724ae54d172be1e611563c/next

deserialize directly into the TaskStorage struct in the in memory cache

this means we need to hold the shard lock while doing it, but we save a bunch of temporary memory allocations from the snapshots
@lukesandberg lukesandberg force-pushed the 04-06-streaming_reads branch from 76294c3 to 632c22b Compare April 6, 2026 20:11
@lukesandberg lukesandberg changed the title streaming reads [turbopack] Reduce persistence buffer lifetimes Apr 6, 2026
@lukesandberg lukesandberg marked this pull request as ready for review April 6, 2026 20:23
@lukesandberg lukesandberg requested a review from sokra April 6, 2026 20:23
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

created-by: Turbopack team PRs by the Turbopack team. Turbopack Related to Turbopack with Next.js.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants