[pull] canary from vercel:canary#1059
Merged
Merged
Conversation
### What?
Splits the previously unified `PrehashedString` (which held a `Payload`
enum of `String | &'static str`) into two separate types:
`StaticPrehashedString { value: &'static str, hash: u64 }` for atoms
produced by `rcstr!` / `make_const_prehashed_string`, and
`DynamicPrehashedString { value: Box<str>, hash: u64 }` for atoms held
in an `Arc`. The static and dynamic paths were already distinguished by
the `STATIC_TAG` / `DYNAMIC_TAG` bits in `RcStr`, so the runtime branch
on enum discriminant was redundant.
### Why?
- **16 bytes saved per heap-allocated `RcStr` value .** Dynamic atoms
drop the `String::capacity` field (which was always equal to `len` since
the contents are immutable) and because they are stored in a
`triomphe::Arc` this drops the Arc payload to 32 bytes instead of 40
which matches a mimalloc bucket (previously we were rounded up to a 48
byte bucket) so we save 16 bytes.
- **8 bytes saved per static `RcStr` value.** The linker will optimally
align our 24 byte struct.
- Removes a layer of dispatch on the hot path (`as_str`, `==`, `Hash`) —
typed deref to the correct variant instead of matching on `Payload`.
(i.o.w. one 'descreminent' traversal instead of 2)
### How?
- `dynamic.rs`: `Payload` enum removed; two structs replace
`PrehashedString`. `deref_from` split into `deref_static` and
`deref_dynamic`. `restore_arc` returns `Arc<DynamicPrehashedString>`.
- `lib.rs`: `as_str` and `into_owned` dispatch on `tag()` (STATIC vs
DYNAMIC vs INLINE) rather than `location()`. New `heap_hash_and_str`
helper for `PartialEq` and `Hash` to share the static/dynamic branch.
`into_owned`'s `try_unwrap` arm uses `String::from(Box<str>)` which
reuses the box allocation (still O(1)).
- `turbo-rcstr-macros`: emit `::turbo_rcstr::StaticPrehashedString`
instead of `::turbo_rcstr::PrehashedString`.
- Added a comment on `DynamicPrehashedString` noting the future move to
`triomphe::ThinArc` to fold the two heap allocations (Arc header + boxed
bytes) into one. Deferred because that change would make
`RcStr::from(String)` copy the bytes, invalidating the documented cheap
`String -> RcStr -> String` round-trip — wants a separate evaluation.
<!-- NEXT_JS_LLM_PR -->
### What?
Adds a `compile_route` MCP tool that triggers on-demand compilation of a
specific route (app or pages) without issuing an HTTP request, and
returns any compilation issues.
### Why?
Coding agents and benchmarking workflows need a way to warm the module
graph or measure compile time for a route without standing up a live
backend to satisfy the request. The existing path — hitting the URL —
requires the route's runtime dependencies to be available and couples
compile timing to request handling.
### How?
- New tool `mcp/compile_route` registered in
`get-or-create-mcp-server.ts`, backed by a `compileRoute({ page,
clientOnly })` callback plumbed from the Turbopack hot reloader
- Reuses the dev server's existing on-demand entry path (`ensurePage` /
`handleRouteType`), so the call path matches a first navigation.
- Adds a `subscribeToChanges` opt-out on `ensurePage` and threads it
through `handleRouteType` / `handlePagesErrorRoute`. One-shot MCP
compilations skip HMR subscription wiring — without this, each call
would leak a subscription that fires on every subsequent file change for
the life of the dev server.
- Telemetry: registers `mcp/compile_route` in the `McpToolName` union.
- e2e test in
`test/development/mcp-server/mcp-server-compile-route.test.ts`.
<!-- NEXT_JS_LLM_PR -->
…93338) Expose memory data to the MCP tool for the turbo trace server
(#93807) ## What Stop pinning compiled chunk source on `EcmascriptBuildNodeChunkVersion`. The struct previously held a `Vec<ReadRef<CodeAndIds>>` for every module in the chunk, even though the HMR update path only needs the bytes for the *changed* subset and the bytes are already on disk. That field was also forcing the upstream `CodeAndIds` / `BatchGroupCodeAndIds` tasks to stay `serialization = "skip"`, so warm restarts had to re-walk every module and re-hash its source to rebuild `entries_hashes`. This change mirrors the browser-side pattern: a new `EcmascriptBuildNodeChunkContentEntries` task lives on the chunk content and holds `ResolvedVc<Code>` + `ResolvedVc<u64>` per module. The version struct shrinks to `{ chunk_path, minify_type, entries_hashes }`, drops `serialization = "skip"`, and switches `chunk_path` from `String` to `RcStr`. `update_ecmascript_node_chunk_content` now resolves entries lazily, only when an added or modified module actually needs its code shipped. ## Why Two wins, both for dev sessions starting against a warm filesystem cache: - **Memory.** The version no longer transitively pins every module's compiled `Rope` in heap — those bytes can stay on disk until HMR actually needs them. - **Warm-restart CPU.** `entries_hashes` is sourced from the per-module `Code::source_code_hash()` task (already cached) and the version itself now round-trips through the persistent cache, so we don't re-hash anything on warm start. The HMR payload shape is unchanged. ## Perf This should speed up warm builds a bit but the major benefit is not recomputing node outputs and keeping them in ram measuring v0 after loading the main route Branch: Cold: 12.3G Warm: 7.34G Canary: Cold 12.3G Warm: 8.5G The trace file confirms the recomputations are gone and the heap measurements confirm we trimming ~1.1g of ram Using the devlow benchmarks i was able to confirm a possible small progression ``` # canary chat dev startup build=warm: root page = 23.96 s (from root page/start) chat dev startup build=warm: root page = 21.21 s (from root page/start) chat dev startup build=warm: root page = 22.70 s (from root page/start) # branch chat dev startup build=warm: root page = 20.94 s (from root page/start) chat dev startup build=warm: root page = 19.43 s (from root page/start) chat dev startup build=warm: root page = 23.49 s (from root page/start) ``` ## Tests I added a new integration test to ensure we don't accidentally regress here. Which confirms that 'clean warm builds' run nothing and 'clean warm dev sessions' just set up HMR session infra <!-- NEXT_JS_LLM_PR -->
Record simple counters for how many items were persisted. This should be cheap enough to always do
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to subscribe to this conversation on GitHub.
Already have an account?
Sign in.
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
See Commits and Changes for more details.
Created by
pull[bot] (v2.0.0-alpha.4)
Can you help keep this open source service alive? 💖 Please sponsor : )