perf(router-core): cache static SSR manifest serialization in LRU - #8144
perf(router-core): cache static SSR manifest serialization in LRU#8144Sheraff wants to merge 2 commits into
Conversation
🚀 Changeset Version Preview4 package(s) bumped directly, 19 bumped as dependents. 🟩 Patch bumps
|
|
View your CI Pipeline Execution ↗ for commit 89b6860
☁️ Nx Cloud last updated this comment at |
|
Important Review skippedDraft detected. Please check the settings in the CodeRabbit UI or the ⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Pro Plus Run ID: You can disable this status message by setting the Use the checkbox below for a quick retry:
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
Bundle Size BenchmarksThis pull request does not affect bundle size in any measured scenario. |
There was a problem hiding this comment.
Important
At least one additional CI pipeline execution has run since the conclusion below was written and it may no longer be applicable.
Nx Cloud is proposing a fix for your failed CI:
We fixed a bug in createSerializedManifestFragment where the hasEntries flag was not updated after adding the inlineCssHrefs entry, and no comma guard existed before appending "routes":, producing malformed JS like {"scriptFormat":"esm""routes":...}. This invalid script caused a parse error on every hydrated page, breaking SSR/RSC hydration across all E2E suites. Adding hasEntries = true after the inlineCssHrefs block and a guarded comma before "routes": restores valid object literal output in all field combinations.
Note
⏳ We are verifying this fix by re-running a subset of the 20 failed tasks that were analyzed.
diff --git a/packages/router-core/src/ssr/ssr-server.ts b/packages/router-core/src/ssr/ssr-server.ts
index ee4cb579..5886823b 100644
--- a/packages/router-core/src/ssr/ssr-server.ts
+++ b/packages/router-core/src/ssr/ssr-server.ts
@@ -273,6 +273,10 @@ function createSerializedManifestFragment(
head += ','
}
head += '"inlineStyle":{"attrs":{"suppressHydrationWarning":true}}'
+ hasEntries = true
+ }
+ if (hasEntries) {
+ head += ','
}
head += '"routes":'
Or Apply changes locally with:
npx nx-cloud apply-locally E8np-RSDj
Apply fix locally with your editor ↗ View interactive diff ↗
🎓 Learn more about Self-Healing CI on nx.dev
Merging this PR will degrade performance by 3.03%
|
| Mode | Benchmark | BASE |
HEAD |
Efficiency | |
|---|---|---|---|---|---|
| ❌ | Memory | mem server error-paths unmatched (react) |
544.1 KB | 957.8 KB | -43.19% |
| ❌ | Memory | mem server error-paths unmatched (solid) |
559.8 KB | 961.6 KB | -41.78% |
| ❌ | Memory | mem server error-paths redirect (vue) |
427.7 KB | 733.2 KB | -41.66% |
| ❌ | Memory | mem server error-paths not-found (solid) |
557.9 KB | 950.3 KB | -41.29% |
| ❌ | Memory | mem server error-paths unmatched (vue) |
577.6 KB | 956.5 KB | -39.61% |
| ❌ | Memory | mem server serialization-payload (solid) |
4.4 MB | 4.7 MB | -5.3% |
| ❌ | Memory | mem client navigation-churn (solid) |
592.5 KB | 623.3 KB | -4.93% |
| ❌ | Memory | mem server serialization-payload (vue) |
4.5 MB | 4.7 MB | -4.75% |
| ❌ | Simulation | ssr assets linked-css control (react) |
369.1 ms | 383.1 ms | -3.66% |
| ❌ | Memory | mem server request-churn (react) |
662.6 KB | 687.3 KB | -3.6% |
| ❌ | Memory | mem client interrupted-navigations (vue) |
368.7 KB | 382.1 KB | -3.5% |
| ❌ | Simulation | ssr server-fn not-found (vue) |
290.4 ms | 300.2 ms | -3.28% |
| ⚡ | Memory | mem server error-paths not-found (vue) |
2,304.3 KB | 475.9 KB | ×4.8 |
| ⚡ | Memory | mem server aborted-requests (vue) |
1.1 MB | 1 MB | +8.28% |
| ⚡ | Simulation | ssr dehydrate plain control (vue) |
602.8 ms | 560.8 ms | +7.49% |
| ⚡ | Simulation | ssr server-fn during document ssr (vue) |
674.5 ms | 627.5 ms | +7.48% |
| ⚡ | Simulation | ssr dehydrate rich types (vue) |
531.8 ms | 500.3 ms | +6.3% |
| ⚡ | Simulation | ssr before-load chain (react) |
217.7 ms | 205.3 ms | +6.04% |
| ⚡ | Memory | mem server error-paths redirect (react) |
318.9 KB | 303.3 KB | +5.15% |
| ⚡ | Simulation | ssr not-found (solid) |
238.4 ms | 226.8 ms | +5.09% |
| ... | ... | ... | ... | ... | ... |
ℹ️ Only the first 20 benchmarks are displayed. Go to the app to view all benchmarks.
Tip
Investigate this regression by commenting @codspeedbot fix this regression on this PR, or directly use the CodSpeed MCP with your agent.
Comparing perf/task3-ssr-manifest-cache (89b6860) with main (d83a896)
Summary
dehydrate()ran serval/crossSerializeStreamover the payload including the static route manifest (preparedManifest.routes) on every request, though it is byte-identical per matched-route set.lru-cache.ts) and emitted as its own$_TSR.router.manifest=…script immediately after the initial$_TSR.router=chunk (ScriptBuffer preserves order; client hydrate() reads it after all scripts execute). Per-request serialization covers only matches + dehydratedData.Performance
51 matched routes × 10 assets (~76KB payload), timing dehydrate + script take only:
Verification
ssr-server-manifest-cache.test.ts(6 tests): cache-hit ≡ uncached hydration result, byte-identical repeat requests, emission ordering, request-assets merge over cache, LRU eviction correctness/boundedness.window.scrollTounhandled errors reproduce identically on pristine main.Risks
Dev-mode HMR manifest mutations aren't cached (intended); adapters baking per-request state into static-route bytes would be wrong (none exist today).