Skip to content

Conversation

samwillis
Copy link
Collaborator

Summary

This PR enhances the awaitTxId utility in @tanstack/electric-db-collection to resolve transaction IDs based on PostgreSQL snapshot metadata from snapshot-end messages, enabling transaction matching on the initial snapshot at the start of a new shape.

Motivation

When an Electric shape starts, the initial snapshot includes a snapshot-end control message with PostgreSQL snapshot metadata (xmin, xmax, xip_list). Previously, awaitTxId could only match against explicit txid arrays sent with individual change messages. This meant that transactions completed before the snapshot started couldn't be matched, even though they were visible in the snapshot data.

By supporting snapshot-based visibility checks, we can now correctly identify which transactions are included in the initial snapshot, improving transaction confirmation reliability.

Changes

Implementation (electric.ts):

  • Added seenSnapshots store to track PostgreSQL snapshots alongside seenTxids
  • Added isSnapshotEndMessage and parseSnapshotMessage helper functions
  • Enhanced awaitTxId to check transaction visibility against stored snapshots using isVisibleInSnapshot from @electric-sql/client
  • Added dual subscription logic to resolve on either explicit txid match or snapshot visibility match
  • Process and store snapshot metadata from snapshot-end control messages

Tests (electric.test.ts):

  • Test snapshot metadata parsing and visibility matching
  • Test txids visible in snapshots (< xmin, in range [xmin, xmax) excluding xip_list)
  • Test txids NOT visible in snapshots (in xip_list, >= xmax)
  • Test awaiting txids that arrive later via snapshot
  • Test handling multiple snapshots
  • Test that explicit txids and snapshot matching work together

Dependencies

  • Upgraded @electric-sql/client from ^1.0.10 to ^1.0.14 to access the isVisibleInSnapshot utility and PostgresSnapshot type

Future Work

This implementation currently handles snapshots at the beginning of shape logs. Upcoming PRs for incremental sync will extend this to handle multiple snapshots over time and may introduce snapshot retention policies (they are kept unbounded for now).

Copy link

changeset-bot bot commented Oct 6, 2025

🦋 Changeset detected

Latest commit: 2c0a5c2

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

This PR includes changesets to release 2 packages
Name Type
@tanstack/electric-db-collection Patch
@tanstack/db-example-react-todo 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

Copy link

pkg-pr-new bot commented Oct 6, 2025

More templates

@tanstack/angular-db

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

@tanstack/db

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

@tanstack/db-ivm

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

@tanstack/electric-db-collection

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

@tanstack/query-db-collection

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

@tanstack/react-db

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

@tanstack/rxdb-db-collection

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

@tanstack/solid-db

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

@tanstack/svelte-db

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

@tanstack/trailbase-db-collection

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

@tanstack/vue-db

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

commit: 2c0a5c2

Copy link
Contributor

github-actions bot commented Oct 6, 2025

Size Change: 0 B

Total Size: 75.2 kB

ℹ️ View Unchanged
Filename Size
./packages/db/dist/esm/collection/change-events.js 943 B
./packages/db/dist/esm/collection/changes.js 1.01 kB
./packages/db/dist/esm/collection/events.js 660 B
./packages/db/dist/esm/collection/index.js 3.18 kB
./packages/db/dist/esm/collection/indexes.js 1.16 kB
./packages/db/dist/esm/collection/lifecycle.js 1.8 kB
./packages/db/dist/esm/collection/mutations.js 2.5 kB
./packages/db/dist/esm/collection/state.js 3.82 kB
./packages/db/dist/esm/collection/subscription.js 1.65 kB
./packages/db/dist/esm/collection/sync.js 1.42 kB
./packages/db/dist/esm/deferred.js 230 B
./packages/db/dist/esm/errors.js 3.1 kB
./packages/db/dist/esm/index.js 1.56 kB
./packages/db/dist/esm/indexes/auto-index.js 806 B
./packages/db/dist/esm/indexes/base-index.js 835 B
./packages/db/dist/esm/indexes/btree-index.js 2 kB
./packages/db/dist/esm/indexes/lazy-index.js 1.21 kB
./packages/db/dist/esm/indexes/reverse-index.js 577 B
./packages/db/dist/esm/local-only.js 827 B
./packages/db/dist/esm/local-storage.js 2.02 kB
./packages/db/dist/esm/optimistic-action.js 294 B
./packages/db/dist/esm/proxy.js 3.86 kB
./packages/db/dist/esm/query/builder/functions.js 615 B
./packages/db/dist/esm/query/builder/index.js 4.04 kB
./packages/db/dist/esm/query/builder/ref-proxy.js 938 B
./packages/db/dist/esm/query/compiler/evaluators.js 1.55 kB
./packages/db/dist/esm/query/compiler/expressions.js 631 B
./packages/db/dist/esm/query/compiler/group-by.js 2.04 kB
./packages/db/dist/esm/query/compiler/index.js 2.04 kB
./packages/db/dist/esm/query/compiler/joins.js 2.52 kB
./packages/db/dist/esm/query/compiler/order-by.js 1.21 kB
./packages/db/dist/esm/query/compiler/select.js 1.28 kB
./packages/db/dist/esm/query/ir.js 785 B
./packages/db/dist/esm/query/live-query-collection.js 340 B
./packages/db/dist/esm/query/live/collection-config-builder.js 2.69 kB
./packages/db/dist/esm/query/live/collection-subscriber.js 1.86 kB
./packages/db/dist/esm/query/optimizer.js 3.08 kB
./packages/db/dist/esm/SortedMap.js 1.24 kB
./packages/db/dist/esm/transactions.js 3 kB
./packages/db/dist/esm/utils.js 1.01 kB
./packages/db/dist/esm/utils/browser-polyfills.js 365 B
./packages/db/dist/esm/utils/btree.js 6.01 kB
./packages/db/dist/esm/utils/comparison.js 754 B
./packages/db/dist/esm/utils/index-optimization.js 1.73 kB

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

Copy link
Contributor

github-actions bot commented Oct 6, 2025

Size Change: 0 B

Total Size: 1.47 kB

ℹ️ View Unchanged
Filename Size
./packages/react-db/dist/esm/index.js 152 B
./packages/react-db/dist/esm/useLiveQuery.js 1.32 kB

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.

LGTM, left a few nits.

if (hasTxid) return true

// Then check if the txid is in any of the seen snapshots
const hasSnapshot = seenSnapshots.state.some((snapshot) =>
Copy link
Contributor

Choose a reason for hiding this comment

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

How big are we envisioning this seenSnapshots list could become? Worst case we're looping over all snapshots and for each snapshot we check isVisibleInSnapshot which in the worst case loop over the snapshot's xip_list. So i'm wondering here if this could become a performance issue. If it is, we could as well keep a set of all txIds we've seen in snapshots and then we could just do a set lookup (which is in constant time).

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Initially it should only be one, the initial snapshot, but with incremental sync it could grow. I intend to revisit both this and the seenTxids in a future PR

}
})

const unsubscribeSeenSnapshots = seenSnapshots.subscribe(() => {
Copy link
Contributor

Choose a reason for hiding this comment

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

iiuc this subscription is racing against the seenTxidssubscription. The first one to find the txId will resolve the promise.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Yes

@samwillis samwillis merged commit a8624c8 into main Oct 6, 2025
6 checks passed
@samwillis samwillis deleted the samwillis/await-txid-snapshots branch October 6, 2025 16:40
@github-actions github-actions bot mentioned this pull request Oct 6, 2025
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.

2 participants