perf: cache runtime checks and IncrementalCache handler for static serving#91604
Open
benfavre wants to merge 1 commit intovercel:canaryfrom
Open
perf: cache runtime checks and IncrementalCache handler for static serving#91604benfavre wants to merge 1 commit intovercel:canaryfrom
benfavre wants to merge 1 commit intovercel:canaryfrom
Conversation
…r static serving path Three optimizations targeting the static page serving hot path: 1. **Cache `process.env.NEXT_RUNTIME` in helpers.ts**: The `isNodeNextRequest`, `isNodeNextResponse`, `isWebNextRequest`, and `isWebNextResponse` guards checked `process.env.NEXT_RUNTIME` on every call. `process.env` access in Node.js is not free (property lookup on a special object). These are called 5+ times per request. Now cached at module level since the runtime never changes after process start. 2. **Cache CacheHandler resolution in next-server.ts**: `getIncrementalCache()` previously resolved the CacheHandler class via dynamic import and called `loadCustomCacheHandlers()` on every request. The resolved class never changes, so we cache the resolution promise (also preventing duplicate imports from concurrent requests). The IncrementalCache instance itself is still created per-request since it derives request-specific state from headers (isOnDemandRevalidate, revalidatedTags). 3. **Cache env lookups in IncrementalCache**: `NEXT_PRIVATE_TEST_PROXY` and `__NEXT_TEST_MAX_ISR_CACHE` were read from `process.env` in the constructor on every request. Now cached as static class properties since these values never change at runtime. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Collaborator
|
Allow CI Workflow Run
Note: this should only be enabled once the PR is ready to go and can only be enabled by a maintainer |
Contributor
Author
Performance ImpactProfiling setup: Node.js v25.7.0, Context: Serving a pre-rendered static page on Next.js takes 5.8ms vs 0.02ms on bare HTTP (49,533 vs 7,648 req/s). This PR targets three sources of per-request overhead in the static serving path. Optimization 1: Cache
Optimization 2: Cache CacheHandler resolution
Optimization 3: Cache
Test Verification
|
Contributor
Author
Test Verification
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Three targeted optimizations for the static page serving hot path, reducing per-request overhead from repeated environment variable lookups and unnecessary dynamic imports.
Cache
process.env.NEXT_RUNTIMEinhelpers.ts--isNodeNextRequest/isNodeNextResponse/isWebNextRequest/isWebNextResponsecheckedprocess.env.NEXT_RUNTIMEon every call (5+ times per request).process.envaccess in Node.js involves a property lookup on a special object that is not free. Now cached at module level since the runtime never changes after process start. Follows existing precedent inuse-cache-wrapper.tswhich already doesconst isEdgeRuntime = process.env.NEXT_RUNTIME === 'edge'.Cache CacheHandler class resolution in
next-server.ts--getIncrementalCache()resolved the CacheHandler class viadynamicImportEsmDefault()and calledloadCustomCacheHandlers()on every request, even though the resolved class never changes. Now cached as a promise (also preventing duplicate imports from concurrent requests during startup). TheIncrementalCacheinstance itself is still created per-request since it derives request-specific state from headers (isOnDemandRevalidate,revalidatedTags).Cache env lookups in
IncrementalCacheconstructor --NEXT_PRIVATE_TEST_PROXYand__NEXT_TEST_MAX_ISR_CACHEwere read fromprocess.envin the constructor on every request. Now cached as static class properties, following the existing pattern used by thedebugproperty.Files changed
packages/next/src/server/base-http/helpers.tspackages/next/src/server/next-server.tspackages/next/src/server/lib/incremental-cache/index.tsTest plan
isNodeNextRequest/isNodeNextResponsestill return correct values in Node runtimeisWebNextRequest/isWebNextResponsestill return correct values in Edge runtimecacheHandlerconfig still resolves correctly on first requestloadCustomCacheHandlersstill called (via the cached promise path)__NEXT_TEST_MAX_ISR_CACHEandNEXT_PRIVATE_TEST_PROXYwhen set before module load🤖 Generated with Claude Code