Can transaction be used on collection?

339

Can transaction be use for monitor change to subcollection?

Transactions in Firestore work by a so-called compare-and-swap operation. In a transaction, you read a document from the database, determine its current state, and then set its new state based on that. When you've done that for the entire transaction, you send the whole package of current-state-and-new-state documents to the server. The server then checks whether the current state in the storage layer still matches what your client started with, and if so it commits the new state that you specified.

Knowing this, the only way it is possible to monitor an entire collection in a transaction is to read all documents in that collection into the transaction. While that is technically possible for small collections, it's likely to be very inefficient, and I've never seen it done in practice. Then again, for just the two documents in your collection it may be totally feasible to simply read them in the transaction.


Keep in mind though that a transaction only ensures consistent data, it doesn't necessarily limit what a malicious user can do. If you want to ensure there are never more than two documents in the collection, you should look at a server-side mechanism.

The simplest mechanism (infrastructure wise) is to use Firestore's server-side security rules, but I don't think those will work to limit the number of documents in a collection, as Doug explained in his answer to Limit a number of documents in a subcollection in firestore rules.

The most likely solution in that case is (as Doug also suggests) to use Cloud Functions to write the documents in the subcollection. That way you can simply reject direct writes from the client, and enforce any business logic you want in your Cloud Functions code, which runs in a trusted environment.

Share:
339
FlutterFirebase
Author by

FlutterFirebase

Updated on December 09, 2022

Comments

  • FlutterFirebase
    FlutterFirebase 11 months

    I am use Firestore and try to remove race condition in Flutter app by use transaction.

    I have subcollection where add 2 document maximum.

    Race condition mean more than 2 document may be add because client code is use setData. For example:

    Firestore.instance.collection(‘collection').document('document').collection('subCollection’).document(subCollectionDocument2).setData({
      ‘document2’: documentName,
    });
    

    I am try use transaction to make sure maximum 2 document are add. So if collection has been change (For example new document add to collection) while transaction run, the transaction will fail.

    But I am read docs and it seem transaction use more for race condition where set field in document, not add document in subcollection.

    For example if try implement:

    Firestore.instance.collection(‘collection').document('document').collection('subCollection').runTransaction((transaction) async {
    
    }),
    

    Give error:

    error: The method 'runTransaction' isn't defined for the class 'CollectionReference'.

    Can transaction be use for monitor change to subcollection?

    Anyone know other solution?

  • FlutterFirebase
    FlutterFirebase over 4 years
    Thanks for reply! But I no see how Cloud Functions can stop race condition. As Doug explain they not work synchronous
  • Frank van Puffelen
    Frank van Puffelen over 4 years
    Correct. But they run in a trusted environment, so you can write code to ensure your business rules are followed. For example: 1) load all documents in the collection, 2) count them, 3a) if there are more then 2, send an error back, 3b) if there are fewer than 2, write the document from the user.
  • FlutterFirebase
    FlutterFirebase over 4 years
    Thanks. I am try test this now. But in theory I still think there will be result where more than 2 document may be in collection. Because if 2 function instance running concurrently and count docs in collection and see only 1 doc. Then they will both write to collection. So issue is time lag between count document in collection (step 2) and write (step 3). I am correct?
  • Frank van Puffelen
    Frank van Puffelen over 4 years
    That's where transactions come in. You'd still use a transaction in the server-side code, you'd just be guaranteeing that this is the only way to write these docs.
  • FlutterFirebase
    FlutterFirebase over 4 years
    Thanks! But you say monitor entire collection very inefficient?
  • Frank van Puffelen
    Frank van Puffelen over 4 years
    "While that is technically possible for small collections, it's likely to be very inefficient, and I've never seen it done in practice. Then again, for just the two documents in your collection it may be totally feasible to simply read them in the transaction."
  • FlutterFirebase
    FlutterFirebase over 4 years
    Thanks! I see what you say. I am try implement but cannot find how is possible to use transaction on collection. Even if load all documentreference into array and iterate I still get race condition because other operation may add document at same time. You can give example? I have try here: stackoverflow.com/q/54866672
  • Frank van Puffelen
    Frank van Puffelen over 4 years
    Is that really a different question from this one, where I'm already trying to help? At first glance it just looks like the same problem, but now with a better MCVE.
  • FlutterFirebase
    FlutterFirebase over 4 years
    My question here and you answer is in theory but no implement. My new question is how can actually implement use transaction for monitor entire subcollection