JJonasen
JJonasen

Reputation: 11

How to update the same document with a read from the same collection in an onUpdate function

I'm trying to update the same document which triggered an onUpdate cloud function, with a read value from the same collection. This is in a kind of chat app made in Flutter, where the previous response to an inquiry is replicated to the document now being updated, for easier showing in the app.

The code does work, however when a user quickly responds to two separate inquiries, they both read the same latest response thus setting the same previousResponse. This must be down to the asynchronous nature of flutter and/or the cloud function, but I can't figure out where to await or if there's a better way to make the function, so it is never triggering the onUpdate for the same user, until a previous trigger is finished. Last part also sound a bit like a bad idea.

So far I tried sticking the read/update in a transaction, however that only seems to work for the single function call, and not when they're asynchronous. Also figured I could fix it, by reading the previous response in a transaction on the client, however firebase doesn't allow reading from a collection in a transaction, when not using the server API.

async function setPreviousResponseToInquiry(
  senderUid: string, 
  recipientUid: string, 
  inquiryId: string) {

    return admin.firestore().collection('/inquiries')
    .where('recipientUid', '==', recipientUid)
    .where('senderUid', '==', senderUid)
    .where('responded', '==', true)
    .orderBy('modified', 'desc')
    .limit(2)
    .get().then(snapshot => {
      if (!snapshot.empty && 
          snapshot.docs.length >= 2) {
        return admin.firestore()
          .doc(`/inquiries/${inquiryId}`)
          .get().then(snap => {
          return snap.ref.update({ 
            previousResponse: snapshot.docs[1].data().response 
          })
        })
      }
    })
}

Upvotes: 1

Views: 278

Answers (1)

Frank van Puffelen
Frank van Puffelen

Reputation: 598708

I see three possible solutions:

  1. Use a transaction on the server, which ensures that the update you write must be based on the version of the data you read. If the value you write depends on the data that trigger the Cloud Function, you may need to re-read that data as part of the transaction.
  2. Don't use Cloud Functions, but run all updates from the client. This allows you to use transactions to prevent the race condition.
  3. If it's no possible to use a transaction, you may have to include a custom version number in both the upstream data (the data that triggers the write), and the fanned out data that you're updating. You can then use security rules to ensure that the downstream data can only be written if its version matches the current upstream data.

I'd consider/try them in the above order, as they gradually get more involved.

Upvotes: 1

Related Questions