SvelteKit adapter-node vs adapter-static when latency only appears under concurrent requests
0 reputation · 14 Jul 2023, 07:03 UTC
0 reputation · 14 Jul 2023, 07:03 UTC
I'm designing a SvelteKit deployment for a page whose load function does a small asynchronous operation (roughly 10 ms). Under single requests both deployment options feel identical, but I expect bursts of around 50 concurrent users hitting the same route, and I want to choose the adapter before load testing rather than after an incident.
The documented trade-off I understand so far: adapter-node executes the server render pipeline per request, so CPU-bound rendering can add latency as concurrency grows, while adapter-static pre-renders at build time and serves flat files, keeping response time roughly constant but removing access to request-specific data like cookies, headers, and dynamic query handling without extra client-side fetching.
What I can't resolve is the decision boundary. Assume a recent SvelteKit 2.x and Node 20 LTS on modest hardware.
32275 reputation · 14 Jul 2023, 07:44 UTC
For a page with a 10ms asynchronous load function, adapter-node will likely handle 50 concurrent users without any perceptible latency increase on modest hardware. Because your operation is asynchronous (I/O-bound), it yields the Node.js event loop, allowing other requests to be processed. The "decision boundary" where SSR latency outweighs flexibility is typically reached only when the CPU-bound portion of rendering (converting the component tree to a string) becomes significant or the event loop is blocked by synchronous code.
The performance difference under load stems from where the work happens:
load function and renders the HTML for every request. While the 10ms async wait is cheap, the CPU must still generate the HTML. If the page is complex, high concurrency can lead to event-loop lag.There is no universal concurrency number because it depends on your component complexity and hardware. To find your specific "knee in the curve," perform a stepped load test:
adapter-node.autocannon to simulate increasing loads:
# Test with 10, 50, and 100 concurrent connections
autocannon -c 50 -d 30 http://localhost:3000/your-route
You do not have to choose an "all-or-nothing" adapter. SvelteKit supports a hybrid approach using adapter-node. You can mark specific routes for prerendering while keeping others dynamic:
// src/routes/my-page/+page.js
export const prerender = true;
export async function load() {
// This runs at build time
}
When using adapter-node, prerendered pages are served as static files, while routes without prerender = true are rendered per request. This allows you to scale the high-traffic, static-capable pages while retaining the ability to use cookies, headers, and dynamic data on other routes.
Svelte's microtask-batched reactivity is a client-side browser behavior. It manages how the DOM updates after the page has loaded. It has no impact on server-side concurrency or the latency of the initial HTML response.
prerender = true on a route, build with adapter-node, and check that the server logs do not show the load function executing upon request.cookies or headers in a prerendered route; they will be unavailable, confirming the need for SSR for request-specific logic.Missing Diagnostic Detail: To provide a more precise estimate of your capacity, could you specify the CPU and memory limits of your target environment (e.g., 1 vCPU / 2GB RAM)?
Use comments to ask for clarification. Post a solution as an answer.
No question comments on this page.