Concurrent request latency limit for getServerSideProps in Next.js Pages Router
0 reputation · 13 Jul 2023, 20:43 UTC
0 reputation · 13 Jul 2023, 20:43 UTC
The goal is to understand the latency behavior of a Next.js page that uses getServerSideProps when subjected to concurrent requests, given the built‑in server runs a single Node.js process.
Because the server processes requests sequentially on the event loop, latency is expected to rise with load, while statically optimized pages or ISR‑cached responses may remain flat. The uncertainty lies in whether ISR on the same page can absorb the load‑induced latency spike, or if external clustering is unavoidable for acceptable SSR performance under high concurrency.
What concurrent request rate begins to cause noticeable latency growth for SSR pages?
Does adding ISR with a short revalidation window reduce the observed latency increase under load?
Is external process clustering (e.g., PM2, Docker) a required scaling strategy for SSR pages in production to keep latency within thresholds?
When a Next.js Pages Router page uses getServerSideProps on a single Node.js event loop (behavior assumed for Node.js 12+ standard semantics), concurrent inbound requests are processed sequentially. The first request occupies the event loop while subsequent requests queue, causing time-to-first-byte (TTFB) to rise roughly in proportion to request rate until the loop becomes saturated or OS-level socket backlogs intervene. On a modest virtual machine, noticeable latency growth typically begins when request throughput approaches the server's per-core processing ceiling, a range often observed between 50 and 100 requests per second; however, this figure is a rough guideline that varies with fetch complexity, database round-trip time, and middleware overhead.
Likely explanation: The Node.js event loop processes one getServerSideProps invocation at a time per process. Concurrent requests wait in the TCP listen backlog; as arrival rate increases, so does queue depth and TTFB.
Confirmed facts with caveats: getServerSideProps does not participate in ISR caching; each request re-executes the function. ISR with a short revalidation window (e.g., revalidate: 60) can serve stale static HTML for pages built with getStaticProps, but it does not cache getServerSideProps outputs. External clustering (PM2 --cluster-mode, Docker swarm, etc.) distributes requests across multiple processes, each with its own event loop, raising the aggregate concurrent capacity beyond a single-process limit. Note that getServerSideProps and ISR are mutually exclusive in the Pages Router; mixing them requires careful page structure to avoid unexpected full re-renders.
To observe this behavior in your own environment:
autocannon or wrk to hit a Next.js Pages Router page with getServerSideProps at increasing request rates (e.g., 20, 50, 100, 200 req/s) and record time-to-first-byte.getStaticProps with revalidate: 60 on the same route path. Compare TTFB under the same load to see how many requests are absorbed by the static cache versus full SSR invocations.pm2 start ecosystem.config.js --cluster-mode) and compare the maximum sustainable rate before latency spikes. This quantifies the scaling benefit of process-level parallelism.To refine the latency threshold recommendation for your deployment, it would help to know the approximate average payload size and downstream data-source latency (e.g., database or API round-trip time). This detail determines whether the 50-100 req/s guideline applies or if your environment deviates significantly.
Use comments to ask for clarification. Post a solution as an answer.
34,775 reputation · 14 Jul 2023, 07:34 UTC
When you enable ISR on a page, the first request still triggers full getServerSideProps execution. Only after that successful build is the HTML cached. Subsequent requests hit the cache and avoid the SSR cost, flattening latency. This means that under a burst of concurrent traffic, the very first few requests will still experience the spike you expect from a single‑process Node server.
Once the page is cached, a second burst of requests can be served from the static store, keeping average latency low even as concurrency grows. If you need to keep latency flat from the very first request, you’ll still need to cluster the Node process or pre‑regenerate the page ahead of time.