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

Seed CacheNodes immediately after receiving response #58669

Merged
merged 5 commits into from
Nov 21, 2023

Conversation

acdlite
Copy link
Contributor

@acdlite acdlite commented Nov 20, 2023

Based on #58666

I submitted this stack as separate PRs so I could run CI but I want to land them as a single unit. So I've left all but this last one in draft mode. You can review them commit-by-commit.

We render nested layouts in parallel on the server. This means we should be able to create a CacheNode entry for every layout in the tree as soon as the Flight response is received by the client, even before the nested layouts have streamed in. Currently, we wait until the nested layouts start rendering before we write them into the cache, which prevents us from performing certain optimizations. That's because the CacheNodes are sent as a prop to LayoutRouter; the only way to unwrap the CacheNode is to wait for LayoutRouter to render.

In previous PRs, I updated the server to create a top-level data structure that contains all the CacheNodes for the entire tree. This PR updates the client side to receive the nodes and write them into the cache.

@ijjk ijjk added created-by: Next.js team PRs by the Next.js team type: next labels Nov 20, 2023
@ijjk
Copy link
Member

ijjk commented Nov 20, 2023

Tests Passed

@ijjk
Copy link
Member

ijjk commented Nov 20, 2023

Stats from current PR

Default Build
General
vercel/next.js canary acdlite/next.js seed-cachenodes-immediately Change
buildDuration 15.2s 15s N/A
buildDurationCached 9s 9.8s ⚠️ +833ms
nodeModulesSize 199 MB 199 MB N/A
nextStartRea..uration (ms) 503ms 501ms N/A
Client Bundles (main, webpack)
vercel/next.js canary acdlite/next.js seed-cachenodes-immediately Change
199-HASH.js gzip 28.7 kB 28.7 kB N/A
3f784ff6-HASH.js gzip 53.3 kB 53.3 kB
494.HASH.js gzip 180 B 181 B N/A
framework-HASH.js gzip 45.2 kB 45.2 kB
main-app-HASH.js gzip 241 B 239 B N/A
main-HASH.js gzip 31.7 kB 31.7 kB N/A
webpack-HASH.js gzip 1.7 kB 1.7 kB
Overall change 100 kB 100 kB
Legacy Client Bundles (polyfills)
vercel/next.js canary acdlite/next.js seed-cachenodes-immediately Change
polyfills-HASH.js gzip 31 kB 31 kB
Overall change 31 kB 31 kB
Client Pages
vercel/next.js canary acdlite/next.js seed-cachenodes-immediately Change
_app-HASH.js gzip 194 B 195 B N/A
_error-HASH.js gzip 182 B 181 B N/A
amp-HASH.js gzip 501 B 503 B N/A
css-HASH.js gzip 322 B 323 B N/A
dynamic-HASH.js gzip 2.5 kB 2.5 kB
edge-ssr-HASH.js gzip 253 B 255 B N/A
head-HASH.js gzip 348 B 347 B N/A
hooks-HASH.js gzip 369 B 368 B N/A
image-HASH.js gzip 4.28 kB 4.27 kB N/A
index-HASH.js gzip 256 B 256 B
link-HASH.js gzip 2.61 kB 2.6 kB N/A
routerDirect..HASH.js gzip 311 B 311 B
script-HASH.js gzip 384 B 383 B N/A
withRouter-HASH.js gzip 307 B 308 B N/A
1afbb74e6ecf..834.css gzip 106 B 106 B
Overall change 3.17 kB 3.17 kB
Client Build Manifests
vercel/next.js canary acdlite/next.js seed-cachenodes-immediately Change
_buildManifest.js gzip 484 B 483 B N/A
Overall change 0 B 0 B
Rendered Page Sizes
vercel/next.js canary acdlite/next.js seed-cachenodes-immediately Change
index.html gzip 528 B 526 B N/A
link.html gzip 538 B 540 B N/A
withRouter.html gzip 522 B 522 B
Overall change 522 B 522 B
Edge SSR bundle Size
vercel/next.js canary acdlite/next.js seed-cachenodes-immediately Change
edge-ssr.js gzip 92.4 kB 92.4 kB N/A
page.js gzip 145 kB 145 kB N/A
Overall change 0 B 0 B
Middleware size
vercel/next.js canary acdlite/next.js seed-cachenodes-immediately Change
middleware-b..fest.js gzip 627 B 625 B N/A
middleware-r..fest.js gzip 150 B 151 B N/A
middleware.js gzip 24.8 kB 24.8 kB N/A
edge-runtime..pack.js gzip 1.92 kB 1.92 kB
Overall change 1.92 kB 1.92 kB
Next Runtimes
vercel/next.js canary acdlite/next.js seed-cachenodes-immediately Change
app-page-exp...dev.js gzip 167 kB 167 kB N/A
app-page-exp..prod.js gzip 93.4 kB 93.2 kB N/A
app-page-tur..prod.js gzip 94.1 kB 94 kB N/A
app-page-tur..prod.js gzip 88.7 kB 88.5 kB N/A
app-page.run...dev.js gzip 137 kB 137 kB N/A
app-page.run..prod.js gzip 88 kB 87.9 kB N/A
app-route-ex...dev.js gzip 23.8 kB 23.8 kB
app-route-ex..prod.js gzip 16.4 kB 16.4 kB
app-route-tu..prod.js gzip 16.5 kB 16.5 kB
app-route-tu..prod.js gzip 16 kB 16 kB
app-route.ru...dev.js gzip 23.2 kB 23.2 kB
app-route.ru..prod.js gzip 16 kB 16 kB
pages-api-tu..prod.js gzip 9.37 kB 9.37 kB
pages-api.ru...dev.js gzip 9.64 kB 9.64 kB
pages-api.ru..prod.js gzip 9.37 kB 9.37 kB
pages-turbo...prod.js gzip 21.8 kB 21.8 kB
pages.runtim...dev.js gzip 22.5 kB 22.5 kB
pages.runtim..prod.js gzip 21.8 kB 21.8 kB
server.runti..prod.js gzip 49.1 kB 49.1 kB
Overall change 256 kB 256 kB
Diff details
Diff for page.js

Diff too large to display

Diff for 199-HASH.js

Diff too large to display

Diff for app-page-exp..ntime.dev.js

Diff too large to display

Diff for app-page-exp..time.prod.js

Diff too large to display

Diff for app-page-tur..time.prod.js

Diff too large to display

Diff for app-page-tur..time.prod.js

Diff too large to display

Diff for app-page.runtime.dev.js

Diff too large to display

Diff for app-page.runtime.prod.js

Diff too large to display

Commit: 06f91c3

This reverts commit d5aea2c, which
added an additional field to the RSC payload intended to represent a
tree of subtree data for all the nested layouts in a response. I didn't
think about the fact that the subtreeData slot, which represents the
node at the root of the subtree, would be included twice.

Instead, we should replace the subtreeData slot to be of the
CacheNodeSeedData tree type, instead of just a single node. I'll do that
in the following commits.
I need to use the result of createComponentTree in two different parts
of the Flight response, but it shouldn't be called during a prefetch. So
I hoisted the shouldSkipComponentTree check to earlier in the control
flow. This also lets us get rid of an extra React element that was being
used to lazily call createComponentTree.
The Flight response type includes the component node for the root of the
subtree that was rendered. Instead of passing just the root node, we
want to pass the entire tree of nested layouts. As an imcremental step,
this changes the type to pass a CacheNodeSeedData object, but it passes
only a partial tree that includes only the root node. In the next step,
we'll pass the entire tree in this object, and in exchange we'll remove
the initialChildNode prop to LayoutRouter.
@acdlite acdlite force-pushed the seed-cachenodes-immediately branch 3 times, most recently from 846933e to 71f2f3c Compare November 21, 2023 01:54
We render nested layouts in parallel on the server. This means we should
be able to create a CacheNode entry for every layout in the tree as soon
as the Flight response is received by the client, even before the nested
layouts have streamed in. Currently, we wait until the nested layouts
start rendering before we write them into the cache, which prevents us
from performing certain optimizations. That's because the CacheNodes are
sent as a prop to LayoutRouter; the only way to unwrap the CacheNode is
to wait for LayoutRouter to render.

In previous PRs, I updated the server to create a top-level data
structure that contains all the CacheNodes for the entire tree. This PR
updates the client side to receive the nodes and write them into
the cache.
@acdlite acdlite marked this pull request as ready for review November 21, 2023 02:18
@@ -11,21 +11,22 @@ export function applyFlightData(
wasPrefetched: boolean = false
): boolean {
// The one before last item is the router state tree patch
const [treePatch, subTreeData, head /* , cacheNodeSeedData */] =
flightDataPath.slice(-4)
const [treePatch, cacheNodeSeedData, head] = flightDataPath.slice(-3)
Copy link
Contributor

Choose a reason for hiding this comment

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

While you're touching this, should extract the slices call to some sort of helper or something? It's always been sort of error-prone and this is a good opportunity to clean it up

Copy link
Contributor

Choose a reason for hiding this comment

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

(same for the random array accesses)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I intentionally left these alone because we’re about to completely change this data structure anyway.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

But yes I agree we should use accessor functions for internally unsound data structures in the future

@feedthejim
Copy link
Contributor

looks good to me on my end, nice reverse engineering :)

@acdlite acdlite merged commit 642ad12 into vercel:canary Nov 21, 2023
104 of 107 checks passed
acdlite added a commit to acdlite/next.js that referenced this pull request Nov 21, 2023
We previously used preloadComponent to render nested page/layouts in
parallel, but now that all layouts are passed to React at the top level
(vercel#58669), we no longer need this special module.

A flaw of preloadComponent was that it only called the top-most
component in the layout. In the new approach, React can render the
entire layout tree in parallel.
kodiakhq bot pushed a commit that referenced this pull request Nov 22, 2023
After the changes in #58669, it's now possible for these `DynamicUsageErrors` to be caught in the streaming renderer. We didn't have any logic to handle this happening inside the existing catch block, so this adds a check for that specific error and re-throws it so that it properly bubbles to `exportPage`. `exportPage` checks the error to see if it's a `DynamicUsageError` and if so, marks the page as dynamic.

(This resolves an issue with particular scenarios hanging the build, caught by a few of our tests: [example](https://github.com/vercel/next.js/actions/runs/6949079695/job/18907597751?pr=58744), [example](https://github.com/vercel/next.js/actions/runs/6948412805/job/18904387924))
kodiakhq bot pushed a commit that referenced this pull request Nov 27, 2023
We previously used preloadComponent to render nested page/layouts in
parallel, but now that all layouts are passed to React at the top level
(#58669), we no longer need this special module.

A flaw of preloadComponent was that it only called the top-most
component per layout. In the new approach, React can preload non-layout
components, too.

Co-authored-by: Zack Tanner <zacktanner@gmail.com>
@github-actions github-actions bot added the locked label Dec 6, 2023
@github-actions github-actions bot locked as resolved and limited conversation to collaborators Dec 6, 2023
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

4 participants