'firestore cloud functions onCreate/onDelete sometimes immediately triggered twice

I have observed this behavior occasionally with both onCreate and onDelete triggers. enter image description here

Both the executions happened for the same document created in firestore. There's only one document there so I don't understand how it could trigger the handler twice. the handler itself is very simple:

module.exports = functions.firestore.document('notes/{noteId}').onCreate((event) => {
  const db = admin.firestore();
  const params = event.params;
  const data = event.data.data();
  // empty
});

this doesn't happen all the time. What am I missing?



Solution 1:[1]

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
}

Solution 2:[2]

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)
  })
})

Solution 3:[3]

Late to the party, I had this issue but having a min instance solved the issue for me Upon looking @xaxsis attached screenshot, my function took almost the amount of time about 15 seconds for the first request and about 1/4 of that for the second request

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 Simon Bengtsson
Solution 2
Solution 3 Mark Carlton