Skip to content

fix(live-query): clone results on cache read when cache: 'cloned' is used (issue #2309) - #2310

Merged
dfahlander merged 5 commits into
masterfrom
liz/issue-2309-useLiveQuery-cache-cloned
Jun 16, 2026
Merged

fix(live-query): clone results on cache read when cache: 'cloned' is used (issue #2309)#2310
dfahlander merged 5 commits into
masterfrom
liz/issue-2309-useLiveQuery-cache-cloned

Conversation

@liz709

@liz709 liz709 commented Jun 9, 2026

Copy link
Copy Markdown
Contributor

Closes #2309. This PR ensures that query results retrieved from the query cache are deeply cloned on every read if the 'cloned' cache option is used. This fixes a regression where React hooks like useLiveQuery did not trigger a re-render when mutating a returned object in-place and putting it back to the database, as well as protecting the cache against direct modifications by the user.

Summary by CodeRabbit

  • Bug Fixes

    • Improved live-query caching to better control how optimistic results are prepared and returned, preventing unintended mutations when result freezing is disabled.
  • Tests

    • Updated and modernized the live-query test suite, including refactored helpers, a more structured results/mutation matrix runner with reliable coordination and cleanup, and refreshed regression coverage for optimistic updates, transactions, error cases, and cache/mutation isolation behaviors.

@coderabbitai

coderabbitai Bot commented Jun 9, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 079af112-77cd-4d75-87c6-6f63b5094c96

📥 Commits

Reviewing files that changed from the base of the PR and between 9f7f313 and 4815f18.

📒 Files selected for processing (1)
  • src/live-query/cache/cache-middleware.ts

📝 Walkthrough

Walkthrough

Defers result cloning from write-path to read-path in cache-middleware to fix Issue 2309 (useLiveQuery not detecting in-place mutations); updates liveQuery tests with Promise.all shape modernization and adds regression test for cloned/disabled cache modes.

Changes

Live Query Optimistic Result Fix

Layer / File(s) Summary
Lazy-cloning strategy for cached results
src/live-query/cache/cache-middleware.ts
Write-path stops deep-cloning res.result when caching (relies on later read-path cloning); read-path captures applyOptimisticOps output to local result and deep-clones before returning unless freezeResults is enabled, deferring immutability checks to the return boundary.
Test updates and Issue 2309 regression
test/tests-live-query.js
Modernizes Promise.all call shape, allows reassignment of actualResults in matrix runner, and adds Issue 2309 regression test validating that in-place-mutated objects emit new references on next liveQuery emission across cloned and disabled cache modes.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related PRs

  • dexie/Dexie.js#2268: Also modifies src/live-query/cache/cache-middleware.ts optimistic-update handling and mutation paths involving BlobRef resolution.

Suggested reviewers

  • dfahlander

Poem

🐰 I hopped through queries, cloning peek and deep,
No stale object shadows now disturb your sleep.
Tests hum like meadows, signals rise and fall,
Emissions chase mutations, catching one and all.
🥕

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately describes the main fix: cloning results on cache read when cache: 'cloned' is used, directly referencing issue #2309.
Linked Issues check ✅ Passed The code changes directly address issue #2309 by implementing deep cloning of cached query results on the read-path when optimistic updates are applied, ensuring React hooks detect mutations.
Out of Scope Changes check ✅ Passed Changes are tightly scoped: cache-middleware.ts optimizes cloning behavior, and test file updates comprehensively validate the fix including regression tests for issue #2309.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch liz/issue-2309-useLiveQuery-cache-cloned

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

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

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@test/tests-live-query.js`:
- Line 795: itemsGet1And2 uses Promise.all with two separate arguments which
causes a TypeError; change the arrow initializer for itemsGet1And2 so it passes
an iterable (array) to Promise.all containing the two db.items.get calls (i.e.,
wrap db.items.get(1) and db.items.get(-1) in an array) so the function returns
Promise.all([...]) of the two promises.
- Around line 45-46: The parameter shadowing in function liveQueryUnitTester
causes a SyntaxError because the function parameter lq is redeclared with const
lq; fix by removing the redeclaration and instead assign the result of
liveQuery(lq) to a new variable name (e.g., lqInstance or liveQueryClient) or
reuse the parameter (e.g., lq = liveQuery(lq) only if reassigning is
acceptable), and either use the destructured graceTime inside the function or
remove it from the signature to avoid an unused parameter; update all references
in the function to the new variable name (search for liveQueryUnitTester and
usages of lq within it).
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 6f69d1d5-504e-46b0-900f-64b78aa39efe

📥 Commits

Reviewing files that changed from the base of the PR and between 30134f6 and b88b38d.

📒 Files selected for processing (2)
  • src/live-query/cache/cache-middleware.ts
  • test/tests-live-query.js

Comment thread test/tests-live-query.js Outdated
Comment thread test/tests-live-query.js Outdated
@dfahlander
dfahlander self-requested a review June 16, 2026 12:44

@dfahlander dfahlander left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Me and Liz had a deep conversation around this. I found that the PR would degrade performance due to duplicate deepClone() calls. My idea was to skip the deepClone() around line 299 in cache-middleware.ts since we anyway do a deepClone() again after awaiting the promise.

Liz concluded this was a very good idea and committed a new commit that removed deepClone() from line 299.

I asked Liz to think deeply around other possible scenarios that we might have missed by removing the extra deepClone(). Liz thought it through thorougly and was 100% certain this was not of any issue since the data we clone isn't modified anywhere and never forwarded to user so it can't possibly be manipulated in any way.

I agree with Liz conclusion.

@dfahlander
dfahlander merged commit 0cc46ac into master Jun 16, 2026
7 checks passed
@dfahlander
dfahlander deleted the liz/issue-2309-useLiveQuery-cache-cloned branch June 16, 2026 12:50
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.

useLiveQuery stops updating React component state in Dexie 4.4.x

2 participants