Maverick7
Maverick7

Reputation: 1097

Transaction Succeeding in Firestore but lands up in onFailure

I am running a transaction in firestore like this:

final DocumentReference deleteRef = db.collection("ABC").document("XYZ");

db.runTransaction(new Transaction.Function<Void>() {
    @Override
    public Void apply(Transaction transaction) throws FirebaseFirestoreException {
        DocumentSnapshot documentSnapshot = transaction.get(deleteRef);
        if(documentSnapshot.exists())
        {
            transaction.delete(deleteRef);
            Log.d("MyActivity", "inside documentSnapshot.exists()");
            Log.d("MyActivity",deleteRef.getPath());

        }
        return null;
    }
}).addOnSuccessListener(new OnSuccessListener<Void>() {
    @Override
    public void onSuccess(Void aVoid) {
        // implement logic
        Log.d("MyActivity","onSuccess");
    }
}).addOnFailureListener(new OnFailureListener() {
    @Override
    public void onFailure(@NonNull Exception e) {
        Log.d("MyActivity","onFailure");
        Log.e("MyActivity", "transaction failure", e);
    }
});

Logs of Client A:

2019-10-21 20:28:45.134 15866-16240/someapp D/MyActivity: inside documentSnapshot.exists()

2019-10-21 20:28:45.134 15866-16240/someapp D/MyActivity: ABC/XYZ

2019-10-21 20:28:45.488 15866-15866/someapp D/MyActivity: onSuccess

Logs of Client B:

2019-10-21 20:28:46.041 30293-31123/someapp D/MyActivity: inside documentSnapshot.exists()

2019-10-21 20:28:46.041 30293-31123/someapp D/MyActivity: ABC/XYZ

2019-10-21 20:28:47.900 30293-30293/someapp D/MyActivity: onFailure

2019-10-21 20:58:04.137 6537-6537/someapp E/MyActivity: transaction failure com.google.firebase.firestore.FirebaseFirestoreException: Transaction failed all retries. at com.google.firebase.firestore.core.SyncEngine.lambda$transaction$0(com.google.firebase:firebase-firestore@@19.0.1:283) at com.google.firebase.firestore.core.SyncEngine$$Lambda$2.then(com.google.firebase:firebase-firestore@@19.0.1) at com.google.android.gms.tasks.zzf.run(Unknown Source) at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:428) at java.util.concurrent.FutureTask.run(FutureTask.java:237) at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(ScheduledThreadPoolExecutor.java:272) at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1133) at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:607) at com.google.firebase.firestore.util.AsyncQueue$DelayedStartFactory.run(com.google.firebase:firebase-firestore@@19.0.1:205) at java.lang.Thread.run(Thread.java:761) Caused by: com.google.firebase.firestore.FirebaseFirestoreException: Every document read in a transaction must also be written. at com.google.firebase.firestore.core.Transaction.commit(com.google.firebase:firebase-firestore@@19.0.1:182) at com.google.firebase.firestore.core.SyncEngine.lambda$transaction$1(com.google.firebase:firebase-firestore@@19.0.1:270) at com.google.firebase.firestore.core.SyncEngine$$Lambda$1.then(com.google.firebase:firebase-firestore@@19.0.1) at com.google.android.gms.tasks.zzf.run(Unknown Source)  at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:428)  at java.util.concurrent.FutureTask.run(FutureTask.java:237)  at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(ScheduledThreadPoolExecutor.java:272)  at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1133)  at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:607)  at com.google.firebase.firestore.util.AsyncQueue$DelayedStartFactory.run(com.google.firebase:firebase-firestore@@19.0.1:205)  at java.lang.Thread.run(Thread.java:761)

Now, 2 clients A and B are near simultaneously running this transaction. As one would expect, if Client A lands up in onSuccess, Client B lands up in onFailure.

But, unexpectedly, both the clients are deleting the document.

In other words, even if the transaction lands in onFailure for Client B, it deletes the document, which should not be case.

Please help me understand this.

Upvotes: 0

Views: 156

Answers (1)

Doug Stevenson
Doug Stevenson

Reputation: 317372

Only the first client is actually deleting the document. The second client would see a non-existent document.

You might also want to know that deletes in Cloud Firestore don't generate an error. Also, gets on documents also do not generate errors for documents that don't exist.

So actually, with the code you're showing, the second client would see documentSnapshot.exists() return false, and nothing would happen in the transaction, because exists() also doesn't generate any errors.

If you're seeing a client end up in a failure, try logging the exception to see what's really going on.

Upvotes: 2

Related Questions