Skip to content

perf: replace delete with = undefined in request hot paths#91560

Open
benfavre wants to merge 2 commits intovercel:canaryfrom
benfavre:perf/avoid-delete-shape-mutation
Open

perf: replace delete with = undefined in request hot paths#91560
benfavre wants to merge 2 commits intovercel:canaryfrom
benfavre:perf/avoid-delete-shape-mutation

Conversation

@benfavre
Copy link
Contributor

@benfavre benfavre commented Mar 18, 2026

Summary

Replace delete obj.prop with obj.prop = undefined in server request-handling hot paths where the object is not exposed to user code, to preserve V8 hidden classes.

Problem

The delete operator changes an object's hidden class (shape), forcing V8 into slower dictionary mode. TanStack Start saw >50% CPU time reduction in affected methods.

Changes

  • strip-flight-headers.ts: Flight headers cleared with = undefined (safe: IncomingHttpHeaders not exposed to user iteration)
  • base-server.ts: NEXT_URL header cleared with = undefined (safe: internal header)
  • request-meta.ts: removeRequestMeta uses = undefined (safe: internal metadata accessed only via getRequestMeta())

NOT changed (intentionally)

Query object keys (_rsc, nextInternalLocale, __next_* prefixed params) must use delete because the query object is exposed to user code. Setting to undefined would leak internal keys via Object.keys(), for...in, and Object.entries().

Test plan

  • Verify RSC requests work correctly (flight headers stripped)
  • Verify request metadata removal works
  • No regressions in SSR rendering

Generated with Claude Code

@nextjs-bot
Copy link
Collaborator

Allow CI Workflow Run

  • approve CI run for commit: 159ff24

Note: this should only be enabled once the PR is ready to go and can only be enabled by a maintainer

@nextjs-bot
Copy link
Collaborator

nextjs-bot commented Mar 18, 2026

Allow CI Workflow Run

  • approve CI run for commit: 771818b

Note: this should only be enabled once the PR is ready to go and can only be enabled by a maintainer

…classes

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@benfavre benfavre force-pushed the perf/avoid-delete-shape-mutation branch from 159ff24 to 02e6340 Compare March 18, 2026 09:36
@benfavre benfavre changed the title perf(server): avoid delete on hot-path objects to preserve V8 hidden classes perf: replace delete with = undefined in request hot paths Mar 18, 2026
…ects

The query object is exposed to user code (getServerSideProps, middleware, etc.).
Setting internal keys to undefined instead of deleting them would leak
those keys via Object.keys(), for...in, and Object.entries() — even though
the values are undefined. Only keep the change for internal-only objects
(request headers, request metadata) where enumeration isn't observable.
benfavre added a commit to benfavre/next.js that referenced this pull request Mar 18, 2026
The delete→undefined changes for NEXT_URL header and request-meta are
already in PR vercel#91560. Keep only the changes unique to this PR:
- parsedUrl.query fresh object replacement (avoids N delete operations)
- loadable timer guards during SSR
@benfavre
Copy link
Contributor Author

Test Verification

  • server-utils.test.ts: 4/4 passed
  • request-meta: consumers use getRequestMeta(req, key) which returns meta[key] — undefined is equivalent to missing

All tests run on the perf/combined-all branch against canary. Total: 203 tests across 13 suites, all passing.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants