【问题标题】:How to deal with firebase trigger function execution order如何处理firebase触发函数执行顺序
【发布时间】:2018-02-25 16:58:02
【问题描述】:

我有一个 firebase http 函数,可以将数据附加到 firebase 数据库列表。第二个函数被配置为在列表发生变化时对其进行处理,然后更新一些摘要数据。就我而言,这些更新是突发的。我正在使用 node.js firebase 函数。

查看 firebase 日志,当我从一个空列表开始时,我看到了这个序列:

  1. 从 http 添加到列表 - 列表有 1 个元素
  2. 从 http 添加到列表 - 列表有 2 个元素
  3. 从 http 添加到列表 - 列表有 3 个元素
  4. 从 http 添加到列表 - 列表有 4 个元素
  5. 用 1 个元素汇总列表
  6. 用 3 个元素汇总列表
  7. 用 4 个元素汇总列表
  8. 用 2 个元素汇总列表

我的问题是摘要只包含 2 个元素而不是 4 个。

看起来汇总触发器函数是并行调用而不是顺序调用的,因此当多个触发器非常接近时,最后一个完成的触发器可能是第一个触发而不是最后一个触发的函数之一。

可以使用哪些方法来确保汇总计算具有“所有数据”并且先前运行较慢的汇总计算不会覆盖后面的汇总计算? firebase 函数触发器可以序列化以按照它们启动的顺序执行吗?

理想情况下,我希望避免在突发事件发生时计算摘要 N 次,因此一些解决方案可以在将来的短时间内“安排”摘要,然后在出现新事件时取消并重新安排做个好人。

【问题讨论】:

    标签: firebase firebase-realtime-database google-cloud-functions


    【解决方案1】:

    对于来自多个客户端或调用的事件的传递顺序绝对不能保证。事实上,你甚至很难定义事件的时间,因为在客户端发出请求的那一刻和你的函数中的最终工作为该客户端完成的那一刻之间有很多可变的移动部分。

    您能做的最好的事情是假设多个客户端都在有效地向您的函数发送无序请求,并使用数据库事务来防止它们进行的写入发生任何类型的冲突。

    如果您绝对必须序列化事物,则需要让其他程序或代理定义正确的序列并序列化所有工作,确保所有写入都以可预测的序列发生。

    如果您想阅读有关订购为何困难的详细讨论,请read this article(Cloud Functions 构建在 pubsub 之上)。

    【讨论】:

    • 在 firebase 的上下文中,在为下一个服务之前完成一个“onChange”似乎并不合理。特别是因为它以新状态传递给处理程序。我只想让最终摘要知道它实际上使用了所有可用数据。我可以添加定期更新,但数据触发更新似乎更干净。
    • 您如何建议在所有可能运行它的服务器实例中同步所有正在运行的 onChange 实例,用于单个路径(使用通配符)?尤其是考虑到他们都可能在做阻塞工作,而这些工作可能需要大量可变的时间?
    【解决方案2】:

    我的解决方法是存储一个 admin.database.ServerValue.TIMESTAMP 和列表添加,并在结果计算器中验证它是否为最新时间戳生成了结果。如果没有,它会再试一次。在大多数情况下,它不需要重新计算摘要,因为我的输入源通常是零星的单个列表添加而不是集中添加。我将其实现为一个返回 Promise 的函数,如果需要重新计算,该函数会调用自身。这是顺序:

    1. 读取当前列表和时间戳
    2. 计算汇总结果并存储它们
    3. 再次读取时间戳
    4. 如果时间戳不同,则转到 1,否则完成

    代码如下:

    /// return a Promise that new summary and detail results will be posted
    function updateResults(regattaId, lapdataTS, depth) {
      if (depth > 10) {
        return Promise.reject("Too many recomputes");
      }
      return admin.database().ref('/eventdata/'+regattaId).once('value')
      .then(function (snapshot) {
        const rawdata = snapshot.val(); 
    
        if (rawdata.lapdataTS === lapdataTS) {
            // console.log("already computed");
            return Promise.resolve();
        }
        lapdataTS = rawdata.lapdataTS ? rawdata.lapdataTS : null;
        const results = regattaCalc.computeResults(rawdata);
    
        var updates = {};
        updates['results/' + regattaId] = results;
        updates['summary/' + regattaId] = results.regattaInfo;
        return admin.database().ref().update(updates);
      }).then(function () {
        // read last TS and see if it matches our summary
        return admin.database().ref('/eventdata/'+regattaId+'/lapdataTS').once('value');
      }).then(function (snapshot) {
       if (snapshot.val() === lapdataTS) {  
           return Promise.resolve();
       } else {
           //console.log("Need to calc again");
           return updateResults(regattaId, lapdataTS, depth++);
       }
      }).catch((reason) => {
        console.log("Error generating summary: " + reason);
        return Promise.reject(reason);
      });
    }
    
    exports.compupteResults = functions.database.ref('/eventdata/{regattaId}').onWrite(event => {
    return updateResults(regattaId,null,0);
     });
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2020-04-18
      • 1970-01-01
      • 1970-01-01
      • 2010-09-10
      • 1970-01-01
      • 1970-01-01
      • 2018-05-17
      • 1970-01-01
      相关资源
      最近更新 更多