2014-04-14 92 views
2

我們有一個數據集,它在應用程序處理數據集時會增長。經過漫長的討論後,我們決定暫時不需要阻塞或異步API,我們會定期查詢我們的數據存儲。查詢日益增長的數據集

我們認爲兩個選項來設計的API進行查詢我們的存儲:

  1. 的查詢方法返回的數據和指示天氣,我們可能有更多的數據的標記的快照。當我們完成對最後一次返回的快照的迭代時,我們再次查詢以獲取其他數據的另一個快照。
  2. 查詢方法返回數據上的「實時」迭代器,當此迭代器前進時,它將返回以下選項之一:數據可用,不再有數據,可能有更多數據。

我們使用C++,並且因爲這個問題的範圍超出了原因,我們借用了.NET樣式枚舉器API。這裏有一些代碼來演示這兩種選擇。你更喜歡哪個選項?

/* ======== FIRST OPTION ============== */ 

// similar to the familier .NET enumerator. 
class IFooEnumerator 
{ 
    // true --> A data element may be accessed using the Current() method 
    // false --> End of sequence. Calling Current() is an invalid operation. 
    virtual bool MoveNext() = 0; 
    virtual Foo Current() const = 0; 
    virtual ~IFooEnumerator() {} 
}; 

enum class Availability 
{ 
    EndOfData, 
    MightHaveMoreData, 
}; 

class IDataProvider 
{ 
    // Query params allow specifying the ID of the starting element. Here is the intended usage pattern: 
    // 1. Call GetFoo() without specifying a starting point. 
    // 2. Process all elements returned by IFooEnumerator until it ends. 
    // 3. Check the availability. 
    //  3.1 MightHaveMoreDataLater --> Invoke GetFoo() again after some time by specifying the last processed element as the starting point 
    //         and repeat steps (2) and (3) 
    //  3.2 EndOfData --> The data set will not grow any more and we know that we have finished processing. 
    virtual std::tuple<std::unique_ptr<IFooEnumerator>, Availability> GetFoo(query-params) = 0; 
}; 


/* ====== SECOND OPTION ====== */ 

enum class Availability 
{ 
    HasData, 
    MightHaveMoreData, 
    EndOfData, 
}; 


class IGrowingFooEnumerator 
{ 
    // HasData: 
    //  We might access the current data element by invoking Current() 
    // EndOfData: 
    //  The data set has finished growing and no more data elements will arrive later 
    // MightHaveMoreData: 
    //  The data set will grow and we need to continue calling MoveNext() periodically (preferably after a short delay) 
    //  until we get a "HasData" or "EndOfData" result. 
    virtual Availability MoveNext() = 0; 
    virtual Foo Current() const = 0; 
    virtual ~IFooEnumerator() {} 
}; 

class IDataProvider 
{ 
    std::unique_ptr<IGrowingFooEnumerator> GetFoo(query-params) = 0; 
}; 

更新

鑑於目前的答案,我有一些澄清。辯論主要在於界面 - 它的表現力和直觀性表現了對不斷增長的數據集的查詢,並在某個時間點停止增長。這兩個接口的實現是可能沒有競爭條件(AT-至少我們相信如此),因爲以下屬性:

  1. 首屆選項可以正確地實施,如果對迭代器+國旗代表的快照系統在查詢時。獲取快照語義不成問題,因爲我們使用數據庫事務。
  2. 第二個選項可以在第一個選項正確實現的情況下執行。第二個選項的「MoveNext()」將在內部使用類似第一個選項的內容,並在需要時重新發出查詢。
  3. 數據集可以從「可能有更多數據」更改爲「數據結束」,但反之亦然。因此,如果我們錯誤地因爲競爭條件返回「可能有更多數據」,我們只會得到一個小的性能開銷,因爲我們需要再次查詢,並在下一次我們將收到「數據結束」。

回答

0

「調用的getFoo()一段時間通過指定的最後處理的元素爲出發點,之後再次」

你如何打算這樣做?如果它使用較早返回的IFooEnumerator,則在功能上這兩個選項是等效的。否則,讓調用者銷燬「枚舉器」,然後很長時間後調用GetFoo()繼續迭代意味着您失去了監視客戶端對查詢結果持續興趣的能力。現在可能你沒有這個需求,但我認爲這是一個糟糕的設計,無法在整個結果處理過程中跟蹤狀態。

+0

看到更新以上 – Alex

+0

那麼你更喜歡第二個選項,與「活」迭代器? – Alex

+0

@亞歷山大,那就是那個。乾杯。 –

0

這真的取決於很多東西能否整個系統將在所有的工作(不進入細節關於你的實際執行):

  1. 不管你如何扭轉它,會有檢查之間的競爭條件爲「是否有更多的數據」和更多的數據被添加到系統。這意味着嘗試捕獲最後幾個數據項可能毫無意義?
  2. 您可能需要限制「有更多數據」的重複運行次數,否則最終會導致無限循環「處理最後一批時出現新數據」。
  3. 知道數據是否已被更新是多麼容易 - 如果所有更新都是「新項目」,並且新ID依次更高,則可以簡單地查詢「X上面是否有數據」,其中X是您的上一個ID 。但是,例如,如果您計算了數據中有多少項目的屬性Y設置爲值A,並且數據可能會在數據庫中的任何位置進行更新(例如,目前出租車目前處於哪個位置的數據庫) GPS每隔幾秒鐘就有一次,並且有數千輛汽車,可能很難確定自上次讀取數據庫以來哪些汽車有更新)。

至於你的實現,在選項2中,我不確定你的意思是MightHaveMoreData狀態 - 無論它有或沒有,對吧?在這種情況下重複輪詢更多的數據是一個糟糕的設計 - 因爲您將永遠無法100%確定在從獲取最後一個數據到處理完成所花費的時間內沒有提供「新數據」並且採取行動(顯示,用於購買股票市場上的股票,停止火車或者一旦處理完您的新數據後您想要做的任何事情)。

+0

查看更新以上 – Alex

+0

所以,即使有更新,我也沒有看到任何僞造我的陳述1 - 你永遠不會有「保證沒有更新,因爲你提取了數據」。那麼就出現了「爲什麼打擾」這個問題 - 爲什麼不拍第一個快照。它真的改變了那麼多嗎? –

+0

在我們的案例中,這並非毫無意義。我們有一種知道數據集結束並且不會再增長的方式。這種方式與問題本身無關。只要相信我們:) – Alex

0

讀寫鎖可能有幫助。許多讀者可以同時訪問數據集,只有一個作者。 這個想法很簡單: - 當你需要只讀訪問時,閱讀器使用「讀取塊」,它可以與其他閱讀共享,並與作家獨佔; - 當你需要寫入權限時,寫入者使用寫鎖,這對讀者和作者都是唯一的;

+0

看到上面的更新 – Alex