fix(build): don't block SSG on telemetry flush, add persistence spans to trace-build#91335
Merged
lukesandberg merged 12 commits intocanaryfrom Mar 16, 2026
Merged
fix(build): don't block SSG on telemetry flush, add persistence spans to trace-build#91335lukesandberg merged 12 commits intocanaryfrom
lukesandberg merged 12 commits intocanaryfrom
Conversation
Collaborator
Tests Passed |
lukesandberg
commented
Mar 13, 2026
fbb2dd4 to
c3cd496
Compare
lukesandberg
commented
Mar 13, 2026
lukesandberg
commented
Mar 13, 2026
lukesandberg
commented
Mar 13, 2026
lukesandberg
commented
Mar 13, 2026
Collaborator
Stats from current PR✅ No significant changes detected📊 All Metrics📖 Metrics GlossaryDev Server Metrics:
Build Metrics:
Change Thresholds:
⚡ Dev Server
📦 Dev Server (Webpack) (Legacy)📦 Dev Server (Webpack)
⚡ Production Builds
📦 Production Builds (Webpack) (Legacy)📦 Production Builds (Webpack)
📦 Bundle SizesBundle Sizes⚡ TurbopackClient Main Bundles
Server Middleware
Build DetailsBuild Manifests
📦 WebpackClient Main Bundles
Polyfills
Pages
Server Edge SSR
Middleware
Build DetailsBuild Manifests
Build Cache
🔄 Shared (bundler-independent)Runtimes
📝 Changed Files (2 files)Files with changes:
View diffspages-api.runtime.dev.jsDiff too large to display pages.runtime.dev.jsDiff too large to display 📎 Tarball URL |
Merging this PR will not alter performance
Comparing Footnotes
|
lukesandberg
commented
Mar 14, 2026
lukesandberg
commented
Mar 14, 2026
sokra
approved these changes
Mar 15, 2026
… to trace-build This fixes two issues introduced in PR #90397: 1. **Don't block SSG on Turbopack shutdown**: Previously, `workerMain()` awaited the shutdown promise before returning, which blocked SSG from starting until Turbopack persistence completed. Now `workerMain` returns immediately, and trace event collection happens in `waitForShutdown()` which is called asynchronously after the build. This allows SSG and persistence to run in parallel. 2. **Add Turbopack persistence spans to trace-build allowlist**: Added `turbopack-build-events`, `turbopack-persistence`, and `turbopack-compaction` to the trace-build allowlist so these spans are captured in `.next/trace-build`. Enabled persistent caching in the trace-build test fixture to ensure these spans are emitted. Changes: - Move trace event collection from `workerMain` to `waitForShutdown` - Record trace events in parent process after shutdown completes - Add Turbopack span names to to-json-build allowlist - Enable turbopackFileSystemCacheForBuild in trace-build test - Update test snapshot to include turbopack-build-events Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
…_IGNORE_DIRTY in test - After project.shutdown() resolves, yield to the event loop via setTimeout so the JS async iterator drains all buffered persistence/compaction TraceEvents before getTraceEvents() is called in waitForShutdown. - Set TURBO_ENGINE_IGNORE_DIRTY=1 in the trace-build test so persistent caching works in dirty git repos (test directories are always dirty). - Update the turbopack snapshot to expect turbopack-compaction and turbopack-persistence spans.
Replace the setTimeout(100) hack with explicit iterator lifecycle
management:
- Expose stop() on backgroundLogCompilationEvents return value that
calls iterator.return() to close the async iterator
- After project.shutdown(), call stop() then await the promise to
drain buffered events before getTraceEvents()
- Signal subscription end from Rust when the receiver loop exits
(defense in depth: iterator self-closes even without stop())
- Update return type to Promise<void> & { stop(): void }
- Fix TURBO_ENGINE_IGNORE_DIRTY comment and remove inaccurate note
about persistence spans
- Drop redundant comment in workerMain
The Rust-side subscription close signal allows the JS iterator to properly drain all compilation events after shutdown, including the persistence span that was previously missed due to timing.
Add a production-only test that reads the trace-build file and verifies that static-generation starts before turbopack-persistence finishes. This ensures SSG is not blocked on the Turbopack shutdown promise. Uses the startTime field (Date.now() epoch ms) which is comparable across the worker and parent processes.
The timing-based assertion (startTime comparison) was incorrect: for small test fixtures, persistence finishes before SSG starts because the build pipeline has many steps between turbopackBuild() returning and static-generation beginning. Replace with a structural assertion that verifies turbopack-persistence is not an ancestor of static-generation in the span tree, proving they are independent operations. The existence of both spans in trace-build already proves the fix works: persistence proves the iterator was properly drained, and static-generation proves SSG ran to completion.
Replace the custom stop() method on the backgroundLogCompilationEvents promise with standard AbortSignal-based cancellation. The caller creates an AbortController and passes its signal; aborting the signal immediately closes the async iterator via an abort event listener, fixing the previous limitation where abort only took effect on the next event.
…s loop The abort listener already calls iterator.return() which breaks the for-await loop, making the explicit aborted check unnecessary.
…path cleanup - Move duplicated parseTraceFile/parseTraceEvents into test/lib/parse-trace-file.ts and update all 5 test files to use the shared utility. - Fix error path in turbopackBuild() to abort the compilation events iterator and drain it before shutting down the project, preventing a leaked subscription on build failure.
- Use relative import paths for the shared parse-trace-file utility instead of bare specifiers that TypeScript can't resolve. - Rename compilationEventsController to shutdownController (thread 7). - Improve shutdown comment to explain it's the last chance to capture events (thread 8). - Fix error path ordering: shutdown before abort so final events are emitted before the iterator closes (thread 9). - Remove the SSG/persistence overlap assertion test since it asserts a static condition (thread 10).
…events are captured The telemetry global was set after installBindings, so when SWC loading failed, eventSwcLoadFailure() found no telemetry instance and silently dropped the NEXT_SWC_LOAD_FAILURE event. Move distDir computation and telemetry initialization before installBindings to fix this. Co-Authored-By: Claude <noreply@anthropic.com>
The Telemetry constructor creates distDir/cache in CI environments via getStorageDirectory(). Moving telemetry init before installBindings (previous commit) inadvertently caused getCacheDir's "no-cache" warning to be suppressed because the cache dir already existed when checked. Co-Authored-By: Claude <noreply@anthropic.com>
c66ecad to
77ececa
Compare
Contributor
Author
This stack of pull requests is managed by Graphite. Learn more about stacking. |
Contributor
Author
Merge activity
|
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 join this conversation on GitHub.
Already have an account?
Sign in to comment
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.

What?
Two fixes for the Turbopack build tracing introduced in #90397:
Don't block SSG on Turbopack shutdown:
workerMain()no longer awaits the shutdown promise before returning. Trace event collection is deferred towaitForShutdown(), which the parent process awaits after SSG completes. This allows static generation and Turbopack persistence/cache-flush to run in parallel.Add persistence spans to
trace-buildallowlist:turbopack-build-events,turbopack-persistence, andturbopack-compactionare now included in theto-json-build.tsallowlist so they appear in.next/trace-build.Why?
await shutdownPromiseinworkerMain()was too eager — it prevented the caller from acknowledging the build as complete and starting SSG until Turbopack persistence finished flushing to disk.turbopack-persistence,turbopack-compaction) were not in theto-json-build.tsallowlist, so they were silently filtered out of.next/trace-build.How?
impl.ts(worker):await shutdownPromisefromworkerMain()— it now returns build results immediatelywaitForShutdown()now returns{ debugTraceEvents }after awaiting shutdown, so trace events are collected only after all compilation events (including persistence spans) have been processedindex.ts(parent):recordTraceEvents(debugTraceEvents)from theworkerMainresult handler into theshutdownPromise.then()chain, so events are replayed into the parent reporter after shutdown completesto-json-build.ts:turbopack-build-events,turbopack-persistence,turbopack-compactionto the allowlistTest updates:
turbopackFileSystemCacheForBuild: truein the trace-build test fixtureturbopack-build-events