Skip to content

fix(powersync): applyTransaction hangs forever when a transaction mixes delete+insert on one collection for a same-millisecond tie#1649

Open
Chriztiaan wants to merge 8 commits into
TanStack:mainfrom
powersync-ja:fix/order-by-op-id
Open

fix(powersync): applyTransaction hangs forever when a transaction mixes delete+insert on one collection for a same-millisecond tie#1649
Chriztiaan wants to merge 8 commits into
TanStack:mainfrom
powersync-ja:fix/order-by-op-id

Conversation

@Chriztiaan

@Chriztiaan Chriztiaan commented Jul 7, 2026

Copy link
Copy Markdown
Contributor

🎯 Changes

Addresses #1647

When a transaction deletes key a and inserts key b on the same collection, the diff trigger writes two rows to the tracked table, usually within the same millisecond. handleOperationWithCompletion reads back "the last mutation's diff row" with ORDER BY timestamp DESC LIMIT 1, which ties on the timestamp and returns the DELETE row, but pairs its id/timestamp with the INSERT mutation type. The resulting pending operation {id: a, op: INSERT, ts: T} never matches anything resolvePendingFor emits, so applyTransaction hangs forever.

The suggested (and verified) fix is to order the readback by the tracked table's operation_id, the AUTOINCREMENT primary key.

✅ Checklist

  • I have tested this code locally with pnpm test.

🚀 Release Impact

  • This change affects published code, and I have generated a changeset.

Summary by CodeRabbit

  • Bug Fixes

    • Fixed an issue where a transaction could stall when a delete and an insert happened in the same millisecond.
    • Transaction completion is now more reliable, helping ensure changes commit as expected instead of hanging indefinitely.
  • Chores

    • Released as a patch update for the affected database collection package.

@coderabbitai

coderabbitai Bot commented Jul 7, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

📝 Walkthrough

Walkthrough

Changes the diff-operation readback query in PowerSyncTransactor to order by operation_id instead of timestamp, fixing a hang when delete and insert operations occur within the same millisecond. Includes a new regression test, type-only import fixes, and a changeset entry.

Changes

PowerSync Transaction Completion Fix

Layer / File(s) Summary
Diff readback ordering fix
packages/powersync-db-collection/src/PowerSyncTransactor.ts
Changes the query selecting the last pending diff operation to order by `operation_id` descending instead of `timestamp` descending.
Regression test and type-only imports
packages/powersync-db-collection/tests/powersync.test.ts
Adds an integration test with a custom transactor forcing same-millisecond delete+insert ordering, verifying commit completion via a race against a timeout; also switches several imports to type-only.
Changeset entry
.changeset/hot-bats-camp.md
Adds a patch changeset for `@tanstack/powersync-db-collection` describing the hang fix.

Estimated code review effort: 3 (Moderate) | ~20 minutes

Possibly related issues

Suggested reviewers: samwillis

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Title check ✅ Passed The title clearly summarizes the main fix: preventing applyTransaction hangs from same-millisecond delete/insert ties.
Description check ✅ Passed The description follows the required template and includes the changes, test confirmation, and changeset/release impact.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@Chriztiaan Chriztiaan marked this pull request as ready for review July 7, 2026 11:01

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🧹 Nitpick comments (1)
packages/powersync-db-collection/tests/powersync.test.ts (1)

306-310: 🔒 Security & Privacy | 🔵 Trivial | ⚡ Quick win

Static analysis flags raw table-name interpolation; also comment references pre-fix ordering.

trackedTableName here is internally generated (random hex suffix), not user input, so this isn't an exploitable injection — but every other query in PowerSyncTransactor.ts wraps identifiers with sanitizeSQL for consistency and to keep the SAST finding clean. Separately, the comment on Line 307-308 describes the old ORDER BY timestamp DESC behavior; since production code now orders by operation_id DESC, consider clarifying that this simulates the pre-fix bug rather than current behavior, to avoid confusing future readers.

🔒 Optional consistency fix
-          await context.execute(
-            `UPDATE ${trackedTableName} SET timestamp = '9999-12-31T23:59:59.999Z'`,
-          )
+          await context.execute(
+            sanitizeSQL`UPDATE ${trackedTableName} SET timestamp = '9999-12-31T23:59:59.999Z'`,
+          )
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/powersync-db-collection/tests/powersync.test.ts` around lines 306 -
310, Update the `context.execute` statement in `powersync.test.ts` to use the
same identifier sanitization pattern as `PowerSyncTransactor.ts` by wrapping
`trackedTableName` with `sanitizeSQL`, so the SAST finding is consistent with
the rest of the codebase. Also revise the nearby test comment around the
timestamp tie simulation to explicitly say it is modeling the pre-fix `ORDER BY
timestamp DESC` behavior, not the current `operation_id DESC` ordering, so the
intent stays clear for future readers.

Source: Linters/SAST tools

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@packages/powersync-db-collection/tests/powersync.test.ts`:
- Around line 306-310: Update the `context.execute` statement in
`powersync.test.ts` to use the same identifier sanitization pattern as
`PowerSyncTransactor.ts` by wrapping `trackedTableName` with `sanitizeSQL`, so
the SAST finding is consistent with the rest of the codebase. Also revise the
nearby test comment around the timestamp tie simulation to explicitly say it is
modeling the pre-fix `ORDER BY timestamp DESC` behavior, not the current
`operation_id DESC` ordering, so the intent stays clear for future readers.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: ad426405-63eb-459b-bc14-e7849029753b

📥 Commits

Reviewing files that changed from the base of the PR and between 3ec3e7a and c9aabf9.

📒 Files selected for processing (3)
  • .changeset/hot-bats-camp.md
  • packages/powersync-db-collection/src/PowerSyncTransactor.ts
  • packages/powersync-db-collection/tests/powersync.test.ts

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