How to allow an access to the document for only one onUpdate function at the same time?

I have an app where every sale is registered as a document in the Tracking collection. Another collection is the Actuals. There are stored documents for each user with the totalValue field for the current month.

TotalValue is calculated in this way - when the sale is added or modified (onCreate / onUpdate functions) the value of the field totalValue is taken from the document, it's calculated (for example is incremented by 100 as there was a new sale added with value=100) and then pushed back to that document with the new value. (In fact there are many other fields that are calculated and I'm updating the whole document but let's have an example with only that totalValue.) The code looks like this:

exports.updateActuals = functions.region(util_1.getFunctionRegion()).firestore
  .document(`${trackingCollectionName}/{id}`)
  .onUpdate((change) => {
  const service = new TargetService();
  if (<TrackingDto>change.after.data().isDeleted) {
    return service.getAndDeleteFromActuals(change.before.id, <TrackingDto>change.before.data())
    .then((actual: ActualsDto) => admin.firestore().doc(`${actualsCollectionName}/${actual.id}`).set(actual.data))
    .catch(er => error(er))
  } else {
    return service.getAndUpdateActuals(change.before.id, <TrackingDto>change.after.data(), <TrackingDto>change.before.data())
    .then((actual: ActualsDto) => admin.firestore().doc(`${actualsCollectionName}/${actual.id}`).set(actual.data))
    .catch(er => error(er));
  }
});

If the user works in online mode this onUpdate function works like a charm. The problem occurs when the user works in offline mode. When he switched back to online mode all sales he has made during offline mode are sent to firebase. So for example at the same time, 10 onUpdate functions will be triggered by those registered sales. It means that his document from the "Actuals" collection will be edited by 10 onUpdate functions. This causes wrong calculations as they are sharing the same value (totalValue field). Example of the problem:

  1. ...
  2. totalValue = 100
  3. onUpdate_1 takes totalValue=100 and adds 10
  4. totalValue = 110
  5. onUpdate_2 takes totalValue=110 and adds 20
  6. onUpdate_3 takes totalValue=110 and adds 30
  7. totalValue = 140
  8. ...

Is there a way to force onUpdate to wait until a specific document will be not in use or delay adding sales documents to firebase (but not as threads on the app side)? Is there another option?

1 answer

  • answered 2021-09-27 13:30 Frank van Puffelen

    If the new value of a field depends on its current value, you should always use a transaction - or an atomic increment operation to prevent the problem you now have.

    In this case I'd use admin.firestore.FieldValue.increment(10), which ensures the database itself will read-increment-write the value and thus prevent the problem you're now seeing.


    You can also tell Cloud Functions to never run more than once instance of a Cloud Function at a time by setting maxInstances in your code.

How many English words
do you know?
Test your English vocabulary size, and measure
how many words do you know
Online Test
Powered by Examplum