google-cloud-firestoredatabase-concurrency

Firestore transaction based on non existent document (Collection level locking not available)


Based on this SO answer I came to know that firestore does not have collection level locking in a transaction. In my case, I have to ensure that the username field in users collection is unique before I write to a collection. For that, I write a transaction that does this:

  1. Executes a query on users collection to check if a document exists where username=something
  2. If it does exist, fail and return error from transaction
  3. If it does not exist, just run the write operation for the userId I want to update/create.

Now the issue here is that if two clients simultaneously try to run this transaction, both might query the collection and since the collection is not locked, one client might insert/update a document in collection while other won't see it.

Is my assumption correct? And if yes, then how to deal with such scenarios?


Solution

  • What you're trying to do is actually not possible to do atomically, as it's not possible to transact safely on a document that you can't identify with an ID. The problem here is that a transaction is only "safe" if you can get() the specific document to add or modify. Since you can't get() a document using a field value in the document, you're at a loss.

    If you want to ensure uniqueness of anything in Firestore, that uniqueness will need to be coded into the document ID itself. In the simplest case, you can use the username as the ID of a document in a new collection. If you do that, your transaction can simply get() the required document by username, check to see if it exists, then write the document if it doesn't. Else, the transaction can fail.

    Bear in mind that because there are limitations to document IDs in Firestore, you might need to escape or encode that username if your usernames could possibly violate the rules.