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

[Segment Cache] Support for non-PPR projects/pages #73960

Merged
merged 2 commits into from
Dec 18, 2024

Conversation

acdlite
Copy link
Contributor

@acdlite acdlite commented Dec 16, 2024

Based on:


This implements support for the Segment Cache in projects/pages where PPR is not enabled.

I originally thought the Segment Cache would be tied to the roll out of PPR, because to take full advantage of per-segment caching, you need PPR to generate static shells for each segment. However, as I was investigating how to support the incremental PPR opt-in API, where some pages support PPR and others don't — perhaps just by falling back to the old cache implementation — I realized that there are benefits to per-segment caching even if the requests themselves are not per-segment.

For example, when performing a non-PPR-style prefetch, the server diffs the prefetched page against a description of the base page sent by the client. In the old caching implementation, the base tree represented whatever the current page was at the time of the prefetch. In the Segment Cache implementation, we improve on this by telling the server to exclude any shared layouts that are already in the client cache.

This ended up requiring more code than I would have preferred, because dynamic server responses and per-segment server responses have differentformats and constraints. However, I realized I was going to have to implement most of this regardless in order to support <Link prefetch={true}>, which performs a full dynamic request.

Once more of the pieces are settled, we can simplify the implementation by unifying/redesigning some of the data structures, especially FlightRouterState, which has become overloaded with many different concerns. But we need to land some of our experiments first — one reason there's more code than you might expect is because of all the different combinations of features/experiments we need to support simultaneously.

While it's likely that PPR will be enabled by default sometime within the next few release cycles, supporting non-PPR projects means we have a pathway to rolling out additional prefetching improvements (like prioritization and cancellation) independently of the PPR release.

@ijjk
Copy link
Member

ijjk commented Dec 16, 2024

Tests Passed

@ijjk
Copy link
Member

ijjk commented Dec 16, 2024

Stats from current PR

Default Build (Increase detected ⚠️)
General Overall increase ⚠️
vercel/next.js canary acdlite/next.js segment-cache-non-ppr-support Change
buildDuration 18.8s 15.9s N/A
buildDurationCached 15s 12.8s N/A
nodeModulesSize 415 MB 416 MB ⚠️ +727 kB
nextStartRea..uration (ms) 477ms 474ms N/A
Client Bundles (main, webpack) Overall increase ⚠️
vercel/next.js canary acdlite/next.js segment-cache-non-ppr-support Change
1187-HASH.js gzip 51.4 kB 52.4 kB ⚠️ +942 B
8276.HASH.js gzip 169 B 168 B N/A
8377-HASH.js gzip 5.36 kB 5.36 kB N/A
bccd1874-HASH.js gzip 53 kB 53 kB N/A
framework-HASH.js gzip 57.5 kB 57.5 kB N/A
main-app-HASH.js gzip 232 B 236 B N/A
main-HASH.js gzip 34.1 kB 34.1 kB N/A
webpack-HASH.js gzip 1.71 kB 1.71 kB N/A
Overall change 51.4 kB 52.4 kB ⚠️ +942 B
Legacy Client Bundles (polyfills)
vercel/next.js canary acdlite/next.js segment-cache-non-ppr-support Change
polyfills-HASH.js gzip 39.4 kB 39.4 kB
Overall change 39.4 kB 39.4 kB
Client Pages
vercel/next.js canary acdlite/next.js segment-cache-non-ppr-support Change
_app-HASH.js gzip 193 B 193 B
_error-HASH.js gzip 193 B 193 B
amp-HASH.js gzip 512 B 510 B N/A
css-HASH.js gzip 343 B 342 B N/A
dynamic-HASH.js gzip 1.84 kB 1.84 kB
edge-ssr-HASH.js gzip 265 B 265 B
head-HASH.js gzip 363 B 362 B N/A
hooks-HASH.js gzip 393 B 392 B N/A
image-HASH.js gzip 4.49 kB 4.49 kB N/A
index-HASH.js gzip 268 B 268 B
link-HASH.js gzip 2.35 kB 2.34 kB N/A
routerDirect..HASH.js gzip 328 B 328 B
script-HASH.js gzip 397 B 397 B
withRouter-HASH.js gzip 323 B 326 B N/A
1afbb74e6ecf..834.css gzip 106 B 106 B
Overall change 3.59 kB 3.59 kB
Client Build Manifests
vercel/next.js canary acdlite/next.js segment-cache-non-ppr-support Change
_buildManifest.js gzip 749 B 746 B N/A
Overall change 0 B 0 B
Rendered Page Sizes
vercel/next.js canary acdlite/next.js segment-cache-non-ppr-support Change
index.html gzip 522 B 523 B N/A
link.html gzip 538 B 537 B N/A
withRouter.html gzip 519 B 520 B N/A
Overall change 0 B 0 B
Edge SSR bundle Size Overall increase ⚠️
vercel/next.js canary acdlite/next.js segment-cache-non-ppr-support Change
edge-ssr.js gzip 129 kB 129 kB N/A
page.js gzip 204 kB 205 kB ⚠️ +1.01 kB
Overall change 204 kB 205 kB ⚠️ +1.01 kB
Middleware size
vercel/next.js canary acdlite/next.js segment-cache-non-ppr-support Change
middleware-b..fest.js gzip 671 B 668 B N/A
middleware-r..fest.js gzip 155 B 156 B N/A
middleware.js gzip 31.3 kB 31.3 kB N/A
edge-runtime..pack.js gzip 844 B 844 B
Overall change 844 B 844 B
Next Runtimes Overall increase ⚠️
vercel/next.js canary acdlite/next.js segment-cache-non-ppr-support Change
274-experime...dev.js gzip 322 B 322 B
274.runtime.dev.js gzip 314 B 314 B
app-page-exp...dev.js gzip 354 kB 356 kB ⚠️ +1.7 kB
app-page-exp..prod.js gzip 128 kB 129 kB ⚠️ +927 B
app-page-tur..prod.js gzip 141 kB 142 kB ⚠️ +919 B
app-page-tur..prod.js gzip 136 kB 137 kB ⚠️ +926 B
app-page.run...dev.js gzip 344 kB 346 kB ⚠️ +1.72 kB
app-page.run..prod.js gzip 124 kB 125 kB ⚠️ +928 B
app-route-ex...dev.js gzip 37.5 kB 37.5 kB
app-route-ex..prod.js gzip 25.5 kB 25.5 kB
app-route-tu..prod.js gzip 25.5 kB 25.5 kB
app-route-tu..prod.js gzip 25.3 kB 25.3 kB
app-route.ru...dev.js gzip 39.1 kB 39.1 kB
app-route.ru..prod.js gzip 25.3 kB 25.3 kB
pages-api-tu..prod.js gzip 9.69 kB 9.69 kB
pages-api.ru...dev.js gzip 11.6 kB 11.6 kB
pages-api.ru..prod.js gzip 9.68 kB 9.68 kB
pages-turbo...prod.js gzip 21.7 kB 21.7 kB
pages.runtim...dev.js gzip 27.5 kB 27.5 kB
pages.runtim..prod.js gzip 21.7 kB 21.7 kB
server.runti..prod.js gzip 916 kB 916 kB
Overall change 2.42 MB 2.43 MB ⚠️ +7.12 kB
build cache Overall increase ⚠️
vercel/next.js canary acdlite/next.js segment-cache-non-ppr-support Change
0.pack gzip 2.08 MB 2.08 MB ⚠️ +4.55 kB
index.pack gzip 74.2 kB 73.9 kB N/A
Overall change 2.08 MB 2.08 MB ⚠️ +4.55 kB
Diff details
Diff for 1187-HASH.js

Diff too large to display

Diff for main-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: b9cef07

@acdlite acdlite changed the title Segment cache non ppr support [Segment Cache] Support for non-PPR projects/pages Dec 16, 2024
@acdlite acdlite force-pushed the segment-cache-non-ppr-support branch 2 times, most recently from be2d370 to f32d1ef Compare December 16, 2024 22:23
@acdlite acdlite marked this pull request as ready for review December 16, 2024 22:23
/**
* "refresh" and "refetch", despite being similarly named, have different
* semantics:
* - "refetch" is used during a requets to inform the server where rendering
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
* - "refetch" is used during a requets to inform the server where rendering
* - "refetch" is used during a request to inform the server where rendering

* - "refetch" is used during a requets to inform the server where rendering
* should start from.
*
* - "refresh" is used by the client to mark that a segmentshould re-fetch the
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
* - "refresh" is used by the client to mark that a segmentshould re-fetch the
* - "refresh" is used by the client to mark that a segment should re-fetch the

Comment on lines 89 to 91
* TODO: We should rethink the protocol for dynamic requests. It might not
* make for the client to send a FlightRouterState, since this type is
* overloaded with concerns.
Copy link
Member

Choose a reason for hiding this comment

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

👍

Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
* TODO: We should rethink the protocol for dynamic requests. It might not
* make for the client to send a FlightRouterState, since this type is
* overloaded with concerns.
* TODO: We should rethink the protocol for dynamic requests. It might not
* make sense for the client to send a FlightRouterState, since this type is
* overloaded with concerns.

@acdlite acdlite force-pushed the segment-cache-non-ppr-support branch 2 times, most recently from 1d38174 to 52652a3 Compare December 17, 2024 19:26
During a non-PPR prefetch, where we render up to the nearest loading
boundary, we should be able to skip segments that are already cached on
the client. To do this, we need to be able to tell the server that
a segment is already cached while also indicating where the "new" part
of the navigation starts — that is, the part inside the shared layout.

To do this, I added a new kind of marker to FlightRouterState called
"inside-shared-layout". It's similar to the "refresh" marker but has
different semantics. I tried my best to explain how it's used in the
code comments.

This implements the marker but does not yet change any behavior on the
client. I'm submitting this as its own PR to confirm that none of the
existing behavior regresses. I'll follow up with the client changes
in a separate PR.

As another follow-up, we're overdue for a redesign of the protocol for
sending dynamic requests. I'm not sure it makes sense to use the
FlightRouterState type, given that it's overloaded with so many
other concerns.
@acdlite acdlite force-pushed the segment-cache-non-ppr-support branch 3 times, most recently from 378c0a4 to 82e7694 Compare December 18, 2024 01:42
This implements support for the Segment Cache in projects/pages where
PPR is not enabled.

I originally thought the Segment Cache would be tied to the roll out of
PPR, because to take full advantage of per-segment caching, you need
PPR to generate static shells for each segment. However, as I was
investigating how to support the incremental PPR opt-in API, where some
pages support PPR and others don't — perhaps just by falling back to
the old cache implementation — I realized that there are benefits to
per-segment caching even if the requests themselves are not per-segment.

For example, when performing a non-PPR-style prefetch, the server diffs
the prefetched page against a description of the base page sent by the
client. In the old caching implementation, the base tree represented
whatever the current page was at the time of the prefetch. In the
Segment Cache implementation, we improve on this by telling the server
to exclude any shared layouts that are already in the client cache.

This ended up requiring more code than I would have preferred, because
dynamic server responses and per-segment server responses have different
formats and constraints. However, I realized I was going to have to
implement most of this regardless in order to support
<Link prefetch={true}>, which performs a full dynamic request.

Once more of the pieces are settled, we can simplify the implementation
by unifying/redesigning some of the data structures, especially
FlightRouterState, which has become overloaded with many different
concerns. But we need to land some of our experiments first — one
reason there's more code than you might expect is because of all the
different combinations of features/experiments we need to
support simultaneously.

While it's likely that PPR will be enabled by default sometime within
the next few release cycles, supporting non-PPR projects means we have a
pathway to rolling out additional prefetching improvements (like
prioritization and cancellation) independently of the PPR release.
@acdlite acdlite force-pushed the segment-cache-non-ppr-support branch from 82e7694 to b9cef07 Compare December 18, 2024 03:34
@acdlite acdlite merged commit 747fe60 into vercel:canary Dec 18, 2024
103 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants