Skip to content

Conversation

@kevin-dp
Copy link
Contributor

🎯 Changes

Addresses Issue #1122, fixing SyncTransactionAlreadyCommittedError in progressive sync mode after browser visibility changes.

The bug stemmed from two main issues:

  1. Duplicate begin() calls: processMoveOutEvent was incorrectly calling begin() again during atomic swap, leading to orphaned transactions. This is fixed by ensuring transactionStarted is correctly passed as true.
  2. Stale transactionStarted flag: The transactionStarted flag was reset after commit(), risking a stale true state if commit() failed. This is fixed by resetting the flag before commit().

New comprehensive unit tests have been added to cover visibility resume scenarios and ensure the fix. A detailed bug report is also included.

✅ 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).

Open in Cursor Open in Web

These tests reproduce scenarios related to the bug described in Issue #1122
where SyncTransactionAlreadyCommittedError occurs after browser visibility
changes in progressive mode.

One failing test demonstrates a related bug: when there's a persisting
(optimistic) transaction, committed sync transactions are not removed from
pendingSyncedTransactions because commitPendingTransactions() skips processing
when hasPersistingTransaction is true.

Test scenarios covered:
- Visibility resume after atomic swap
- New changes after visibility resume
- Duplicate messages during buffering phase
- Visibility change during active sync
- Move-out messages during atomic swap
- Multiple rapid visibility changes
- Up-to-date in separate batch after changes
- Orphaned committed transactions
- Sync messages while optimistic mutation is persisting (FAILING)
…ive mode

This commit fixes two issues related to transaction state management
in progressive mode that could contribute to Issue #1122:

1. Fix duplicate begin() calls during atomic swap:
   - During atomic swap, processMoveOutEvent was called with transactionStarted=false
   - But begin() was already called at the start of the atomic swap
   - If processMoveOutEvent had rows to delete, it would call begin() again
   - This created duplicate transactions, with only the last one being committed
   - Fix: Pass true to processMoveOutEvent during atomic swap

2. Fix transactionStarted reset order:
   - Previously, transactionStarted was reset to false AFTER commit()
   - If commit() threw an exception, transactionStarted would remain true
   - This could cause subsequent batches to skip begin() and try to use
     an already-committed or non-existent transaction
   - Fix: Reset transactionStarted BEFORE calling commit()

Also updates tests to verify:
- Sync messages work correctly while optimistic mutations are persisting
- Subsequent sync messages don't throw SyncTransactionAlreadyCommittedError
Add comprehensive documentation of the bug analysis, root cause,
and solution for the SyncTransactionAlreadyCommittedError issue
in progressive mode.
@cursor
Copy link

cursor bot commented Jan 13, 2026

Cursor Agent can help with this pull request. Just @cursor in comments and I'll start working on changes in this branch.
Learn more about Cursor Agents

@changeset-bot
Copy link

changeset-bot bot commented Jan 13, 2026

⚠️ No Changeset found

Latest commit: da0788b

Merging this PR will not cause a version bump for any packages. If these changes should not result in a new version, you're good to go. If these changes should result in a version bump, you need to add a changeset.

This PR includes no changesets

When changesets are added to this PR, you'll see the packages that this PR includes changesets for and the associated semver types

Click here to learn what changesets are, and how to add one.

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

@pkg-pr-new
Copy link

pkg-pr-new bot commented Jan 13, 2026

More templates

@tanstack/angular-db

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

@tanstack/db

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

@tanstack/db-ivm

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

@tanstack/electric-db-collection

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

@tanstack/offline-transactions

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

@tanstack/powersync-db-collection

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

@tanstack/query-db-collection

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

@tanstack/react-db

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

@tanstack/rxdb-db-collection

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

@tanstack/solid-db

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

@tanstack/svelte-db

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

@tanstack/trailbase-db-collection

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

@tanstack/vue-db

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

commit: da0788b

@github-actions
Copy link
Contributor

Size Change: 0 B

Total Size: 90.5 kB

ℹ️ View Unchanged
Filename Size
./packages/db/dist/esm/collection/change-events.js 1.39 kB
./packages/db/dist/esm/collection/changes.js 1.19 kB
./packages/db/dist/esm/collection/events.js 388 B
./packages/db/dist/esm/collection/index.js 3.32 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.34 kB
./packages/db/dist/esm/collection/state.js 3.46 kB
./packages/db/dist/esm/collection/subscription.js 3.62 kB
./packages/db/dist/esm/collection/sync.js 2.38 kB
./packages/db/dist/esm/deferred.js 207 B
./packages/db/dist/esm/errors.js 4.49 kB
./packages/db/dist/esm/event-emitter.js 748 B
./packages/db/dist/esm/index.js 2.69 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.93 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.75 kB
./packages/db/dist/esm/query/builder/functions.js 733 B
./packages/db/dist/esm/query/builder/index.js 4.08 kB
./packages/db/dist/esm/query/builder/ref-proxy.js 1.05 kB
./packages/db/dist/esm/query/compiler/evaluators.js 1.42 kB
./packages/db/dist/esm/query/compiler/expressions.js 430 B
./packages/db/dist/esm/query/compiler/group-by.js 1.87 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.45 kB
./packages/db/dist/esm/query/compiler/select.js 1.06 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.4 kB
./packages/db/dist/esm/query/live/collection-registry.js 264 B
./packages/db/dist/esm/query/live/collection-subscriber.js 1.93 kB
./packages/db/dist/esm/query/live/internal.js 145 B
./packages/db/dist/esm/query/optimizer.js 2.56 kB
./packages/db/dist/esm/query/predicate-utils.js 2.97 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.3 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 924 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/cursor.js 457 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.47 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.12 kB
./packages/react-db/dist/esm/useLiveSuspenseQuery.js 559 B
./packages/react-db/dist/esm/usePacedMutations.js 401 B

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

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.

3 participants