事件溯源在物聯網裝置資料同步中應用案例 - eventstore

banq發表於2021-12-24

我們是一家小型軟體諮詢公司,專門從事涉及函數語言程式設計的專案工作。那是 2015 年,我們的一個客戶是(實際上仍然是)一家大型汽車連鎖店。

他們打電話給我們是因為他們的 IT 戰略發生了變化:他們經營的商店被分成多條車道,每條車道一次為一輛車提供服務。每條車道的盡頭是一個金屬達文波特辦公桌,上面有各種硬體——印表機、診斷裝置等。2015 年之前,達文波特包含一臺固定式 PC。客戶正準備擺脫這些 PC,而是為每位員工發放行動式裝置(基本上是堅固耐用的平板電腦)。

現在有一個問題:當員工(和他們的裝置)搬到不同的車道(或不同的商店)時,他們需要重新配置它以連線到本地 davenport 的裝置。這是一個繁瑣的過程,需要手動輸入裝置背面的 9 點式 MAC 地址等。 所以客戶要求我們開發一個可以自動化這個過程的軟體,有以下要求:

  • 每臺裝置只能配置一次;然後它的配置應該自動在所有裝置上可用。
  • 該軟體應該在容量有限的不可靠無線網路的環境中執行。
  • 該組織無法操作中央伺服器。

這些要求意味著裝置需要在本地儲存配置資料以確保可用性並在它們之間傳達配置更改。

早些時候,我們意識到我們無法使用通常的永續性方法將當前配置狀態儲存在資料庫中。在永遠線上的環境中保持一致性已經夠難了,但是隨著裝置進出網路,很明顯這將是無法管理的。

我們的想法圍繞著事實的想法,即真實的事物。

“Davenport 123 連線到 IP x 上的 Acme PrintAce”不是事實:它只適用於特定時間段,現在可能不再適用。

但是,

“安妮特·穆勒 (Annette Mueller) 於 2021 年 12 月 6 日表示,Davenport 123 連線到 IP x 的 Acme PrintAce”

——這是我們可以儲存的事實,如果我們在場見證的話。

見證的事實純粹是累積的,這大大簡化了同步和一致性:當兩個裝置相遇時,它們只需要在各自相對的裝置還不知道時交換那些事實。

事實並不是孤立存在的。如果“Hans Mayer 於 2021 年 12 月 6 日宣告,Davenport 123 連線到 IP y 的 Highres WriteQueen”,那麼該事實與前一個有關。這兩個事實都採用有關 Davenport 123 配置當前狀態的屬性陳述的形式。它們可以通過以下方式相關聯:

  • 一個事實“取代”了另一個。
  • 這兩個事實是相互矛盾的。

系統如何區分兩者?好吧,如果一個事實是在已知另一個事實的情況下做出的,那麼第二個事實將取代第一個事實,因為它暗示了因果關係。如果不是,即如果兩個事實相互獨立,就會發生衝突。

 

當我們開始設計系統時,我們沉迷於衝突。我們做了兩個假設:

  • 衝突很少發生,因為我們假設使用者在進行配置更改時會站在裝置附近。
  • 對於雙向衝突,有正確的方法和錯誤的解決方法——我們需要確保系統選擇正確的方法。

結果證明這兩個假設都是錯誤的,直到系統執行時我們才注意到這一點。

...

我們年輕而天真:我們沒有考慮同步演算法的不合理有效性,它會在任何人有機會解決衝突之前將兩個相互衝突的事實傳播到許多裝置。這是一個 UI 失敗——使用者不知道為什麼他們會看到這個選擇,並且很難弄清楚誰應該按下什麼按鈕。

例如,我們迅速轉向自動建立“合併事實”,例如,更喜歡最近的事實而不是舊的事實。不幸的是,這幾乎以災難告終,因為這發生在所有具有相互矛盾的事實的裝置上,結果證明是相當多的。他們會建立多個合併事實,這些事實也必須合併,從而啟動無休止的合併級聯.

我們做了兩個改變:

  • 沒有更多的自動合併事實
  • 面對一個Conflict值,系統會確定性地選擇其中一個值

我們完全希望使用者在系統選擇錯誤值時抱怨,但這種情況從未發生。使用者只需更改配置,手動新增合併事實。

檢視資料,儘管我們假設物理上接近,但我們對衝突發生的頻率感到驚訝。儘管我們從未真正弄清楚為什麼這些衝突如此頻繁,但我們也不應該感到驚訝:在呈現手動合併 UI 時,我們假設只有一個系統,而實際上是一個分散式系統在工作。

 

由於裝置之間只有弱連線和不可靠的網路連線,我們不得不在本地儲存事實。我們決定使用 SQLite,它以其簡單性、可擴充套件性和可靠性而聞名。

使用合適的資料庫還有另一個令人愉快的副作用:一些使用者在犯了配置錯誤時要求“倒轉時間”。我們通過收集與給定地點相關的所有事實並按時間順序顯示它們的列表來實現這一點。然後使用者可以選擇過去的時間點。系統現在可以簡單地收集與該地點相關的所有事實,並使用它來建立一個虛擬資料庫。

我們使用了一個 monad 來實現資料庫的依賴注入。最初,這是為了在沒有磁碟儲存的情況下進行測試。在這裡,我們能夠重新使用此功能來建立資料庫介面的臨時實現,該介面只能訪問截至該時間點的事實子集。由此,系統可以使用現有程式碼重建那個時間點的狀態,並在當前狀態之上生成新的事實,以重新建立過去的狀態。

因此,我們的“事實資料庫”的設計已經方便地提供了實現這個全新的時間機器所需的所有部分。

 

“事實資料庫”設計的一個關鍵方面是它支援跨裝置的高效同步。各種事件會觸發此類同步:配置更改或裝置進入 WiFi 網路。發生這種情況時,相關裝置會廣播一個 UDP 資料包,請求同步夥伴。

一旦找到,系統就會將所有事實組裝成每個裝置上的Merkle 樹。然後這些裝置協作以廣度優先的方式向下遍歷 Merkle 樹。Merkle 樹在每個節點都有一個雜湊碼,用於標識其子樹中的所有資料。在每一層,雙方確定裝置之間匹配的雜湊值,將它們從遍歷中移除。底部是需要轉移到另一方的事實列表。

頂部雜湊值包含在觸發同步的 UDP 廣播中:任何接收到它的裝置都可以將它與自己的頂部雜湊值進行比較。如果它們匹配,則不需要同步。

 

應該很容易將這個系統從根本上看作是一個事件溯源系統。只需將“事件”替換為“事實”,事件溯源中的技術和技術就適用。

詳細點選標題見原文

 

相關文章