Skip to content

fix: Per-entry cache TTL is ignored by the in-memory cache adapter - #10671

Open
mtrezza wants to merge 1 commit into
parse-community:alphafrom
mtrezza:refactor/lru-cache-11.5.2
Open

mtrezza wants to merge 1 commit into
parse-community:alphafrom
mtrezza:refactor/lru-cache-11.5.2

Conversation

@mtrezza

@mtrezza mtrezza commented Sep 22, 2026

Copy link
Copy Markdown
Member

Issue

Two related defects in the in-memory cache adapter, plus the lru-cache upgrade that makes one of them fatal.

1. Per-entry TTL has never been applied. LRUCache.put() passed ttl as lru-cache's third positional argument, but that slot is an options object. Destructuring a number yields undefined for every option, so the per-entry TTL was silently discarded and the cache-wide TTL always won. Verified on the currently pinned 11.2.7:

new LRUCache({ max: 10, ttl: 5000 }).set('a', 1, 60000)
// -> stored entry has ttl: 5000, not 60000

The practical effect is that RedisCacheAdapter honours per-entry TTLs while InMemoryCacheAdapter ignores them, so the two adapters disagree. For example ParseGraphQLController caches its config with put(configCacheKey, graphQLConfig, 60000) but in-memory it actually expires after cacheTTL (5s by default).

2. this.ttl was never assigned. The default parameter put(key, value, ttl = this.ttl) read a property the constructor never set, so it was always undefined.

3. lru-cache@11.3.0 turns defect 1 into a hard failure. In an undocumented change (absent from the upstream CHANGELOG), set(), get(), has(), peek(), fetch(), forceFetch() and memo() now write back onto the caller-supplied options object:

set(k, v, setOptions = {}) {
  const { status = ... } = setOptions
  setOptions.status = status   // 11.2.7 only destructured, never wrote

Passing a non-object therefore throws. Bisected: 11.2.7 OK, 11.3.011.5.3 throw TypeError: Cannot create property 'status' on number '60000'. This is why the Dependabot bump (#10596) fails every CI job.

Two call sites passed a positional number, both on default code paths:

  • src/Adapters/Cache/LRUCache.js — reached from ParseGraphQLController._putCachedGraphQLConfig through the default InMemoryCacheAdapter.
  • src/LiveQuery/ParseLiveQueryServer.ts — the invalid-session-token negative cache, which fires whenever a LiveQuery client presents an INVALID_SESSION_TOKEN (the path covered by the spec for GHSA-2xm2-xj2q-qgpj).

This PR supersedes #10632, whose LRUCache.js fix it adopts, and #10596.

Closes #10596
Closes #10632

Approach

Fix both call sites to pass an options object, assign this.ttl in the constructor, and bump lru-cache to 11.5.2.

LRUCache.put() now maps the TTL explicitly:

  • Infinity -> 0, which is how lru-cache expresses "never expires", matching RedisCacheAdapter's Infinity handling.
  • any other positive finite number -> forwarded as { ttl }.
  • anything else -> undefined, which is how lru-cache expresses "use the cache-wide TTL".

Infinity is deliberately not forwarded as-is. Node cannot express it as a timer duration: under ttlAutopurge it emits TimeoutOverflowWarning: Infinity does not fit into a 32-bit signed integer and clamps the timer to 1ms, so the purge timer re-fires every millisecond.

ParseLiveQueryServer now passes { ttl: this.config.cacheTimeout }. That one is behaviour-preserving by construction: authCache is built with ttl: config.cacheTimeout, so the explicit value equals the cache-wide one.

Behaviour change

Per-entry TTLs now take effect in the in-memory adapter. The most visible consequence is that the GraphQL config cache honours its intended 60000 instead of falling back to cacheTTL (5s by default), bringing it in line with the Redis adapter.

Breaking Changes

None.

Tests

Adds specs covering per-entry TTL longer than the cache TTL, shorter than the cache TTL, Infinity, a non-numeric TTL, and an omitted TTL. Four of them fail against the unfixed adapter on 11.5.2 with the same TypeError seen in CI, and all pass with the fix.

Verified locally: InMemoryCacheAdapter 8/8, CacheController 5/5, ParseGraphQLController 25/25, ParseLiveQuery 56/56, lint clean.

Known adapter difference, not changed here

RedisCacheAdapter treats ttl === 0 as "do not cache at all" (it returns before writing), whereas the in-memory adapter treats 0 as "not a positive TTL" and falls back to the cache-wide value. This PR keeps the existing in-memory behaviour rather than widening scope; worth a follow-up if full parity is wanted.

Tasks

  • Add tests — per-entry TTL behaviour in spec/InMemoryCacheAdapter.spec.js

Summary by CodeRabbit

  • Bug Fixes

    • Cache entries now respect their individual expiration settings, including durations longer than the default cache duration.
    • Entries with no valid individual duration use the configured default; entries set to never expire remain cached indefinitely.
    • Invalid session-token results now use the server’s configured cache duration.
  • Maintenance

    • Updated the caching component to a newer version.

@parse-github-assistant

Copy link
Copy Markdown

🚀 Thanks for opening this pull request! We appreciate your effort in improving the project. Please let us know once your pull request is ready for review.

Tip

  • Keep pull requests small. Large PRs will be rejected. Break complex features into smaller, incremental PRs.
  • Use Test Driven Development. Write failing tests before implementing functionality. Ensure tests pass.
  • Group code into logical blocks. Add a short comment before each block to explain its purpose.
  • We offer conceptual guidance. Coding is up to you. PRs must be merge-ready for human review.
  • Our review focuses on concept, not quality. PRs with code issues will be rejected. Use an AI agent.
  • Human review time is precious. Avoid review ping-pong. Inspect and test your AI-generated code.

Note

Please respond to review comments from AI agents just like you would to comments from a human reviewer. Let the reviewer resolve their own comments, unless they have reviewed and accepted your commit, or agreed with your explanation for why the feedback was incorrect.

Caution

Pull requests must be written using an AI agent with human supervision. Pull requests written entirely by a human will likely be rejected, because of lower code quality, higher review effort and the higher risk of introducing bugs. Please note that AI review comments on this pull request alone do not satisfy this requirement. Our CI and AI review are safeguards, not development tools. If many issues are flagged, rethink your development approach. Invest more effort in planning and design rather than using review cycles to fix low-quality code.

@coderabbitai

coderabbitai Bot commented Sep 22, 2026

Copy link
Copy Markdown

Review in Change Stack →

Navigate logical layers of code changes, visualize relationships, and explore their blast radius.

Note

Currently processing new changes in this PR. This may take a few minutes, please wait...

⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Essentials

Run ID: ee12c6a2-7609-4917-923c-4b75803924ab

📥 Commits

Reviewing files that changed from the base of the PR and between 14ed717 and 408303a.

📒 Files selected for processing (1)
  • package-lock.json

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Essentials

Run ID: f6b446f3-7d5d-4fa4-b210-240ccc2f2272

📥 Commits

Reviewing files that changed from the base of the PR and between 46ecb8f and 14ed717.

📒 Files selected for processing (3)
  • package-lock.json
  • spec/InMemoryCacheAdapter.spec.js
  • src/Adapters/Cache/LRUCache.js

Included review availability: Your plan provides up to 10 included reviews per hour; 8 remain after this review.


📝 Walkthrough

Walkthrough

The PR upgrades lru-cache from 11.2.7 to 11.5.2 and updates cache calls to use its options-object TTL API. LRUCache now handles per-entry TTL values alongside the configured cache TTL. Tests cover longer, shorter, infinite, non-positive, and omitted entry TTLs.

Changes

LRU Cache TTL Handling

Layer / File(s) Summary
Dependency and cache TTL behavior
package.json, package-lock.json, src/Adapters/Cache/LRUCache.js, spec/InMemoryCacheAdapter.spec.js
The dependency and lockfile move to lru-cache 11.5.2. LRUCache passes TTL through the options object, maps Infinity to zero, passes positive finite TTLs through, and uses the configured cache TTL for other values. Tests cover these TTL behaviors.
Live Query cache integration
src/LiveQuery/ParseLiveQueryServer.ts
Invalid-session-token cache entries pass cacheTimeout through the LRU options object.

Priority: ⬇️ Low

Estimated code review effort: 2 (Simple) | ~15 minutes

Merge Risk: ⚪ Minimal · up to 14ed7

The described upgrade updates the cache calls and covers the relevant TTL cases; no specific user-impacting issue requiring resolution before merge is established.


Important

Pre-merge checks failed

Please resolve all errors before merging. Addressing warnings is optional.

❌ Failed checks (2 warnings, 1 inconclusive)

Check name Status Explanation Resolution
Linked Issues check ⚠️ Warning [#10596] requires the lru-cache upgrade and compatibility without changing the affected cache flows. The dependency is upgraded to 11.5.2, and ParseLiveQueryServer now passes an options object wit… Keep numeric LRUCache.put() arguments from changing the effective cache-wide TTL. Update the adapter and its tests to avoid forwarding those values as per-entry TTLs, while retaining the options-object fix for the LiveQuery call.
Out of Scope Changes check ⚠️ Warning The whole-PR diff also changes unrelated js-yaml entries in package-lock.json: versions 3.14.2 to 3.15.2 and 4.1.1 to 4.3.2. These updates do not support the lru-cache upgrade in [#10596]. Remove the unrelated js-yaml lockfile updates, or provide evidence that they are required for the [#10596] dependency upgrade.
Engage In Review Feedback ❓ Inconclusive The available review metadata reports zero actionable findings in the current review. It also states that this does not establish whether earlier posted comments are absent or resolved. The checkout c… Provide the earlier review comments and their discussion, resolution, and related commit history. Then verify that each item was addressed through a commit or that the reviewer retracted it after discussion.
✅ Passed checks (4 passed)
Check name Status Explanation
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check. Docstring coverage is scoped to functions touched by this diff. Analyzed 0 functions across 3…
Security Check ✅ Passed No security vulnerability is evident in the changed code. The lockfile pins lru-cache 11.5.2, and the version-specific OSV query returned no advisories for that package version. The new TTL handling r…
Title check ✅ Passed The title uses the required fix: prefix and clearly describes the per-entry cache TTL issue addressed by the changes.
Description check ✅ Passed The description explains the issue, approach, behavior change, breaking changes, and tests. It includes the required Issue, Approach, and Tasks sections. It omits the template’s Pull Request section a…
Full details: Linked Issues check

Explanation

[#10596] requires the lru-cache upgrade and compatibility without changing the affected cache flows. The dependency is upgraded to 11.5.2, and ParseLiveQueryServer now passes an options object with the configured TTL. However, LRUCache.put() now converts a numeric TTL into { ttl }, and the new tests require that TTL to override the cache-wide TTL. The prior effective behavior used the cache-wide TTL because the numeric third argument was discarded. This changes cache expiry behavior instead of preserving it.

Full details: Engage In Review Feedback

Explanation

The available review metadata reports zero actionable findings in the current review. It also states that this does not establish whether earlier posted comments are absent or resolved. The checkout contains no review-discussion export, so it cannot show whether earlier feedback received discussion and either a commit or reviewer retraction.

  • Fix all pre-merge checks with AI
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create a new PR

Comment @coderabbitai help to get the list of available commands.

coderabbitai[bot]
coderabbitai Bot previously approved these changes Sep 22, 2026
@codecov

codecov Bot commented Sep 22, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 93.82%. Comparing base (7cac84a) to head (14ed717).

Additional details and impacted files
@@           Coverage Diff           @@
##            alpha   #10671   +/-   ##
=======================================
  Coverage   93.82%   93.82%           
=======================================
  Files         192      192           
  Lines       16875    16882    +7     
  Branches      252      252           
=======================================
+ Hits        15833    15840    +7     
  Misses       1020     1020           
  Partials       22       22           

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@mtrezza
mtrezza force-pushed the refactor/lru-cache-11.5.2 branch from 46ecb8f to 14ed717 Compare September 22, 2026 22:28
@mtrezza mtrezza changed the title refactor: Bump lru-cache from 11.2.7 to 11.5.2 fix: Per-entry cache TTL is ignored by the in-memory cache adapter Sep 22, 2026
@mtrezza
mtrezza force-pushed the refactor/lru-cache-11.5.2 branch from 14ed717 to 408303a Compare September 22, 2026 23:11

This branch has not been deployed

No deployments
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.

1 participant