Skip to content

Format the cached origin stack right away so it doesn't keep caller closures alive - #1275

Open
illia-haidar wants to merge 2 commits into
porsager:masterfrom
illia-haidar:fix-origin-cache-retention
Open

illia-haidar wants to merge 2 commits into
porsager:masterfrom
illia-haidar:fix-origin-cache-retention

Conversation

@illia-haidar

Copy link
Copy Markdown

Fixes #1274

cachedError() caches one Error per tagged-template call site. Until its .stack is read, V8 keeps the structured call sites, and through them the first caller's closure, alive for as long as originCache lives, which is the life of the process. This formats the stack right after creating the error, once per call site, so the cached error only holds a string.

  • Origin text: unchanged. The frames are captured at the same moment as before.
  • Error.stackTraceLimit: restored before the stack is formatted, so a user-defined Error.prepareStackTrace never runs while the limit is temporarily 4.
  • A throwing Error.prepareStackTrace: leaves the error unformatted, as before.
  • The cache: still avoids creating an Error per query (Expensive instantiation of Errors to capture stack traces? #290).

The new test, Origin stack is formatted when a query is created, counts Error.prepareStackTrace calls while a new call site creates its query, before the query runs.

  • It fails without the change (1 != 0).
  • It skips on a runtime that doesn't call a user-defined Error.prepareStackTrace.

Locally, against PostgreSQL 17:

  • tests/index.js (ESM) passes on Node 26.0.0 and 24.22.1, all 265 tests;
  • the CJS build (npm run build:cjs, cjs/tests) passes too;
  • eslint src tests is clean.

cjs/, deno/ and cf/ are left for the build step.

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Bounded memory retention: the cached origin Error keeps the first caller's closure alive for the life of the process

1 participant