TanStack / TanStack/db

Add awaitPersisted() method to wait for record sync

Open
#661 1 comment 2 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Dominant language
TypeScript
Stars
3.9k
Forks
266
Avg merge
1d 4h
Merged PRs (30d)
55

Description

Feature Request

Add a collection.awaitPersisted(id) method that returns a promise resolving when all active mutations for a given record have been persisted.

Motivation

Currently, to wait for a record to sync, you need to manually track the transaction:

const tx = collection.update(todoId, draft => { draft.completed = true })
await tx.isPersisted.promise

This requires holding onto the transaction reference. For fire-and-forget mutations or when multiple components might update the same record, it would be cleaner to await persistence by record ID:

collection.update(todoId, draft => { draft.completed = true })
// Later, anywhere in the app...
await collection.awaitPersisted(todoId)

Proposed API

collection.awaitPersisted(id: TKey): Promise<void>

Behavior:

  • Finds all active transactions affecting the given key
  • Waits for them to complete via Promise.all()
  • Rechecks for new mutations after completion (new txs might have started while waiting)
  • Loops until no active mutations remain
  • Resolves immediately if no active mutations exist for that key

Implementation

async awaitPersisted(key: TKey): Promise<void> {
  while (true) {
    const info = this.getOptimisticInfo(key)
    if (!info?.isOptimistic) return
    
    await Promise.all(info.mutations.map(m => m.transaction.isPersisted.promise))
    // Recheck - new mutations might have arrived while waiting
  }
}

The loop is necessary because new mutations can be added while waiting for current ones to complete.

Use Cases

  1. Navigation after save: Wait for sync before navigating away
  2. Sequential operations: Ensure first update completes before starting dependent operation
  3. Loading states: Show spinner until all pending changes for a record are persisted
  4. Testing: Easier to wait for specific records to sync in tests

Example Usage

// Simple case
await collection.awaitPersisted(todoId)

// Navigation
async function handleSaveAndClose() {
  collection.update(todoId, draft => { draft.text = newText })
  await collection.awaitPersisted(todoId)
  navigate('/todos')
}

// Testing
test('updates are persisted', async () => {
  collection.update('todo-1', draft => { draft.completed = true })
  await collection.awaitPersisted('todo-1')
  expect(mockApi.updateTodo).toHaveBeenCalled()
})

Implementation Notes

Builds on the existing getOptimisticInfo() infrastructure from #660 which already scans active transactions for a given key.

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start with the existing getOptimisticInfo() infrastructure from #660 and the transaction isPersisted promises described in the issue. Implement and verify the collection method against the stated cases: no active mutations resolves immediately, active mutations are awaited, and new mutations are rechecked before resolving.

Written by the indexing model from the issue text.

Assessment

Tech stack
typescript
Domain
api, data
Issue type
Feature
Difficulty
3/5
Estimated time
1-2 days
Activity status
Stale
Clarity
Mostly clear
Newbie friendliness
48/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.