Skip to content

Fix weakMapMemoize maxSize counting for argument tuples - #831

Open
Nelmad wants to merge 2 commits into
reduxjs:masterfrom
Nelmad:bugfix/weak-map-cache-count
Open

Nelmad wants to merge 2 commits into
reduxjs:masterfrom
Nelmad:bugfix/weak-map-cache-count

Conversation

@Nelmad

@Nelmad Nelmad commented Nov 8, 2026

Copy link
Copy Markdown

With maxSize: 2, calling a memoized function with (10, 1) and (11, 2) over three passes currently computes six times and returns new references. Each new primitive argument node consumes capacity, so generations rotate before two results fit. This change computes twice and preserves both references.

Count each successfully cached or promoted argument tuple containing a primitive once per generation. Current-generation hits and failed calls do not consume capacity; separate tuples still count separately when resultEqualityCheck reuses a result reference. Object-only calls continue to rely on WeakMap garbage collection.

Bounded caches publish new paths only after callbacks succeed, preventing failed calls from retaining primitive branches. Regression tests cover shared prefixes, reference stability, promotion and eviction, mixed object/primitive arguments and garbage collection, maxSize: 1, clearing, exceptions, result equality, and reentrant callbacks. The default unbounded traversal stays separate to preserve its hot path.

Validation on macOS arm64 with Node 24.22.1 and pnpm 11.23.0:

  • pnpm test: 162 runtime tests passed, with one existing skip and three existing todos; 29 type tests passed, plus the loose-null-checks compiler run.
  • pnpm lint, pnpm format:check, and git diff --check passed.
  • pnpm build passed. Smoke checks passed for all five generated JavaScript bundles and both CJS entry modes; a consumer of the generated declarations compiled successfully.

Paired local production benchmarks against the base branch found no confirmed slowdown in the tested default-path cases. Bounded-path microbenchmarks with matching computation counts measured approximately 14% slower single-primitive hits, 29–44% slower promotions, and 12–20% slower misses (roughly 2–24 ns per call on the test machine). These synthetic measurements capture the cost of deferred writes and are not application-level latency estimates.

Fixes #829

@netlify

netlify Bot commented Nov 8, 2026 •

Copy link
Copy Markdown

✅ Deploy Preview for reselect-docs canceled.

Name Link
🔨 Latest commit f22150c
🔍 Latest deploy log https://app.netlify.com/projects/reselect-docs/deploys/6af03e491c279eb6a4b3ebd3

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.

weakMapMemoize maxSize counts primitive arguments, not results, so multi-argument caches never hit

1 participant