Skip to content

[Next.js/dylib] Make production webpack App Route lazy loading deadlock-free #8039

Description

@proggeramlug

What happened?

Production Next.js webpack output discovers and initializes route/chunk modules lazily by computed path. #5438 added Perry's path-module registry and lazy initialization groundwork, but the Perry/Perch benchmark still had to stay outside the production route boundary because that path could deadlock or lose values.

This issue owns the cold and concurrent lazy-load correctness gate in app-only dylib mode. It does not ask for another synthetic webpack example.

Reproduction app (provided)

Use the exact pinned app and verifier attached to #8034. It is intentionally built with webpack and includes both layers that must lazy-load:

  1. .next/server/app/api/benchmark/route.js, selected by the production server at request time; and
  2. await import("./lazy-work") inside the route implementation.

The imported module computes the response checksum, so an undefined namespace, lost export, duplicate init, or premature placeholder cannot accidentally pass.

bash make-next-app-route-fixture.sh
cd perry-next-app-route-fixture
npm install --package-lock-only
npm ci
npm run build
BASE_URL=http://127.0.0.1:3100 npm run verify

Run the Perry verifier first against a fresh process (cold registry), then a second time in the same process (warm registry).

Required implementation behavior

  • Discover all required .next/server/** JavaScript chunks during AOT compilation without eagerly executing request-only modules at startup.
  • Register the generated module init functions and exports in the provider-visible registry used by the app dylib.
  • Release registry/loader locks before executing an init function so recursive imports cannot deadlock.
  • Publish a module namespace only after initialization is complete, while still handling cycles with CJS-compatible partial exports.
  • Ensure one logical initialization under concurrent first requests; waiters must receive the initialized namespace or the same error.
  • Cache by the same canonical path Next uses at runtime; reject aliasing that initializes one file twice.
  • Propagate initialization exceptions/rejections to every waiter and allow only an explicitly defined retry policy.

Regression coverage

In addition to #8034, add a small path-module test graph containing: recursive lazy initialization, a CJS partial-export cycle, concurrent first access, an async dynamic import, and a module whose export value is undefined (to distinguish a real value from a missing entry).

Acceptance criteria

  • The first cold [Next.js/dylib] Add a pinned production App Route parity fixture and CI gate #8034 request completes; it never hangs waiting on a registry lock or promise.
  • The attached 20-way first-request burst returns all exact checksums and IDs.
  • A second verifier pass uses the initialized modules and is byte-identical.
  • Each lazy module's side-effect counter is 1 per process.
  • Behavior matches in classic executable and app-only dylib modes with shared providers.
  • No eager startup execution of the route or lazy-work is introduced.

Baseline / references

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugConfirmed defect or regressionparityCompatibility gap with Node.js, ECMAScript, or the supported ecosystem

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions