TanStack / TanStack/db

Awaiting txid across multiple collections.

Open
#337 2 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

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

Description

I'd just like to query a pattern I documented in https://electric-sql.com/blog/2025/07/29/local-first-sync-with-tanstack-db#transactions-and-optimistic-actions and https://electric-sql.com/blog/2025/07/29/local-first-sync-with-tanstack-db#write-path-sync

Specifically, is there a better way we can monitor the txid syncing back when writes are made to multiple collections?

The context being that transactions are explicitly designed to allow you to write atomically across collections. With the example as documented:

import { createOptimisticAction } from '@tanstack/react-db'

const createUserWithDefaultWorkspace = createOptimisticAction({
  (loginName) => {
    const userId = crypto.randomUUID()
    const workspaceId = crypto.randomUUID()

    // These inserts are applied atomically
    userCollection.insert({
      id: userId,
      name: loginName
    })
    workspaceCollection.insert({
      id: workspaceId,
      name: 'Default'
    })
    membershipCollection.insert({
      role: 'owner',
      userId,
      workspaceId
    })
  },
  mutationFn: async (_loginName, { transaction }) => {
    // In this case, the `transaction` contains all three mutations.

    // ... handle sending to the server ...
  }
})

The local optimistic state is applied atomically (I believe?) and the mutationFn is invoked atomically (i.e.: once, with a single transaction). However, when then monitoring the replication stream for the write, the example awaits the transaction arriving in all three collections:

export const mutationFn = async (_variables, { transaction }) => {
  const mutations = transaction.mutations

  const collections = new Set(mutations.map(mutation => mutation.collection))
  const payloadData = mutations.map(mutation => {
    const { collection, ...result } = mutation

    return result
  })

  // Post the mutations data to the ingest endpoint.
  const txid = await api.post('/ingest', { mutations: payloadData })

  // Monitor the collections for the transaction to sync back in.
  const promises = [...collections].map(({ utils }) => utils.awaitTxId(txid))
  await Promise.all(promises)
}

Now, the happy path is collections all sync the write back roughly at the same time and there's no real gap between updating the synced state in each collection and dropping the optimistic state. However, obviously there can be race conditions and it's easy for one of the collections to hang, potentially for a long time.

I'm not sure whether this ever would result in flickering / temporary anomalies? But generally it feels like this weakens the transactional guarantees and leaves the door open to oddness. As well as not being very ergonomic DX.

We've obviously discussed various approaches to transactional streams across multiple shapes. I'm not sure what's possible or sane here. Just flagging it up because I was eyeballing it when writing the docs.

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 linked Transactions and Optimistic Actions and Write Path Sync documentation, then trace how transaction.mutations and utils.awaitTxId(txid) coordinate across collections. Determine whether the current multi-collection waiting behavior can preserve atomicity and what supported API or documentation change would address the race and hanging concerns.

Written by the indexing model from the issue text.

Assessment

Tech stack
typescript
Domain
databases, distributed-systems
Issue type
Feature
Difficulty
5/5
Estimated time
Over a week
Activity status
Stale
Clarity
Needs clarification
Newbie friendliness
25/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.