Skip to content

Conversation

@KyleAMathews
Copy link
Collaborator

No description provided.

@changeset-bot
Copy link

changeset-bot bot commented Dec 2, 2025

🦋 Changeset detected

Latest commit: feaba11

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

This PR includes changesets to release 14 packages
Name Type
@tanstack/db Minor
@tanstack/angular-db Patch
@tanstack/electric-db-collection Patch
@tanstack/offline-transactions Patch
@tanstack/powersync-db-collection Patch
@tanstack/query-db-collection Patch
@tanstack/react-db Patch
@tanstack/rxdb-db-collection Patch
@tanstack/solid-db Patch
@tanstack/svelte-db Patch
@tanstack/trailbase-db-collection Patch
@tanstack/vue-db Patch
todos Patch
@tanstack/db-example-paced-mutations-demo 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 Dec 2, 2025

More templates

@tanstack/angular-db

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

@tanstack/db

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

@tanstack/db-ivm

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

@tanstack/electric-db-collection

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

@tanstack/offline-transactions

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

@tanstack/powersync-db-collection

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

@tanstack/query-db-collection

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

@tanstack/react-db

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

@tanstack/rxdb-db-collection

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

@tanstack/solid-db

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

@tanstack/svelte-db

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

@tanstack/trailbase-db-collection

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

@tanstack/vue-db

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

commit: 79db466

@github-actions
Copy link
Contributor

github-actions bot commented Dec 2, 2025

Size Change: +1.54 kB (+1.77%)

Total Size: 88.7 kB

Filename Size Change
./packages/db/dist/esm/collection/index.js 3.07 kB -173 B (-5.34%)
./packages/db/dist/esm/collection/indexes.js 845 B -253 B (-23.04%) 🎉
./packages/db/dist/esm/collection/lifecycle.js 1.58 kB -86 B (-5.16%)
./packages/db/dist/esm/index.js 2.61 kB -30 B (-1.14%)
./packages/db/dist/esm/indexes/auto-index.js 931 B +189 B (+25.47%) 🚨
./packages/db/dist/esm/indexes/base-index.js 729 B -37 B (-4.83%)
./packages/db/dist/esm/indexes/lazy-index.js 0 B -1.1 kB (removed) 🏆
./packages/db/dist/esm/query/builder/functions.js 714 B -19 B (-2.59%)
./packages/db/dist/esm/indexes/basic-index.js 1.9 kB +1.9 kB (new file) 🆕
./packages/db/dist/esm/indexes/index-registry.js 797 B +797 B (new file) 🆕
./packages/db/dist/esm/indexing.js 357 B +357 B (new file) 🆕
ℹ️ 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/mutations.js 2.31 kB
./packages/db/dist/esm/collection/state.js 3.43 kB
./packages/db/dist/esm/collection/subscription.js 2.55 kB
./packages/db/dist/esm/collection/sync.js 2.37 kB
./packages/db/dist/esm/deferred.js 207 B
./packages/db/dist/esm/errors.js 4.19 kB
./packages/db/dist/esm/event-emitter.js 748 B
./packages/db/dist/esm/indexes/btree-index.js 1.87 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.1 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.75 kB
./packages/db/dist/esm/query/builder/index.js 3.96 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 430 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.33 kB
./packages/db/dist/esm/query/live/collection-registry.js 264 B
./packages/db/dist/esm/query/live/collection-subscriber.js 1.74 kB
./packages/db/dist/esm/query/live/internal.js 130 B
./packages/db/dist/esm/query/optimizer.js 2.56 kB
./packages/db/dist/esm/query/predicate-utils.js 2.91 kB
./packages/db/dist/esm/query/subset-dedupe.js 921 B
./packages/db/dist/esm/scheduler.js 1.3 kB
./packages/db/dist/esm/SortedMap.js 1.18 kB
./packages/db/dist/esm/strategies/debounceStrategy.js 247 B
./packages/db/dist/esm/strategies/queueStrategy.js 428 B
./packages/db/dist/esm/strategies/throttleStrategy.js 246 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

github-actions bot commented Dec 2, 2025

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.

I left some comments that i would like to see addressed. Nothing major. There are no unit tests for the new BasicIndex. We should add unit tests.

if (!collection.config.defaultIndexType) {
if (isDevModeEnabled()) {
console.warn(
`[TanStack DB] Auto-indexing is enabled but no defaultIndexType is set. ` +
Copy link
Contributor

Choose a reason for hiding this comment

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

nit: this warning is misleading because it says auto-indexing is enabled but actually since we return false here it is disabled. We should make explicit that we are disabling it because no defaultIndexType is set.

I think it would make more sense to still keep it enabled and just fall back to a default index type in case the user does not provide one?

* Simpler and smaller than BTreeIndex, good for read-heavy workloads.
* Use BTreeIndex for write-heavy workloads with large collections.
*/
export class BasicIndex<
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggestion: For many users it may not be clear which index they should pick based on the names BasicIndex and BTreeIndex. Perhaps we should rename them to clarify this: BasicIndex -> ReadOptimizedIndex and BTreeIndex -> WriteOptimizedIndex. The docstrings can then explain like they do now that ReadOptimizedIndex is backed by a Map + sorted Array and its performance implications and that WriteOptimizedIndex is backed by a B+ tree.

/**
* Binary search to find insertion point in sorted array
*/
private findInsertionIndex(value: any): number {
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 remove this method. We already have a findInsertPosition helper functon in utils/array-utils.ts.
That helper function expects the array to be an array of tuples though so perhaps we should modify that helper to be over a basic array of values. And we can define a variant of it on top for tupled arrays (such that the parts of the code that were using it before don't need to be changed, just need to use the tupled variant).

// No more keys for this value, remove from map and sorted array
this.valueMap.delete(normalizedValue)

const idx = this.findInsertionIndex(normalizedValue)
Copy link
Contributor

Choose a reason for hiding this comment

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

This findInsertionIndex + the if test below is the logic that is needed to delete a value from a sorted array. As mentioned in a previous comment findInsertionIndex should be replaced by the already existing findInsertPosition. And we should add an additional helper function deleteInSortedArray to that array-utils.ts file that does exactly this logic. And then here just use that deleteInSortedArray function.

/**
* Builds the index from a collection of entries
*/
build(entries: Iterable<[TKey, any]>): void {
Copy link
Contributor

Choose a reason for hiding this comment

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

I was expecting build to do something like this:

entries.map(([key, value]) => this.add(key, value))

Because right now it duplicates the add logic.

*/
equalityLookup(value: any): Set<TKey> {
const normalizedValue = normalizeValue(value)
return new Set(this.valueMap.get(normalizedValue) ?? [])
Copy link
Contributor

Choose a reason for hiding this comment

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

nit: this.valueMap.get(normalizedValue) already returns a set so let's do:

return this.valueMap.get(normalizedValue) ?? new Set()

* Performs a reversed range query
*/
rangeQueryReversed(options: RangeQueryOptions = {}): Set<TKey> {
// For BasicIndex, reversed is the same result set, just different iteration order
Copy link
Contributor

Choose a reason for hiding this comment

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

Is this true?
rangeQuery looks up the index for from and the index for to and then iterates from from to to with this for loop:

for (let i = startIdx; i < endIdx; i++)

So it is explicitly expecting startIdx to be smaller than endIdx and so going left to right by incrementing the index.

However, when doing a reversed range query we go from right to left, e.g. from 10 to 5. So shouldn't we either swap the from and to here or modify rangeQuery to find the insertions indexes of both from and to and go from the smallest to the biggest index? i.e. swap startIdx and endIdx if endIdx < startIdx.

This is how the B+ tree index does it which seems to me that we also need to do this here:

rangeQueryReversed(options: RangeQueryOptions = {}): Set<TKey> {
  const { from, to, fromInclusive = true, toInclusive = true } = options
  return this.rangeQuery({
    from: to ?? this.orderedEntries.maxKey(),
    to: from ?? this.orderedEntries.minKey(),
    fromInclusive: toInclusive,
    toInclusive: fromInclusive,
  })
}

get orderedEntriesArrayReversed(): Array<[any, Set<TKey>]> {
return [...this.sortedValues]
.reverse()
.map((value) => [value, this.valueMap.get(value) ?? new Set()])
Copy link
Contributor

Choose a reason for hiding this comment

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

This code iterates 3 times through the sorted values. We can improve it by a single pass with a for loop or with reduce.

/** Index type to use (e.g., BasicIndex, BTreeIndex) */
indexType?: TIndexType
/** Options passed to the index constructor */
options?: TIndexType extends new (
Copy link
Contributor

Choose a reason for hiding this comment

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

If we're going to pass both the index type (i.e. constructor) and the arguments for the constructor. Isn't that equivalent but a bit more verbose than to just pass an index creation function: () => BaseIndex.

// Dev mode detection settings - ON by default in non-production
let devModeConfig: IndexDevModeConfig = {
enabled: true,
collectionSizeThreshold: 1000,
Copy link
Contributor

Choose a reason for hiding this comment

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

This threshold seems arbitrary. Going through an non-indexed collection of 1000 rows should be pretty fast. I guess modern machines can easily go to perhaps 1M rows before it really becomes slow and needs indexes. Time-based suggestions (slowQueryThresholdMs) makes much more sense imo.

claude and others added 10 commits December 11, 2025 10:39
This POC explores making indexing a tree-shakeable opt-in feature to reduce
default bundle size. Key changes:

- Create new `@tanstack/db/indexing` entry point for BTreeIndex and related utils
- Add index registry to replace hard-coded BTreeIndex defaults
- Add dev-mode auto-detection for when indexes would help (collection size,
  slow queries)
- Remove BTreeIndex value export from main entry (types still exported)

Bundle size improvements when NOT using indexing:
- Minified: ~15 KB saved (6.9%)
- Gzipped: ~5.4 KB saved (8.5%)

Usage after this change:
```ts
// Option 1: Enable indexing globally
import { enableIndexing } from '@tanstack/db/indexing'
enableIndexing()

// Option 2: Use explicit index type (best for tree-shaking)
import { BTreeIndex } from '@tanstack/db/indexing'
collection.createIndex((row) => row.userId, { indexType: BTreeIndex })

// Dev mode for index suggestions
import { configureIndexDevMode } from '@tanstack/db'
configureIndexDevMode({ enabled: true, collectionSizeThreshold: 100 })
```

Note: Additional savings (~25KB more) possible by making index-optimization.ts
lazy-loaded, but would require more extensive refactoring of change-events.ts
and order-by.ts.
…y default

Breaking change: autoIndex now defaults to 'off' instead of 'eager'.
This reduces default bundle size by not requiring indexing code.

Changes:
- autoIndex defaults to 'off' - users must explicitly enable or add indexes
- Dev mode suggestions are ON by default (in non-production) to help
  developers identify when indexes would improve performance
- Updated tests to reflect new default behavior

Users who want auto-indexing can either:
1. Set autoIndex: 'eager' on individual collections
2. Import and register BTreeIndex globally:
   ```ts
   import { enableIndexing } from '@tanstack/db/indexing'
   enableIndexing()
   ```

Dev mode will warn in console when queries could benefit from indexes,
and this will also be available in devtools.
Most collections won't have perf issues until much larger sizes.
Slow query detection (10ms) is the more actionable metric.
- Add MapIndex for equality lookups (eq, in) without BTree overhead
- Change enableIndexing() to use MapIndex by default
- Add enableBTreeIndexing() for ORDER BY optimization on large collections
- BTreeIndex is only needed for sorted iteration (10k+ items with ORDER BY)

Bundle size impact:
- MapIndex: ~5 KB (~1.3 KB gzipped)
- BTreeIndex: ~33 KB (~7.8 KB gzipped)
- Savings with MapIndex: ~27 KB (~6 KB gzipped)
…ndexType

- Add BasicIndex using Map + sorted Array for both equality and range queries
- Remove registry pattern - pass defaultIndexType to collection constructor instead
- Remove lazy index infrastructure (LazyIndexWrapper, IndexProxy)
- Simplify indexing.ts entry point to just export BasicIndex and BTreeIndex
- Update all tests to explicitly set defaultIndexType where needed
- Update changeset to reflect simplified approach

Breaking changes:
- createIndex() requires defaultIndexType on collection or indexType in config
- enableIndexing()/enableBTreeIndexing() removed, use defaultIndexType instead
@kevin-dp kevin-dp force-pushed the claude/estimate-bundle-without-indexing-01RpFe1MCKR6TXmrXaPSEXRx branch from 6df29a2 to 79db466 Compare December 11, 2025 11:02
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