如何在 Cloud Function 触发器中使 API 调用幂等

How to make API calls idempotent in a Cloud Function trigger

我有一个点击 API 的 onCreate 云函数,但我需要确保它永远不会点击两次。云函数触发器可以执行多次,我们鼓励使用 eventId 来实现幂等性。这是我目前拥有的:

    let ship_ref = admin.firestore().collection('shipments').doc(event_id)
    return admin.firestore().runTransaction(transaction => {
      return transaction.get(ship_ref)
      .then(ship_doc => {
        if (ship_doc.exists) {
          console.log('label already generated')
          return null 
        }
        // generate the label
        return axios({
          method: 'post',
          ...rest of the axios config
        })
        .then(label_response => {
          const label_data = label_response.data

          // add the label data to the shipment collection
          transaction.set(ship_ref, {
            ...sale_data,
            label_data,
            tracking_url_data,
            completed_sale_ref: snap.ref
          })
          return null
        })
        .catch(e => {
          // handle fail
        }) 
      })
    })
    .then(() => {
      // transaction complete
    })
    .catch(e => {
      // error
    })

这是我关心的问题。根据 docs,如果文档的内容受到并发操作的影响,firestore 事务将再次 运行。在这种情况下,如果它“再次 运行s”,它将 运行 axios 再次调用,这对我来说很糟糕。

如果云函数触发(在本例中为 onCreate)运行 不止一次,它是否有可能并发并导致竞争条件?如果是这样,那么 axios 请求可能会被多次发出,这是我必须避免的。

预先感谢您的帮助。

If a cloud function trigger (onCreate in this case) runs more than once, is there ever a chance that it will be concurrent and cause a race condition?

您可以假设重试不会与之前的调用重叠。可以这样想:如果您在 phone 上呼叫某人,而他们没有回应,同时再次呼叫他们并不会真正提高您联系到他们的机会。挂断电话后,您需要等待一段时间才能再次尝试。这就是这里发生的事情。如果 Cloud Functions 与函数之间的消息由于某种原因第一次被删除,它可能会尝试再次调用该函数。

除此之外,您不应该在事务中执行有状态的工作。这本身就非常糟糕。交易本身有自己的重试机制,以防该交易中的文档发生争用。事务处理程序应该是完全无状态的,并且只对正在处理的文档之外的静态数据进行操作。这在 documentation:

中说明

When using transactions, note that ... Transaction functions should not directly modify application state.

Also:

Do not modify application state inside of your transaction functions. Doing so will introduce concurrency issues, because transaction functions can run multiple times and are not guaranteed to run on the UI thread. Instead, pass information you need out of your transaction functions.

对 axios 的调用应被视为应用程序状态,尤其是因为它是 post。我会更进一步说事务处理程序也不应该阻塞。他们需要尽快 运行 以减少争用。