firestore cloud functions onCreate/onDelete sometimes immediately triggered twice

后端 未结 3 1143
再見小時候
再見小時候 2020-12-05 16:27

I have observed this behavior occasionally with both onCreate and onDelete triggers.

Both the executions happened for the same document created in firestore. There

相关标签:
3条回答
  • 2020-12-05 17:00

    See the Cloud Firestore Triggers Limitations and Guarantees:

    Delivery of function invocations is not currently guaranteed. As the Cloud Firestore and Cloud Functions integration improves, we plan to guarantee "at least once" delivery. However, this may not always be the case during beta. This may also result in multiple invocations for a single event, so for the highest quality functions ensure that the functions are written to be idempotent.

    There is a Firecast video with tips for implementing idempotence.

    Also two Google Blog posts: the first, the second.

    0 讨论(0)
  • 2020-12-05 17:08

    In my case I try to use eventId and transaction to prevent onCreate sometimes triggered twice

    (you may need to save eventId in list and check if it exist if your function actually triggered often)

    const functions = require('firebase-functions')
    const admin = require('firebase-admin')
    const db = admin.firestore()
    exports = module.exports = functions.firestore.document('...').onCreate((snap, context) => {
    
      const prize = 1000
      const eventId = context.eventId
      if (!eventId) {
        return false
      }
    
      // increment money
      const p1 = () => {
        const ref = db.doc('...')
        return db.runTransaction(t => {
            return t.get(ref).then(doc => {
              let money_total = 0
              if (doc.exists) {
                const eventIdLast = doc.data().event_id_last
                if (eventIdLast === eventId) {
                  throw 'duplicated event'
                }
                const m0 = doc.data().money_total
                if(m0 !== undefined) {
                  money_total = m0 + prize
                }
              } else {
                money_total = prize
              }
              return t.set(ref, { 
                money_total: money_total,
                event_id_last: eventId
              }, {merge: true})
            })
        })
      }
    
      // will execute p2 p3 p4 if p1 success
      const p2 = () => {
        ...
      }
    
      const p3 = () => {
        ...
      }
    
      const p4 = () => {
        ...
      }
    
      return p1().then(() => {
        return Promise.all([p2(), p3(), p4()])
      }).catch((error) => {
        console.log(error)
      })
    })
    
    0 讨论(0)
  • 2020-12-05 17:13

    Based on @saranpol's answer we use the below for now. We have yet to check if we actually get any duplicate event ids though.

    const alreadyTriggered = eventId => {
      // Firestore doesn't support forward slash in ids and the eventId often has it
      const validEventId = eventId.replace('/', '')
    
      const firestore = firebase.firestore()
      return firestore.runTransaction(async transaction => {
        const ref = firestore.doc(`eventIds/${validEventId}`)
        const doc = await transaction.get(ref)
        if (doc.exists) {
          console.error(`Already triggered function for event: ${validEventId}`)
          return true
        } else {
          transaction.set(ref, {})
          return false
        }
      })
    }
    
    // Usage
    if (await alreadyTriggered(context.eventId)) {
      return
    }
    
    0 讨论(0)
提交回复
热议问题