Skip to content

fix(db): item disappears during optimistic-to-synced transition#1469

Open
freddybreitenstein wants to merge 1 commit intoTanStack:mainfrom
freddybreitenstein:fix/1017-reconciliation-gap
Open

fix(db): item disappears during optimistic-to-synced transition#1469
freddybreitenstein wants to merge 1 commit intoTanStack:mainfrom
freddybreitenstein:fix/1017-reconciliation-gap

Conversation

@freddybreitenstein
Copy link
Copy Markdown

@freddybreitenstein freddybreitenstein commented Apr 12, 2026

Summary

Fixes #1017 — items disappear from collection during the optimistic → synced transition when onInsert syncs data back (e.g., Electric's txid handshake).

Root Cause

collection.insert() called commit() before recomputeOptimisticState(true). Since commit() synchronously enters mutationFn/onInsert, the item was not in optimisticUpserts when onInsert ran. Any code checking collection.has(key) inside onInsert — or any sync data delivered during onInsert — would not find the item.

// BEFORE (broken):
directOpTransaction.applyMutations(mutations)
directOpTransaction.commit()          // ← enters onInsert, item NOT in optimistic
state.transactions.set(...)
state.recomputeOptimisticState(true)  // ← too late

// AFTER (fixed):
directOpTransaction.applyMutations(mutations)
state.transactions.set(...)
state.recomputeOptimisticState(true)  // ← item in optimistic BEFORE commit
directOpTransaction.commit()          // ← enters onInsert, item IS visible

Impact

This bug causes items to disappear from live queries during the optimistic → synced transition. With Electric SQL on React Native (20-second long-poll), the item could be gone for up to 20 seconds. On web with SSE, it manifests as a brief flicker.

Test Plan

  • Added regression test: verifies collection.has(key) is true at every checkpoint during the transition
  • Verified fix eliminates the visibility gap
  • Full test suite: no regressions (162 pre-existing failures unchanged)
  • Tested in production app (Moi — React Native + Electric SQL) — items now appear instantly

…tack#1017)

collection.insert() called commit() (which synchronously enters
mutationFn/onInsert) before recomputeOptimisticState(true), so the item
was not in optimisticUpserts when onInsert ran. Any sync delivery during
onInsert (e.g., Electric's txid handshake) would not find the item.

Fix: move transactions.set(), scheduleTransactionCleanup(), and
recomputeOptimisticState(true) before commit() so the optimistic entry
exists when mutationFn executes.

Regression test verifies collection.has(key) is true at every checkpoint
during the optimistic → synced transition.

Fixes TanStack#1017
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.

Synced data does not appear in a derived (live query) collection while there is a pending optimistic mutation.

1 participant