Skip to content

Conversation

@KyleAMathews
Copy link
Collaborator

Export the helper function from db-ivm for use in the main db package.

…parison

This fixes a bug where joining collections by small Uint8Array keys would
fail because the keys were compared by reference instead of by content.

The fix normalizes Uint8Array keys (≤128 bytes) to string representations
before using them as Map keys, similar to the approach used in PR #779 for
value comparison. This ensures that Uint8Array instances with the same
byte content are treated as equal, even if they are different objects.

Changes:
- Added normalizeKey() function to convert small Uint8Arrays to string keys
- Updated Index class to normalize keys in all Map operations
- Maintained mapping from normalized keys to original keys for iteration
- Added test case for joining collections with Uint8Array keys

Fixes #896
This refactoring eliminates code duplication by creating shared utilities
for Uint8Array normalization that can be used across both db-ivm and db
packages.

Changes:
- Created new module: packages/db-ivm/src/utils/uint8array.ts
  - Exports UINT8ARRAY_NORMALIZE_THRESHOLD constant (128 bytes)
  - Exports isUint8Array() helper function
  - Exports normalizeUint8Array() for byte array to string conversion
  - Exports normalizeValue() for generic value normalization

- Updated packages/db-ivm/src/indexes.ts:
  - Removed local normalizeKey() function
  - Now imports and uses normalizeValue from shared utilities

- Updated packages/db/src/utils/comparison.ts:
  - Removed duplicate UINT8ARRAY_NORMALIZE_THRESHOLD constant
  - Removed duplicate isUint8Array check logic
  - Now imports and uses shared utilities from @tanstack/db-ivm

- Updated packages/db-ivm/src/index.ts:
  - Added export for utils/uint8array module

Benefits:
- Single source of truth for Uint8Array normalization logic
- Easier maintenance and consistency across packages
- Follows DRY principle

All tests pass (268 tests in db-ivm, 1699 tests in db).
@changeset-bot
Copy link

changeset-bot bot commented Nov 24, 2025

🦋 Changeset detected

Latest commit: 457a409

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

This PR includes changesets to release 12 packages
Name Type
@tanstack/db-ivm Patch
@tanstack/db Patch
@tanstack/angular-db Patch
@tanstack/db-collection-e2e Patch
@tanstack/electric-db-collection Patch
@tanstack/powersync-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

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 24, 2025

More templates

@tanstack/angular-db

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

@tanstack/db

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

@tanstack/db-ivm

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

@tanstack/electric-db-collection

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

@tanstack/offline-transactions

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

@tanstack/powersync-db-collection

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

@tanstack/query-db-collection

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

@tanstack/react-db

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

@tanstack/rxdb-db-collection

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

@tanstack/solid-db

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

@tanstack/svelte-db

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

@tanstack/trailbase-db-collection

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

@tanstack/vue-db

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

commit: 457a409

@github-actions
Copy link
Contributor

Size Change: -36 B (-0.04%)

Total Size: 86.3 kB

Filename Size Change
./packages/db/dist/esm/utils/comparison.js 816 B -36 B (-4.23%)
ℹ️ 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.48 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/index.js 2.64 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.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.22 kB
./packages/db/dist/esm/query/builder/functions.js 733 B
./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.26 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.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 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/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
Collaborator

@samwillis samwillis left a comment

Choose a reason for hiding this comment

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

I think we should consider carefully if this is a change we want to make. The performance of joins is important, and currently this implementation makes all joins use a new (slightly) slower code path with the normalisation and tracking of original keys - thats not needed for 99% of joins that use string or numeric keys.

I'm honestly not sure that supporting uint8 arrays as join keys is a good idea or not. We can decide to just push this back on the user and have them normalise a uuid to a string when syncing data - I do wander if that is a better option that adding complexity.

describe(`Operators`, () => {
describe(`Join operation`, () => {
testJoin()
testUint8ArrayKeyJoin()
Copy link
Collaborator

Choose a reason for hiding this comment

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

If we decide to keep this implementation, can we incorporate this into the main testJoin function rather than adding as a new one with just this single test?

*/
#inner: IndexMap<TKey, TValue, TPrefix>
#consolidatedMultiplicity: Map<TKey, number> = new Map() // sum of multiplicities per key
#originalKeys: Map<TKey, TKey> = new Map() // map from normalized keys to original keys
Copy link
Collaborator

Choose a reason for hiding this comment

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

I'm a little nervous of the overhead of adding this additional map and the lookups. We are making joins slower for all cases in order to support an edge case.

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