如何在云函数中进行幂等聚合?

时间:2018-03-11 05:55:05

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

我正在使用Firebase云功能,该功能可更新数据库中某些文档的某些聚合信息。这是一个非常简单的功能,只需将1个文档计数加1即可。很像example function found in the Firestore documentation

我刚刚注意到,在创建单个新文档时,该函数被调用了两次。请参见下面的屏幕截图,并注意记录的文档ID(iDup09btyVNr5fHl6vif)重复两次:

enter image description here

经过一番挖掘后,我发现this SO post表示以下内容:

  

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

(来自Firestore文档:Limitations and guarantees

这导致我的文档出现问题。如上所述的云函数意味着是幂等的(换句话说,无论函数运行一次还是多次运行,它们改变的数据应该相同)。然而,我之前(我的眼睛)链接的example function并不是幂等的:

exports.aggregateRatings = firestore
  .document('restaurants/{restId}/ratings/{ratingId}')
  .onWrite(event => {
    // Get value of the newly added rating
    var ratingVal = event.data.get('rating');

    // Get a reference to the restaurant
    var restRef = db.collection('restaurants').document(event.params.restId);

    // Update aggregations in a transaction
    return db.transaction(transaction => {
      return transaction.get(restRef).then(restDoc => {
        // Compute new number of ratings
        var newNumRatings = restDoc.data('numRatings') + 1;

        // Compute new average rating
        var oldRatingTotal = restDoc.data('avgRating') * restDoc.data('numRatings');
        var newAvgRating = (oldRatingTotal + ratingVal) / newNumRatings;

        // Update restaurant info
        return transaction.update(restRef, {
          avgRating: newAvgRating,
          numRatings: newNumRatings
        });
      });
    });
});

如果该函数运行一次,则聚合数据会增加,就好像添加了一个评级一样,但如果它在相同评级上再次运行,则会增加汇总数据,就像添加了两个评级一样。

除非我误解了幂等的概念,否则这似乎是一个问题。

是否有人对如何通过云功能以幂等方式增加/减少Cloud Firestore中的聚合数据有任何想法?

(当然不涉及查询汇总数据的每一个文件)

奖励积分:有人知道在Cloud Firestore退出测试版后,功能是否仍然需要是幂等的?

1 个答案:

答案 0 :(得分:4)

Cloud Functions文档提供了有关如何make retryable background functions idempotent的一些指导。您最感兴趣的要点是:

  

在函数外部进行事务检查,与代码无关。例如,在某处记录已经处理了给定事件ID的持久状态。

传递给你的函数的event参数在其上有一个eventId属性是唯一的,但是当它重试时它将是相同的。您应该使用此值来确定事件所采取的操作是否已经发生,因此您知道如有必要,可以第二次跳过该操作。

至于如何确切地检查您的功能是否已经处理了事件ID,有很多方法可以做到这一点,这取决于您。

如果你觉得它根本不值得,你总是可以选择不使你的函数具有幂等性,或者在某些(可能是罕见的)情况下可能有不正确的计数。