Skip to content

turbopack: emit assets before duplicate check in emit_assets#93875

Open
sokra wants to merge 2 commits into
canaryfrom
sokra/fix-incremental-emit-assets
Open

turbopack: emit assets before duplicate check in emit_assets#93875
sokra wants to merge 2 commits into
canaryfrom
sokra/fix-incremental-emit-assets

Conversation

@sokra
Copy link
Copy Markdown
Member

@sokra sokra commented May 15, 2026

What?

Reorders emit_assets in crates/next-core/src/emit.rs so that the actual emit() / emit_rebase() writes happen before check_duplicates() runs, and parallelizes the per-duplicate diff checks inside check_duplicates.

Why?

Under eventual consistency, all children might be removed from emit_assets. This causes all content() and emit() tasks being inactive and keeping the stale value (which is potentially an error).

On recomputation of emit_assets() this only make very few content() tasks active, as tasks are still in a slate error state and make emit_assets() exit early. So it takes N recomputations of emit_assets() to be up-to-date, where N is the number of duplicate assets. This can slow down builds drastically and did cause build time up to 30min.

How?

  • Swap the order in both branches of emit_assets (server-side emit and client-side emit_rebase) so the write happens first, then check_duplicates runs as a diagnostic afterwards. A comment is left explaining the ordering requirement.
  • check_duplicates now returns Result<()> instead of returning the picked asset. The caller picks assets.first() directly, since check_duplicates only validated equivalence — it didn't choose a different asset.
  • Convert the inner loop in check_duplicates to iter.map(async |next| { ... }).try_join().await?, running the diff comparisons in parallel. ext is computed once before the loop and cloned into each task.

sokra and others added 2 commits May 15, 2026 17:17
Reorder emit_assets so the actual emit() runs before check_duplicates().
Under eventual consistency the duplicate check can crash, and we want
the asset written to disk regardless.
@sokra sokra requested a review from lukesandberg May 15, 2026 15:46
@sokra sokra marked this pull request as ready for review May 15, 2026 15:46
@github-actions
Copy link
Copy Markdown
Contributor

Failing test suites

Commit: a782c34 | About building and testing Next.js

pnpm test-dev test/development/app-dir/create-next-app-default/create-next-app-default.test.ts (job)

  • create-next-app default template > should create and run without browser warnings or errors (DD)
Expand output

● create-next-app default template › should create and run without browser warnings or errors

next dev exited unexpectedly with code/signal 1

  192 |           if (code || signal) {
  193 |             this.childProcess = undefined
> 194 |             const error = new Error(
      |                           ^
  195 |               `next dev exited unexpectedly with code/signal ${code || signal}`
  196 |             )
  197 |             clearTimeout(serverReadyTimeoutId)

  at ChildProcess.<anonymous> (lib/next-modes/next-dev.ts:194:27)

pnpm test-dev test/e2e/app-dir/pnpm-workspace-root/pnpm-workspace-root.test.ts (job)

  • pnpm-workspace-root > should detect root directory from pnpm-workspace.yaml and allow imports from outside app dir (DD)
  • pnpm-workspace-root > should not have multiple lockfiles warning when pnpm-workspace.yaml is present (DD)
Expand output

● pnpm-workspace-root › should detect root directory from pnpm-workspace.yaml and allow imports from outside app dir

Command failed with exit code 1 (EPERM): pnpm install --strict-peer-dependencies=false --no-frozen-lockfile --config.cacheDir=/tmp --prefer-offline

  24 |   }
  25 |
> 26 |   await execa('pnpm', args, {
     |   ^
  27 |     cwd,
  28 |     stdio: ['ignore', 'inherit', 'inherit'],
  29 |     env: {

  at makeError (../node_modules/.pnpm/[email protected]/node_modules/execa/lib/error.js:58:11)
  at handlePromise (../node_modules/.pnpm/[email protected]/node_modules/execa/index.js:112:26)
  at installDependencies (lib/create-next-install.js:26:3)
  at Span.traceAsyncFn (../packages/next/src/trace/trace.ts:146:14)
  at lib/create-next-install.js:251:9
  at Span.traceAsyncFn (../packages/next/src/trace/trace.ts:146:14)
  at createNextInstall (lib/create-next-install.js:66:10)
  at lib/next-modes/base.ts:348:13
  at Span.traceAsyncFn (../packages/next/src/trace/trace.ts:146:14)
  at NextDevInstance.createTestDir (lib/next-modes/base.ts:236:5)
  at NextDevInstance.setup (lib/next-modes/next-dev.ts:17:5)
  at lib/e2e-utils/index.ts:285:7
  at Span.traceAsyncFn (../packages/next/src/trace/trace.ts:146:14)
  at createNext (lib/e2e-utils/index.ts:250:12)
  at Object.<anonymous> (lib/e2e-utils/index.ts:340:14)

● pnpm-workspace-root › should not have multiple lockfiles warning when pnpm-workspace.yaml is present

Command failed with exit code 1 (EPERM): pnpm install --strict-peer-dependencies=false --no-frozen-lockfile --config.cacheDir=/tmp --prefer-offline

  24 |   }
  25 |
> 26 |   await execa('pnpm', args, {
     |   ^
  27 |     cwd,
  28 |     stdio: ['ignore', 'inherit', 'inherit'],
  29 |     env: {

  at makeError (../node_modules/.pnpm/[email protected]/node_modules/execa/lib/error.js:58:11)
  at handlePromise (../node_modules/.pnpm/[email protected]/node_modules/execa/index.js:112:26)
  at installDependencies (lib/create-next-install.js:26:3)
  at Span.traceAsyncFn (../packages/next/src/trace/trace.ts:146:14)
  at lib/create-next-install.js:251:9
  at Span.traceAsyncFn (../packages/next/src/trace/trace.ts:146:14)
  at createNextInstall (lib/create-next-install.js:66:10)
  at lib/next-modes/base.ts:348:13
  at Span.traceAsyncFn (../packages/next/src/trace/trace.ts:146:14)
  at NextDevInstance.createTestDir (lib/next-modes/base.ts:236:5)
  at NextDevInstance.setup (lib/next-modes/next-dev.ts:17:5)
  at lib/e2e-utils/index.ts:285:7
  at Span.traceAsyncFn (../packages/next/src/trace/trace.ts:146:14)
  at createNext (lib/e2e-utils/index.ts:250:12)
  at Object.<anonymous> (lib/e2e-utils/index.ts:340:14)

pnpm test-start test/e2e/app-dir/pnpm-workspace-root/pnpm-workspace-root.test.ts (job)

  • pnpm-workspace-root > should detect root directory from pnpm-workspace.yaml and allow imports from outside app dir (DD)
  • pnpm-workspace-root > should not have multiple lockfiles warning when pnpm-workspace.yaml is present (DD)
Expand output

● pnpm-workspace-root › should detect root directory from pnpm-workspace.yaml and allow imports from outside app dir

Command failed with exit code 1 (EPERM): pnpm install --strict-peer-dependencies=false --no-frozen-lockfile --config.cacheDir=/tmp --prefer-offline

  24 |   }
  25 |
> 26 |   await execa('pnpm', args, {
     |   ^
  27 |     cwd,
  28 |     stdio: ['ignore', 'inherit', 'inherit'],
  29 |     env: {

  at makeError (../node_modules/.pnpm/[email protected]/node_modules/execa/lib/error.js:58:11)
  at handlePromise (../node_modules/.pnpm/[email protected]/node_modules/execa/index.js:112:26)
  at installDependencies (lib/create-next-install.js:26:3)
  at Span.traceAsyncFn (../packages/next/src/trace/trace.ts:146:14)
  at lib/create-next-install.js:251:9
  at Span.traceAsyncFn (../packages/next/src/trace/trace.ts:146:14)
  at createNextInstall (lib/create-next-install.js:66:10)
  at lib/next-modes/base.ts:348:13
  at Span.traceAsyncFn (../packages/next/src/trace/trace.ts:146:14)
  at NextStartInstance.createTestDir (lib/next-modes/base.ts:236:5)
  at NextStartInstance.setup (lib/next-modes/next-start.ts:45:5)
  at lib/e2e-utils/index.ts:285:7
  at Span.traceAsyncFn (../packages/next/src/trace/trace.ts:146:14)
  at createNext (lib/e2e-utils/index.ts:250:12)
  at Object.<anonymous> (lib/e2e-utils/index.ts:340:14)

● pnpm-workspace-root › should not have multiple lockfiles warning when pnpm-workspace.yaml is present

Command failed with exit code 1 (EPERM): pnpm install --strict-peer-dependencies=false --no-frozen-lockfile --config.cacheDir=/tmp --prefer-offline

  24 |   }
  25 |
> 26 |   await execa('pnpm', args, {
     |   ^
  27 |     cwd,
  28 |     stdio: ['ignore', 'inherit', 'inherit'],
  29 |     env: {

  at makeError (../node_modules/.pnpm/[email protected]/node_modules/execa/lib/error.js:58:11)
  at handlePromise (../node_modules/.pnpm/[email protected]/node_modules/execa/index.js:112:26)
  at installDependencies (lib/create-next-install.js:26:3)
  at Span.traceAsyncFn (../packages/next/src/trace/trace.ts:146:14)
  at lib/create-next-install.js:251:9
  at Span.traceAsyncFn (../packages/next/src/trace/trace.ts:146:14)
  at createNextInstall (lib/create-next-install.js:66:10)
  at lib/next-modes/base.ts:348:13
  at Span.traceAsyncFn (../packages/next/src/trace/trace.ts:146:14)
  at NextStartInstance.createTestDir (lib/next-modes/base.ts:236:5)
  at NextStartInstance.setup (lib/next-modes/next-start.ts:45:5)
  at lib/e2e-utils/index.ts:285:7
  at Span.traceAsyncFn (../packages/next/src/trace/trace.ts:146:14)
  at createNext (lib/e2e-utils/index.ts:250:12)
  at Object.<anonymous> (lib/e2e-utils/index.ts:340:14)

Other failing CI jobs

@@ -116,8 +116,11 @@ pub async fn emit_assets(
.resolved_cell()
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.

this cell is now non-deterministic

at least add a comment, but we could fix it by moving construction into a new task and using as_side_effect (i think)

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