[pull] canary from vercel:canary#1007
Merged
pull[bot] merged 15 commits intocode:canaryfrom Apr 29, 2026
Merged
Conversation
…ations (#93200) ## What? Adds performance optimizations for the common case of single-item lost follower operations in the turbo-tasks-backend aggregation update system, along with improved tracing capabilities for debugging. ## Why? When profiling turbopack operations, we observed that many aggregation update jobs involve only a single upper losing a single follower. The previous implementation always used vectors and iteration even for these single-item cases, adding unnecessary allocation overhead. Additionally, the existing `trace_aggregation_update` feature was incomplete (compilation errors) and lacked visibility into task data and operation statistics. ## How? ### Single-item optimization - Added a dedicated `InnerOfUpperLostFollower` job variant and `inner_of_upper_lost_follower` function to handle the single upper + single follower case without vector allocation - Optimized the plural variants (`InnerOfUppersLostFollowers`, `InnerOfUppersLostFollower`, `InnerOfUpperLostFollowers`) to delegate to the singular function when they contain only one item ### Tracing improvements - Added `trace_aggregation_update_stats` feature flag that enables counters for all aggregation update job types (new_followers, lost_followers, balance_edge, optimize_task, etc.) recorded in trace spans - Fixed `trace_aggregation_update` feature compilation errors by using `task.get_task_description()` instead of `ctx.get_task_description(task_id)` - Gated the `TaskDataCategory` used in aggregation update `ctx.task(...)` calls behind a const that resolves to `Meta` by default and `All` when tracing is enabled, so traces can see full task data without affecting default performance <!-- NEXT_JS_LLM_PR --> --------- Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
### What?
Scope-hoisted execution of a module that re-exports its own namespace
(`export * as X from './self'`) returned the wrong namespace for named
imports of the module.
Given:
```js
// data.js
import * as Self from './data'
export function foo() { return 'foo' }
export function bar() { return 'bar' }
export function fooViaSelf() { return Self.foo() } // Self.foo undefined
export * as Data from './data'
```
Any binding imported from `./data` that relied on the module's own
namespace (e.g. `Self.foo`, `Data.foo`, `Data.Data.foo`) was `undefined`
at runtime. Without scope hoisting the same code worked correctly.
This PR fixes the bug and adds execution tests covering self-namespace
re-exports — with and without scope hoisting — including chained
re-exports (`Data.Data.foo`, `Data.Data.Data.bar`).
### Why?
For an access like `Self.Data` where `Self = import * as Self from
'./data'`
and `Data` is exposed through `export * as Data from './data'`, the
namespace-member-access optimization rewrites the reference to a named
import resolving to a synthesized rename module
(`./data <export * as Data>`). `ReferencedAsset::get_ident_inner` then
recurses through the rename's `EsmExport::ImportedNamespace("Data")` and
returns a `namespace_ident` derived from the inner module's chunk-item
id — but `EsmAssetReference::code_generation` independently took
`id = referenced_asset.chunk_item_id` and emitted
```js
var <inner-data-ident> = __turbopack_context__.i("<rename-id>");
```
so the variable named like `ns(data.js)` actually held
`ns(rename) = { Data: ns(data.js) }`. The non-optimized
`import * as Self` uses the same mangled name and sees the rename's
namespace, so `Self.foo()` evaluates to `undefined`.
### How?
Keep the variable name and the `.i(...)` argument consistent by moving
the "what to import" decision onto the ident itself:
- `ReferencedAssetIdent::Module` gains an `import_source: ImportSource`
field that describes what to import to populate the namespace variable.
- `ImportSource` is an enum:
- `Module { asset }` — carries a reference to the final module in any
re-export chain, from which the chunk-item id is lazily computed.
- `External { request, ty }` — carries everything needed to emit
`__turbopack_external_import` / `__turbopack_external_require`.
- The `namespace_ident` is cached in `ReferencedAssetIdent::Module` at
resolution time (computed via `ImportSource::get_namespace_ident()`)
so downstream sync visitors can read it without re-entering the async
layer.
- `ReferencedAsset::get_ident` / `get_ident_inner` populate the field.
For in-group re-exports the inner module propagates up; for external
references the `External` variant is used.
- `EsmAssetReference::code_generation` destructures
`ReferencedAssetIdent::Module { namespace_ident, ctxt, import_source, ..
}`
and dispatches purely on `import_source`; it no longer reads
`referenced_asset` after the `get_ident` call. The hoisted-statement
dedup key still uses the directly-referenced asset's id, so two
references that happen to resolve to the same inner module via
different paths (e.g. direct vs. through a rename) still emit
separate `var` declarations for AST merging to rename.
- ESM-external gating (`__turbopack_external_import` vs.
`__turbopack_external_require`) stays where it was — the emit site
reads `self.import_externals` from the surrounding
`EsmAssetReference`, so `ImportSource::External` does not carry it.
No additional `MergeableModuleExposure` or `additional_ids` changes are
needed. The rename module is never referenced at runtime; no snapshot
files change.
### Tests
-
`turbopack/crates/turbopack-tests/tests/execution/turbopack/exports/self-reexport-star/`
— scope-hoisted execution test covering self-namespace re-exports,
nested access (`Data.Data.foo`, `Data.Data.Data.bar`), chained
re-exports through another module, and namespace key enumeration.
-
`turbopack/crates/turbopack-tests/tests/execution/turbopack/exports/self-reexport-star-no-hoisting/`
— same test cases, run with scope hoisting disabled, reusing the
fixtures from the sibling directory.
Verified by `cargo test --test execution` (213 passed) and
`cargo test --test snapshot` (89 passed) in `turbopack-tests`. No
snapshot files are modified.
Closes NEXT-
Fixes #
<!-- NEXT_JS_LLM_PR -->
---------
Co-authored-by: Tobias Koppers <sokra@users.noreply.github.com>
Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: Niklas Mischkulnig <4586894+mischnic@users.noreply.github.com>
The intel runners were quite a bit slower than the base arm runners. We were previously compiling both architectures on self-hosted arm so this just back the status quo on GH runners to improve speed. This shaves off ~10/20 mins. Validated [here](https://github.com/vercel/next.js/actions/runs/25071926790/job/73454315459)
Currently doesn't have a measureable performance impact. Previously we assigned module ids for all nodes in the graph, even the subgraphs we detect as unused. They weren't chunked already, but the codegen that references those wasn't gracefully handling `.chunk_item_id(m)` failing (where `m` is a unused skipped module) So `.iter_nodes()` should basically be deprecated. It doesn't respect `removed_unused_imports` and the side-effect-free inference. And this would get much much worse once NFT is on the module graph, because then there are way more modules that should be skipped via the same mechanism This manifested as the following error after switching from `.iter_nodes` to `.traverse_edges_unordered` in global_module_ids.rs ``` Debug info: - An error occurred while generating the chunk item [project]/node_modules/.pnpm/next@file+..+next.js+packages+next_@babel+core@7.29.0_@opentelemetry+api@1.7.0_@playwri_9a8ea08672cbf59c2720abbb0c8879be/node_modules/next/dist/esm/server/stream-utils/node-web-streams-helper.js [app-rsc] (ecmascript) - Execution of <MergedEcmascriptModule as EcmascriptChunkPlaceable>::chunk_item_content failed - Execution of *EcmascriptChunkItemContent::new failed - Execution of EcmascriptModuleContent::new_merged failed - ModuleId not found for ident: [project]/node_modules/.pnpm/next@file+..+next.js+packages+next_@babel+core@7.29.0_@opentelemetry+api@1.7.0_@playwri_9a8ea08672cbf59c2720abbb0c8879be/node_modules/next/dist/esm/shared/lib/hash.js [app-rsc] (ecmascript) ```
In dev mode, the static shell validation only runs during the initial page load and HMR refreshes, not during client-side navigations. During a client-side navigation an invalid dynamic usage error — for example a `'use cache'` fill timeout, or a request API like `cookies()` called inside a `'use cache'` scope — does still reach the browser via the errored `'use cache'` stream. But when the cache invocation is wrapped in a user-space `try`/`catch`, the error is caught and swallowed in user code, and because the static shell validation isn't running to surface it separately, the error is then neither sent to the browser nor logged to the terminal. When we skip the validation, we now wait for the full render stream to finish and then, if an error was recorded during the render, send it to the browser and log it to the terminal. Waiting for the stream to finish mirrors what the static shell validation already does, and ensures we also catch errors from `'use cache'` invocations that only run in the dynamic stage.
…bursts of changes (#93229) This adds a debounce to the _ui_ in development, reducing churn and flicker from the Next.js logo indicator's "Compiling" and "Rendering" messages. This is wholistic and covers any rapid changes in dev, usually caused by agents making a series of changes. **There's currently a debounce of 30ms coming from the subscription to changes in Turbopack itself, we could consider lengthening that as well.** This commit adds a useDebouncedValue hook and uses it to debounce status transitions in NextLogo, so rapid active->active alternations (e.g. Compiling→Rendering→Compiling) are smoothed into a single stable state for 300ms. Transitions to/from None remain immediate so: - Fast single builds that complete before the 400ms enterDelay still never show the pill. - The pill still appears promptly when a long build starts (no added latency on top of the existing enter delay). Changes: - use-debounced-value.ts: new generic trailing-edge debounce hook with an optional leading predicate to bypass the delay for specific transitions. - next-logo.tsx: apply the debounce to the computed status; derive shouldShowStatus from the debounced value so both pill mount-gating and label transitions are smoothed together. Test Plan: Ran a script that simulated an agent making hundreds of changes in rapid successions. Verified before this change, the indicator would flash between Compiling and Rendering rapidly. Now, it consistently stays on "Compiling" before the final render.
Previously, it only used the rust files as the cache key via `rust-fingerprint`. So changing fixtures didn't invalidate the turborepo run.
Add `iter_reachable_modules` and `iter_reachable_nodes`. They actually respect unused references
…93323) Currently not implemented, but maybe in the future
<!-- Thanks for opening a PR! Your contribution is much appreciated. To make sure your PR is handled as smoothly as possible we request that you follow the checklist sections below. Choose the right checklist for the change(s) that you're making: ## For Contributors ### Improving Documentation - Run `pnpm prettier-fix` to fix formatting issues before opening the PR. - Read the Docs Contribution Guide to ensure your contribution follows the docs guidelines: https://nextjs.org/docs/community/contribution-guide ### Fixing a bug - Related issues linked using `fixes #number` - Tests added. See: https://github.com/vercel/next.js/blob/canary/contributing/core/testing.md#writing-tests-for-nextjs - Errors have a helpful link attached, see https://github.com/vercel/next.js/blob/canary/contributing.md ### Adding a feature - Implements an existing feature request or RFC. Make sure the feature request has been accepted for implementation before opening a PR. (A discussion must be opened, see https://github.com/vercel/next.js/discussions/new?category=ideas) - Related issues/discussions are linked using `fixes #number` - e2e tests added (https://github.com/vercel/next.js/blob/canary/contributing/core/testing.md#writing-tests-for-nextjs) - Documentation added - Telemetry added. In case of a feature if it's used or not. - Errors have a helpful link attached, see https://github.com/vercel/next.js/blob/canary/contributing.md ### Signed commits - This repository requires verified commit signatures on protected branches. - If this pull request is blocked for unsigned commits, re-sign the commits and force-push the branch. - A `Signed-off-by` line in the commit message is not enough. ## For Maintainers - Minimal description (aim for explaining to someone not on the team to understand the PR) - When linking to a Slack thread, you might want to share details of the conclusion - Link both the Linear (Fixes NEXT-xxx) and the GitHub issues - Add review comments if necessary to explain to the reviewer the logic behind a change ### What? ### Why? ### How? Closes NEXT- Fixes # -->
From vercel/nft@baf9df6 Still need to eventually look into actually enabling all of these
Land a few optimizations to the trace server
* Change `SpanEvent` so it is 32 bytes instead of 40 bytes by triggering a `niche` optimization
* Change `args` and `events` to be a `smallvec` with inline size 1
* for `args` it is size <=1 ~31% of the time
* for `events` it is size <=1 69% of the time
* Compute min/max timestamps in a single pass instead of 2 when inserting into the selftimetree
* Bundle dynamically computed 'total' fields behind a single OnceLock
* saves 40 bytes per span due to `Oncelock` overheads
* Inline SpanTimeData and SpanNames into Span
* We get little benefit from deferring the allocations and by inlining we save time and improve memory locality.
* post load SpanTimeData is allocated for 94% of spans, but after loading `trace.nextjs.org` it is 100%
* post load SpanNames is allocated for 0% of spans, but after loading it is 96.2% of spans
* Remove the `inner` `OnceLocks` from `SpanNames` we can just allocate these all together
Measuring with one 10gb trace file I see loading times progress from 75.7s (33G of ram) to 60.5s (19.5G of ram). With loading times hitting >200mb/s occasionally
## What? Updates the tokio and tokio-util dependencies to their latest versions: - `tokio`: `~1.47.3` → `1.52.1` - `tokio-util`: `0.7.13` → `0.7.18` ## Why? Keep Rust dependencies up-to-date to benefit from bug fixes, performance improvements, and security patches. ## How? Updated version constraints in `Cargo.toml` and regenerated `Cargo.lock`. <!-- NEXT_JS_LLM_PR -->
## What Remove another lazy lock from trait vtables. --------- Co-authored-by: Luke Sandberg <lukesandberg@users.noreply.github.com>
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 : )