Search code examples
reactjsfirebasegoogle-cloud-firestoregoogle-cloud-functionsreact-firebase-hooks

Firestore batch/transaction is not atomic with multiple set, delete inside Cloud Function


I want to delete and create multiple documents in a collection (subcollection) in a single batch, inside a Cloud Function using Firestore Web API. The code inside my Cloud Function looks like this (TypeScript):

const collectionRef = db.collection('my_collection');
const batch = db.batch();
batch.delete(collectionRef.doc('doc01'));
batch.set(collectionRef.doc('doc02'));
await batch.commit();

In my web client, I use react-firebase-hooks to see collection updates in real time (TypeScript, React.js):

const [docs, loading, error] = useCollectionData(collection(db, 'my_collection'));

The Firestore documentation on transactions & batches states that they should complete their write operations atomically. But this is not what I observe. When I call my Cloud Function, I observe separate updates on the client:

  1. docs is updated to include both 'doc01' and 'doc02'.
  2. docs is updated again, now without 'doc01'.

I also see this in my local Firestore Emulator at http://localhost:4000/firestore/default/data/my_collection, that doc02 appears in the list a split-second earlier than 'doc01' is removed.

The same happens if I change my Cloud Function code to use transactions:

const collectionRef = db.collection('my_collection');
await db.runTransaction(async (transaction) => {
  transaction.delete(collectionRef.doc('doc01'));
  transaction.set(collectionRef.doc('doc02'));
});

I want both the delete and set to complete together, without seeing any partial results. What am I doing wrong?


Solution

  • It's hard to be certain as you don't actually show the behavior that is problematic, but most likely you're seeing the events for local writes. The Firestore SDK optimistically fires events on the client that performs the writes. You can check if the snapshot metadata has pending writes to catch this condition.