Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

perf: add bundled rendering runtimes #52997

Merged
merged 126 commits into from Sep 7, 2023
Merged

perf: add bundled rendering runtimes #52997

merged 126 commits into from Sep 7, 2023

Conversation

feedthejim
Copy link
Contributor

@feedthejim feedthejim commented Jul 21, 2023

What?

In Next, rendering a route involves 3 layers:

  • the routing layer, which will direct the request to the correct route to render
  • the rendering layer, which will take a route and render it appropriately
  • the user layer, which contains the user code

In #51831, in order to optimise the boot time of Next.js, I introduced a change that allowed the routing layer to be bundled. In this PR, I'm doing the same for the rendering layer. This is building up on @wyattjoh's work that initially split the routing and the rendering layer into separate entry-points.

The benefits of having this approach is that this allows us to compartmentalise the different part of Next, optimise them individually and making sure that serving a request is as efficient as possible, e.g. rendering a pages route should not need code from the app router to be used.

There are now 4 different rendering runtimes, depending on the route type:

  • app pages: for App Router pages
  • app routes: for App Router route handlers
  • pages: for legacy pages
  • pages api: for legacy API routes

This change should be transparent to the end user, beside faster cold boots.

Notable changes

Doing this change required a lot of changes for Next.js under the hood in order to make the different layers play well together.

New conventions for externals/shared modules

The big issue of bundling the rendering runtimes is that the user code needs to be able to reference an instance of a module/value created in Next during the render. This is the case when the user wants to access the router context during SSR via next/link for example; when you call useContext(value) the value needs to be the exact same reference to one as the one created by createContext earlier.

Previously, we were handling this case by making all files from Next that were affected by this externals, meaning that we were marking them not to be bundled.

Why not keep it this way?

The goal of this PR as stated previously was to make the rendering process as efficient as possible, so I really wanted to avoid extraneous fs reads to unoptimised code.

In order to "fix" it, I introduced two new conventions to the codebase:

  • all files that explicitly need to be shared between a rendering runtime and the user code must be suffixed by .shared-runtime and exposed via adding a reference in the relevant externals file. At compilation time, a reference to a file ending with this will get re-written to the appropriate runtime.
  • all files that need to be truly externals need to be suffixed by .external. At compilation time, a reference to it will stay as-is. This special case is needed mostly only for the async local storages that need to be shared with all three layers of Next.

As a side effect, we should be bundling more of the Next code in the user bundles, so it should be slightly more efficient.

App route handlers are compiled on their own layer

App route handlers should be compiled in their own layer, this allows us to separate more cleanly the compilation logic here (we don't need to run the RSC logic for example).

New rendering bundles

We now generate a prod and a dev bundle for:

  • the routing server
  • the app/pages SSR rendering process
  • the API routes process

The development bundle is needed because:

  • there is code in Next that relies on NODE_ENV
  • because we opt out of the logic referencing the correct rendering runtime in dev for a shared-runtime file. This is because we don't need to and that Turbopack does not support rewriting an external to something that looks like this require('foo').bar.baz yet. We will need to fix that when Turbopack build ships.

New development pipeline

Bundling Next is now required when developing on the repo so I extended the taskfile setup to account for that. The webpack config for Next itself lives in webpack.config.js and contains the logic for all the new bundles generated.

Misc changes

There are some misc reshuffling in the code to better use the tree shaking abilities that we can now use.

fixes NEXT-1573

Copy link

@orca-security-us orca-security-us bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Orca Security Scan Summary

Status Check Issues by priority
Passed Passed Secrets high 0   medium 1   low 0   info 0 View in Orca

packages/next/src/server/response-cache/web.ts Outdated Show resolved Hide resolved
packages/next/src/server/require-hook.ts Show resolved Hide resolved
packages/next/src/export/worker.ts Outdated Show resolved Hide resolved
packages/next/src/export/worker.ts Outdated Show resolved Hide resolved
timneutkens
timneutkens previously approved these changes Sep 6, 2023
Copy link
Member

@timneutkens timneutkens left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good to me now 👍

@feedthejim feedthejim marked this pull request as draft September 6, 2023 11:05
@feedthejim
Copy link
Contributor Author

will wait until after the next patch to release

@feedthejim feedthejim marked this pull request as ready for review September 7, 2023 14:15
@kodiakhq kodiakhq bot merged commit a5b7c77 into canary Sep 7, 2023
47 checks passed
@kodiakhq kodiakhq bot deleted the feedthejim/build-profile branch September 7, 2023 15:51
ijjk added a commit that referenced this pull request Sep 7, 2023
kodiakhq bot pushed a commit that referenced this pull request Sep 7, 2023
This reverts commit a5b7c77.

Our E2E tests are failing with this change this reverts to allow investigating async 

x-ref: https://github.com/vercel/next.js/actions/runs/6112149126/job/16589769954
feedthejim added a commit that referenced this pull request Sep 8, 2023
kodiakhq bot pushed a commit that referenced this pull request Sep 8, 2023
see #52997

also added a fix by @jridgewell to fix turbopack





Co-authored-by: Justin Ridgewell <112982+jridgewell@users.noreply.github.com>
kodiakhq bot pushed a commit to vercel/vercel that referenced this pull request Sep 12, 2023
This PR adds an environment variable that should allow us to test the bundled version for Next.js on Vercel, see vercel/next.js#52997 for reference.

The changes include:
- a new environment variable `VERCEL_NEXT_BUNDLED_SERVER`
- some logic changes that will put app route handlers into their own lambda groups
- extra logic to require early the rendering runtimes (see PR above for details)
@github-actions github-actions bot locked as resolved and limited conversation to collaborators Sep 22, 2023
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
created-by: Next.js team PRs by the Next.js team locked Turbopack Related to Turbopack with Next.js. type: next
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

6 participants