2017-09-16 43 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); 
});