Skip to content

Conversation

@KyleAMathews
Copy link
Collaborator

When using a static queryKey with syncMode: 'on-demand', the system now automatically appends serialized LoadSubsetOptions to create unique cache keys for different predicate combinations.

This fixes an issue where static queryKeys in on-demand mode would cause all live queries with different predicates to share the same cache entry, defeating the purpose of predicate push-down.

Changes:

  • Added serialization utilities for LoadSubsetOptions (serializeLoadSubsetOptions, serializeExpression, serializeValue)
  • Modified createQueryFromOpts to automatically append serialized predicates when queryKey is static and syncMode is 'on-demand'
  • Function-based queryKeys continue to work as before
  • Eager mode with static queryKeys unchanged (no automatic serialization)

Tests:

  • Added comprehensive test suite for static queryKey with on-demand mode
  • Tests verify different predicates create separate cache entries
  • Tests verify identical predicates reuse the same cache entry
  • Tests verify eager mode behavior unchanged
  • All existing tests pass

🎯 Changes

✅ Checklist

  • I have followed the steps in the Contributing guide.
  • I have tested this code locally with pnpm test:pr.

🚀 Release Impact

  • This change affects published code, and I have generated a changeset.
  • This change is docs/CI/dev-only (no release).

… mode

When using a static queryKey with syncMode: 'on-demand', the system now
automatically appends serialized LoadSubsetOptions to create unique cache
keys for different predicate combinations.

This fixes an issue where static queryKeys in on-demand mode would cause
all live queries with different predicates to share the same cache entry,
defeating the purpose of predicate push-down.

Changes:
- Added serialization utilities for LoadSubsetOptions (serializeLoadSubsetOptions,
  serializeExpression, serializeValue)
- Modified createQueryFromOpts to automatically append serialized predicates
  when queryKey is static and syncMode is 'on-demand'
- Function-based queryKeys continue to work as before
- Eager mode with static queryKeys unchanged (no automatic serialization)

Tests:
- Added comprehensive test suite for static queryKey with on-demand mode
- Tests verify different predicates create separate cache entries
- Tests verify identical predicates reuse the same cache entry
- Tests verify eager mode behavior unchanged
- All existing tests pass
@changeset-bot
Copy link

changeset-bot bot commented Nov 12, 2025

🦋 Changeset detected

Latest commit: 41d5a86

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 2 packages
Name Type
@tanstack/query-db-collection Patch
@tanstack/db-collection-e2e Patch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@pkg-pr-new
Copy link

pkg-pr-new bot commented Nov 12, 2025

More templates

@tanstack/angular-db

npm i https://pkg.pr.new/@tanstack/angular-db@800

@tanstack/db

npm i https://pkg.pr.new/@tanstack/db@800

@tanstack/db-ivm

npm i https://pkg.pr.new/@tanstack/db-ivm@800

@tanstack/electric-db-collection

npm i https://pkg.pr.new/@tanstack/electric-db-collection@800

@tanstack/offline-transactions

npm i https://pkg.pr.new/@tanstack/offline-transactions@800

@tanstack/powersync-db-collection

npm i https://pkg.pr.new/@tanstack/powersync-db-collection@800

@tanstack/query-db-collection

npm i https://pkg.pr.new/@tanstack/query-db-collection@800

@tanstack/react-db

npm i https://pkg.pr.new/@tanstack/react-db@800

@tanstack/rxdb-db-collection

npm i https://pkg.pr.new/@tanstack/rxdb-db-collection@800

@tanstack/solid-db

npm i https://pkg.pr.new/@tanstack/solid-db@800

@tanstack/svelte-db

npm i https://pkg.pr.new/@tanstack/svelte-db@800

@tanstack/trailbase-db-collection

npm i https://pkg.pr.new/@tanstack/trailbase-db-collection@800

@tanstack/vue-db

npm i https://pkg.pr.new/@tanstack/vue-db@800

commit: 41d5a86

@github-actions
Copy link
Contributor

Size Change: 0 B

Total Size: 86 kB

ℹ️ View Unchanged
Filename Size
./packages/db/dist/esm/collection/change-events.js 1.38 kB
./packages/db/dist/esm/collection/changes.js 977 B
./packages/db/dist/esm/collection/events.js 388 B
./packages/db/dist/esm/collection/index.js 3.24 kB
./packages/db/dist/esm/collection/indexes.js 1.1 kB
./packages/db/dist/esm/collection/lifecycle.js 1.67 kB
./packages/db/dist/esm/collection/mutations.js 2.26 kB
./packages/db/dist/esm/collection/state.js 3.43 kB
./packages/db/dist/esm/collection/subscription.js 2.42 kB
./packages/db/dist/esm/collection/sync.js 2.12 kB
./packages/db/dist/esm/deferred.js 207 B
./packages/db/dist/esm/errors.js 4.11 kB
./packages/db/dist/esm/event-emitter.js 748 B
./packages/db/dist/esm/index.js 2.63 kB
./packages/db/dist/esm/indexes/auto-index.js 742 B
./packages/db/dist/esm/indexes/base-index.js 766 B
./packages/db/dist/esm/indexes/btree-index.js 1.87 kB
./packages/db/dist/esm/indexes/lazy-index.js 1.1 kB
./packages/db/dist/esm/indexes/reverse-index.js 513 B
./packages/db/dist/esm/local-only.js 837 B
./packages/db/dist/esm/local-storage.js 2.04 kB
./packages/db/dist/esm/optimistic-action.js 359 B
./packages/db/dist/esm/paced-mutations.js 496 B
./packages/db/dist/esm/proxy.js 3.22 kB
./packages/db/dist/esm/query/builder/functions.js 733 B
./packages/db/dist/esm/query/builder/index.js 3.84 kB
./packages/db/dist/esm/query/builder/ref-proxy.js 917 B
./packages/db/dist/esm/query/compiler/evaluators.js 1.35 kB
./packages/db/dist/esm/query/compiler/expressions.js 691 B
./packages/db/dist/esm/query/compiler/group-by.js 1.8 kB
./packages/db/dist/esm/query/compiler/index.js 1.96 kB
./packages/db/dist/esm/query/compiler/joins.js 2 kB
./packages/db/dist/esm/query/compiler/order-by.js 1.25 kB
./packages/db/dist/esm/query/compiler/select.js 1.07 kB
./packages/db/dist/esm/query/expression-helpers.js 1.43 kB
./packages/db/dist/esm/query/ir.js 673 B
./packages/db/dist/esm/query/live-query-collection.js 360 B
./packages/db/dist/esm/query/live/collection-config-builder.js 5.26 kB
./packages/db/dist/esm/query/live/collection-registry.js 264 B
./packages/db/dist/esm/query/live/collection-subscriber.js 1.77 kB
./packages/db/dist/esm/query/live/internal.js 130 B
./packages/db/dist/esm/query/optimizer.js 2.6 kB
./packages/db/dist/esm/query/predicate-utils.js 2.88 kB
./packages/db/dist/esm/query/subset-dedupe.js 921 B
./packages/db/dist/esm/scheduler.js 1.21 kB
./packages/db/dist/esm/SortedMap.js 1.18 kB
./packages/db/dist/esm/strategies/debounceStrategy.js 237 B
./packages/db/dist/esm/strategies/queueStrategy.js 418 B
./packages/db/dist/esm/strategies/throttleStrategy.js 236 B
./packages/db/dist/esm/transactions.js 2.9 kB
./packages/db/dist/esm/utils.js 881 B
./packages/db/dist/esm/utils/browser-polyfills.js 304 B
./packages/db/dist/esm/utils/btree.js 5.61 kB
./packages/db/dist/esm/utils/comparison.js 852 B
./packages/db/dist/esm/utils/index-optimization.js 1.51 kB
./packages/db/dist/esm/utils/type-guards.js 157 B

compressed-size-action::db-package-size

@github-actions
Copy link
Contributor

Size Change: 0 B

Total Size: 3.34 kB

ℹ️ View Unchanged
Filename Size
./packages/react-db/dist/esm/index.js 225 B
./packages/react-db/dist/esm/useLiveInfiniteQuery.js 1.17 kB
./packages/react-db/dist/esm/useLiveQuery.js 1.11 kB
./packages/react-db/dist/esm/useLiveSuspenseQuery.js 431 B
./packages/react-db/dist/esm/usePacedMutations.js 401 B

compressed-size-action::react-db-package-size

Copy link
Contributor

@kevin-dp kevin-dp left a comment

Choose a reason for hiding this comment

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

Left some comments.

* Serializes LoadSubsetOptions into a stable, hashable format for query keys
* @internal
*/
function serializeLoadSubsetOptions(
Copy link
Contributor

Choose a reason for hiding this comment

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

Let's move all the serialization functions into a utility file

result.orderBy = options.orderBy.map((clause) => ({
expression: serializeExpression(clause.expression),
direction: clause.compareOptions.direction,
nulls: clause.compareOptions.nulls,
Copy link
Contributor

Choose a reason for hiding this comment

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

This object only contains the direction and nulls but is missing the other options from StringCollationConfig:

export type CompareOptions = StringCollationConfig & {
  direction: OrderByDirection
  nulls: `first` | `last`
}

*/
function serializeLoadSubsetOptions(
options: LoadSubsetOptions | undefined
): unknown {
Copy link
Contributor

Choose a reason for hiding this comment

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

Why is the return type typed as unknown ? Shouldn't this be string | null.
Actually, i'm wondering why we return null instead of undefined. I think the code would be slightly simplier if we return undefined. So the return type would be string | undefined`.

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.

4 participants