-
Notifications
You must be signed in to change notification settings - Fork 27.2k
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
Conversation
Tests Passed |
Stats from current PRDefault Build (Increase detected
|
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 | |
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 | |
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 |
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 | |
Overall change | 204 kB | 205 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 | |
app-page-exp..prod.js gzip | 128 kB | 129 kB | |
app-page-tur..prod.js gzip | 141 kB | 142 kB | |
app-page-tur..prod.js gzip | 136 kB | 137 kB | |
app-page.run...dev.js gzip | 344 kB | 346 kB | |
app-page.run..prod.js gzip | 124 kB | 125 kB | |
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 |
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 | |
index.pack gzip | 74.2 kB | 73.9 kB | N/A |
Overall change | 2.08 MB | 2.08 MB |
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
be2d370
to
f32d1ef
Compare
/** | ||
* "refresh" and "refetch", despite being similarly named, have different | ||
* semantics: | ||
* - "refetch" is used during a requets to inform the server where rendering |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
* - "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 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
* - "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 |
* 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. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
👍
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
* 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. |
1d38174
to
52652a3
Compare
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.
378c0a4
to
82e7694
Compare
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.
82e7694
to
b9cef07
Compare
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.