2017-09-16 42 views
0

我有一个数据追加到一个火力点数据库列表中的火力HTTP功能。第二个功能被配置为在列表发生变化时处理列表,然后更新一些汇总数据。在我的情况下,这些更新爆发。我使用的是node.js的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次总结,因此某些解决方案可以在未来的某个短时间内“安排”摘要,然后取消并重新安排新事件发生会好的。

回答

1

但绝对交付从多个客户端或调用即将发生的事件的顺序不能保证。事实上,即使定义了事件的时间,你也很难,因为在客户端发出请求的那一刻到客户端完成对该客户端的最终工作完成的时刻之间,会有很多可变的移动部分。

你能做的最好的事情是假设多个客户端都有效地发出序请求你的功能,并使用数据库事务,以防止任何类型的碰撞对他们所做的写入。

如果你绝对必须序列的事情,你需要有一些其他程序或代理定义正确的序列和序列化的各项工作,确保所有的写操作在可预测的顺序发生。

+0

这似乎并不合理e在火力点的背景下完成一个'onChange',然后再维修下一个。特别是因为它将新状态传递给处理程序。我只是希望最后的总结知道它实际上使用了所有可用的数据。我可以添加定期更新,但数据触发更新看起来更清晰。 – Glenn

+0

您会如何建议将所有正在运行的服务器实例中的onChange的所有正在运行的实例(对于单个路径(使用通配符))进行同步?尤其是考虑到他们可能都在做阻塞工作,可能需要大量的时间? –

0

我的解决方法是存储与列表中添加一个admin.database.ServerValue.TIMESTAMP并在其生产的最新时间戳结果的结果计算器验证。如果不是,它会再次尝试。在大多数情况下,它不需要重新计算摘要,因为我的输入源通常是零星单个列表添加而不是集中添加。我把它作为一个返回Promise的函数实现,如果需要重新计算,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); 
});