You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
### Problem
Both `polyfillTags` and `traceMetaTags` are only rendered if they
haven't already been flushed, however the current check to skip
rendering (lines 81-89) doesn't take this into account – only the render
logic does (lines 93-98).
This results in `renderToReadableStream` being called (potentially
thousands of times – [for each
chunk](https://github.com/vercel/next.js/blob/498349c375e2602f526f64e8366992066cfa872c/packages/next/src/server/stream-utils/node-web-streams-helper.ts#L368)
in the [head insertion transform stream in the transformer
chain](https://github.com/vercel/next.js/blob/498349c375e2602f526f64e8366992066cfa872c/packages/next/src/server/stream-utils/node-web-streams-helper.ts#L834)),
constructing new `ReadableStream`s each time, just to render empty
strings.
It's present in canary/v16 and I suspect it was introduced in v15 (so
can be backported). The logic check for v14 looks correct.
### Solution
This PR ensures the shortcut check lines up with the render logic by
just zeroing tag arrays after they've been used. There are a number of
ways to achieve the same result – including just adding the
`hasFlushedInitially` check to the shortcut logic – however this would
result in the logic still being split in two places, so I've chosen one
way to ensure the logic is centralized. Happy with whatever method is
preferred here.
### Results
This results in a 10-38% perf improvement end-to-end, especially for
large renders. Eg, in [theo's
benchmark](https://github.com/t3dotgg/cf-vs-vercel-bench/tree/main/next-bench)
it brings the number of `renderToReadableStream` calls from 1745 here
down to just 1, which has a noticeable impact on end-to-end performance
I've [created a repo to highlight/reproduce the
problem](https://github.com/mhart/next-server-inserted-fix) based off
that benchmark. The results from there:
- Node.js: 1.18x faster
- Deno: 1.38x faster
- workerd: 1.24x faster
- Bun: 1.15x faster
---------
Co-authored-by: Josh Story <story@hey.com>
at RenderFromTemplateContext (bundler:///<next-src>)
3253
3253
at OuterLayoutRouter (bundler:///<next-src>)
3254
-
339 | */
3255
-
340 | function InnerLayoutRouter({
3256
-
> 341 | tree,
3254
+
332 | */
3255
+
333 | function InnerLayoutRouter({
3256
+
> 334 | tree,
3257
3257
| ^
3258
-
342 | segmentPath,
3259
-
343 | debugNameContext,
3260
-
344 | cacheNode,
3258
+
335 | segmentPath,
3259
+
336 | debugNameContext,
3260
+
337 | cacheNode,
3261
3261
To get a more detailed stack trace and pinpoint the issue, start the app in development mode by running \`next dev\`, then open "/use-cache-private-without-suspense" in your browser to investigate the error.
0 commit comments