【问题标题】:firestore cloud functions onCreate/onDelete sometimes immediately triggered twicefirestore 云函数 onCreate/onDelete 有时会立即触发两次
【发布时间】:2018-07-21 23:55:04
【问题描述】:

我偶尔会在 onCreate 和 onDelete 触发器中观察到这种行为。

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

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

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

【问题讨论】:

  • noteId 里面有什么?
  • @PeterHaddad 不幸的是,我没有记录这个特定调用的参数。我现在已经添加了日志,如果再次发生这种情况,我会更新问题。当它发生在 onDelete 时,我实际上已经验证它会为同一个文档 id 触发两次 onDelete。

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


【解决方案1】:

查看 Cloud Firestore 触发器Limitations and Guarantees

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

有一个Firecast video 提供了实现幂等性的技巧。

还有两个 Google 博客帖子:the firstthe second

【讨论】:

  • 哇,谢谢您的回复。我不敢相信我在阅读文档时错过了这一点。将致力于使函数具有幂等性。
  • 您可以使用 firebase 传递给您的函数的eventId 来帮助解决如here 所述的无能。
  • @saranpol:我认为这个问题在 1.0.0 版本中仍然存在。
  • 好的,谢谢@BobSnyder 我想我会使用 EventContext.eventId 和 transaction 来解决这个问题
  • 我是唯一一个发现这种行为完全离谱的人吗?这是否意味着我需要在我不想被随机运行两次的每个函数上实现一个 eventID 系统?谷歌怎么能指望我们在生产环境中使用 Firebase 来做出如此怪异的行为呢?真是个无赖,说真的。
【解决方案2】:

根据@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
}

【讨论】:

  • 这笔交易似乎是对这个想法的明智补充。也许取决于您处理的事件数量,最好添加时间戳并清理 X 天之前的内容?
  • 通过替换“/”字符,这不会引入与其他事件ID的可能冲突吗?
  • 由于我假设 ID 是随机生成的,我认为这不太可能
【解决方案3】:

就我而言,我尝试使用 eventIdtransaction 来防止 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)
  })
})

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2018-09-14
    • 2022-01-14
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-01-02
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多