天天看點

mit_6.824_2021_lab2D_log_compactionmit_6.824_2021_lab2D_log_compaction

mit_6.824_2021_lab2D_log_compaction

lab2D 主要是日志壓縮功能,實驗裡是采用論文的最簡單的方式,snapshot 壓縮快照,并且更簡單的是不分塊的形式;感覺主要有兩點需要注意:

  1. 快照功能的加入對原有邏輯的修改
  2. 快照功能的流程(InstallSnapshot, Snapshot, CondInstallSnapshot, applyCh)

lab2D_log_compaction

實驗内容

  1. 要實作快照功能,需要實作

    Snapshot

    CondInstallSnapshot

    InstallSnapshot RPC

  2. 還需要繼續使用修剪後的日志進行操作
  3. lab3 将會對 lab2 更徹底地測試快照功能,因為會壓測

實驗提示

  • 在單個

    InstallSnapshot

    RPC 中發送整個快照。不用分塊和 offset
  • Raft 日志不能再使用日志條目的絕對位置或日志的長度來确定日志條目索引;您将需要使用獨立于日志位置的索引方案。
  • 即使日志被修剪,仍然需要在

    AppendEntries

    RPC 中的新條目之前正确發送條目的術語和索引;這可能需要儲存和引用最新快照的

    lastIncludedTerm/lastIncludedIndex

    ,且兩者需要考慮被持久化
  • 使用

    persister.SaveStateAndSnapshot()

    将每個快照存儲在持久化對象中 。
  • 完整的 lab2 測試 (2A+2B+2C+2D) 的合理消耗時間是 8 分鐘的實時時間和一分半的 CPU 時間。

繼續從 lab2D 中提取出的提示有:

  • 整個 lab 的架構,可以檢視架構圖:https://pdos.csail.mit.edu/6.824/notes/raft_diagram.pdf;從分層的角度看,整個系統包括:client 層,service 狀态機層, raft 層,persister 持久化層
  • 除了 raft code 之外,還有上層服務(這裡叫 service),service 也需要拿到一份快照
  • service 調用

    Snapshot

    将快照應用給 raft
  • leader 調用

    InstappSnapshot

    rpc 請求将快照傳播給 followers
  • 當 follower 接收到

    InstallSnapshot

    RPC 後,必須将 raft 包含的快照送出給上層服務。通過

    applyCh <- ApplyMsg

    的方式;上層 service 從

    applyCh

    中讀,并使用

    CondInstallSnapshot

    以告訴 Raft 該服務需要安裝該快照
  • 可以在

    config.go

    中的

    applierSnap()

    中檢視上層服務調用快照的用法
  • 如果快照是舊快照,則

    CondInstallSnapshot

    拒絕安裝快照,即 Raft 在快照的

    lastIncludedTerm/lastIncludedIndex

    之後處理了條目

    這是因為 Raft 可能會在處理

    InstallSnapshot

    RPC 之後,并且在服務調用

    CondInstallSnapshot

    之前,處理其他 RPC 并在

    applyCh

    上發送消息。Raft 傳回到舊快照是不行的,是以必須拒絕舊快照。當您的實作拒絕快照時,

    CondInstallSnapshot

    應該隻傳回

    false,

    以便服務知道它不應該切換到快照(這一段我了解一切都是為了原子性應用快照)
  • 若快照是最新的,則 raft 修建其日志,然後傳回 true,并且服務應該在處理

    applyCh

    上的下一條消息之前切換到快照。
  • CondInstallSnapshot

    是為了讓 服務 和 raft 原子地切換到快照,可以以其他接口實作,你也可以始終傳回

    true

    的方式來實作 raft

實驗思路

關于

CondInstallSnapshot

這裡看資料,實作方式參差不齊,有的始終傳回 true,有的會實作邏輯,對這個 api 有解釋的,當時隻找到這篇文章

https://www.cnblogs.com/sun-lingyu/p/14591757.html

總結來說,是為了實作 service 和 raft 兩層應用快照的原子性

經過思考之後,這裡給出自己的了解,如若不對,歡迎提出改進和建議

  • Snapshot

    是上層 service 主動調用 api 通知 raft peer 建立快照并存儲到 persister 持久化層的;
  • InstallSnapshot

    這條鍊路是,leader 通知 follower,即 follower 被動接受 leader 的快照,并且隻是在 raft 層,而快照是需要上層 service 同時持有,是以一條完整鍊路應該是

    InstallSnapshot -> applyCh -> CondInstallSnapshot

    ,引入這種處理邏輯,是為了保證上層

    service

    和 raft 層應用快照時是原子性地同時應用

CondInstallSnapshot

是在上層 service 拿到快照後,詢問 raft 層是否一起安裝快照

InstallSnapshot

RPC

論文圖 13 描述的很清楚,但是這裡的實作可以不用管 chunk 實作,部分參數和邏輯忽略

mit_6.824_2021_lab2D_log_compactionmit_6.824_2021_lab2D_log_compaction

offset

done

屬性可以不要了,實作步驟中的 2、3、4 也可以不要了

這裡我一開始有個疑問是,這個 rpc 請求的傳回體參數隻有 term 一個,并不考慮成功與否;并且 rpc handler 實作中也可以不用像

AppendEntries

那樣判斷日志是否沖突;但是陳舊的日志判斷,還是要有

其實這裡應該利用了 5.4 節的 raft 安全性,能成為真正的 leader,其日志條目必定是最新且完整的,故其生成的快照資訊是對的,如果該 leader 是陳舊的 leader,将必定會導緻 term 落後而變成 follower ;如果該 leader 的 term 與絕大多數的 follower term 一緻,即能與其他follower 取得聯系,則該 leader 根據選舉的 up-to-date 原則和安全性,其生成的快照對于落後的 follower 來說是正确的

快照引入後改造的索引系統

快照功能引入後,由于涉及日志條目的截斷操作,截斷之前的日志變得不可通路了,故需要設定 getter 和 setter 做下标的轉換,不能直接擷取絕對位置了,這裡有幾點需要注意

  • 需要實作關于

    log[]

    的 getter 和 setter
  • 原有實作是下标從1開始,0為空日志;在引入快照功能後,截斷過後下标也要從1開始,0為

    lastIncludedTerm

    日志,這樣友善下标運算
  • 對于

    nextIndex[]

    也要有相關邏輯的改造,若發現 nextIndex 比快照

    lastIncludedIndex

    小,則改為發送快照,而不是 log 日志條目,并且相關邏輯也從

    nextIndex > 0

    變為

    nextIndex > lastIncludedIndex

實驗結果

全部 passed 🎉

mit_6.824_2021_lab2D_log_compactionmit_6.824_2021_lab2D_log_compaction

感想

對于最後的 lab2D,不要隻實作了功能就完了,還需要了解

Snapshot

InstallSnapshot

CondInstallSnapshot

這些 api 的意義,其實 lab2D 已經引出了 raft 層之上的 service 層了,這個将會在 lab3 有更深的接觸