firestore云函数onCreate / onDelete有时会立即触发两次

时间:2018-02-11 20:02:48

标签: firebase google-cloud-functions google-cloud-firestore

我偶尔会在onCreate和onDelete触发器上观察到这种行为。 enter image description here

两个执行都发生在firestore中创建的同一文档中。那里只有一个文档,所以我不明白它是如何触发处理程序两次的。处理程序本身非常简单:

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

这不会一直发生。我错过了什么?

3 个答案:

答案 0 :(得分:8)

请参阅Cloud Firestore触发器Limitations and Guarantees

  

目前不保证传递函数调用。作为   Cloud Firestore和Cloud Functions集成改进,我们计划   保证“至少一次”交付。但是,这可能并非总是如此   测试期间的情况。 这也可能导致多次调用   对于单个事件,所以对于最高质量的功能确保   这些函数被写成幂等的。

Firecast video提供实施幂等的提示。

另外两个Google博文:the firstthe second

答案 1 :(得分:1)

在我的情况下,我尝试使用 eventId 交易来防止onCreate有时会触发两次

(你可能需要在列表中保存eventId,如果你的功能经常被触发,检查它是否存在)

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

答案 2 :(得分:1)

基于@saranpol的回答,我们现在使用以下内容。我们尚未检查是否确实得到了重复的事件ID。

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
}