mit_6.824_2021_lab2D_log_compaction
lab2D 主要是日志壓縮功能,實驗裡是采用論文的最簡單的方式,snapshot 壓縮快照,并且更簡單的是不分塊的形式;感覺主要有兩點需要注意:
- 快照功能的加入對原有邏輯的修改
- 快照功能的流程(InstallSnapshot, Snapshot, CondInstallSnapshot, applyCh)
lab2D_log_compaction
實驗内容
- 要實作快照功能,需要實作
、Snapshot
和CondInstallSnapshot
InstallSnapshot RPC
- 還需要繼續使用修剪後的日志進行操作
- lab3 将會對 lab2 更徹底地測試快照功能,因為會壓測
實驗提示
- 在單個
RPC 中發送整個快照。不用分塊和 offsetInstallSnapshot
- Raft 日志不能再使用日志條目的絕對位置或日志的長度來确定日志條目索引;您将需要使用獨立于日志位置的索引方案。
- 即使日志被修剪,仍然需要在
RPC 中的新條目之前正确發送條目的術語和索引;這可能需要儲存和引用最新快照的AppendEntries
,且兩者需要考慮被持久化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 調用
将快照應用給 raftSnapshot
- leader 調用
rpc 請求将快照傳播給 followersInstappSnapshot
- 當 follower 接收到
RPC 後,必須将 raft 包含的快照送出給上層服務。通過InstallSnapshot
的方式;上層 service 從applyCh <- ApplyMsg
中讀,并使用applyCh
以告訴 Raft 該服務需要安裝該快照CondInstallSnapshot
- 可以在
中的config.go
中檢視上層服務調用快照的用法applierSnap()
- 如果快照是舊快照,則
拒絕安裝快照,即 Raft 在快照的CondInstallSnapshot
lastIncludedTerm/lastIncludedIndex
之後處理了條目
這是因為 Raft 可能會在處理
RPC 之後,并且在服務調用InstallSnapshot
之前,處理其他 RPC 并在CondInstallSnapshot
上發送消息。Raft 傳回到舊快照是不行的,是以必須拒絕舊快照。當您的實作拒絕快照時,applyCh
應該隻傳回CondInstallSnapshot
以便服務知道它不應該切換到快照(這一段我了解一切都是為了原子性應用快照)false,
- 若快照是最新的,則 raft 修建其日志,然後傳回 true,并且服務應該在處理
上的下一條消息之前切換到快照。applyCh
-
是為了讓 服務 和 raft 原子地切換到快照,可以以其他接口實作,你也可以始終傳回CondInstallSnapshot
的方式來實作 rafttrue
實驗思路
關于 CondInstallSnapshot
CondInstallSnapshot
這裡看資料,實作方式參差不齊,有的始終傳回 true,有的會實作邏輯,對這個 api 有解釋的,當時隻找到這篇文章
https://www.cnblogs.com/sun-lingyu/p/14591757.html
總結來說,是為了實作 service 和 raft 兩層應用快照的原子性
經過思考之後,這裡給出自己的了解,如若不對,歡迎提出改進和建議
-
是上層 service 主動調用 api 通知 raft peer 建立快照并存儲到 persister 持久化層的;Snapshot
- 而
這條鍊路是,leader 通知 follower,即 follower 被動接受 leader 的快照,并且隻是在 raft 層,而快照是需要上層 service 同時持有,是以一條完整鍊路應該是InstallSnapshot
,引入這種處理邏輯,是為了保證上層InstallSnapshot -> applyCh -> CondInstallSnapshot
和 raft 層應用快照時是原子性地同時應用service
即
CondInstallSnapshot
是在上層 service 拿到快照後,詢問 raft 層是否一起安裝快照
InstallSnapshot
RPC
InstallSnapshot
論文圖 13 描述的很清楚,但是這裡的實作可以不用管 chunk 實作,部分參數和邏輯忽略
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 做下标的轉換,不能直接擷取絕對位置了,這裡有幾點需要注意
- 需要實作關于
的 getter 和 setterlog[]
- 原有實作是下标從1開始,0為空日志;在引入快照功能後,截斷過後下标也要從1開始,0為
日志,這樣友善下标運算lastIncludedTerm
- 對于
也要有相關邏輯的改造,若發現 nextIndex 比快照nextIndex[]
小,則改為發送快照,而不是 log 日志條目,并且相關邏輯也從lastIncludedIndex
變為nextIndex > 0
nextIndex > lastIncludedIndex
實驗結果
全部 passed 🎉
感想
對于最後的 lab2D,不要隻實作了功能就完了,還需要了解
Snapshot
,
InstallSnapshot
,
CondInstallSnapshot
這些 api 的意義,其實 lab2D 已經引出了 raft 層之上的 service 層了,這個将會在 lab3 有更深的接觸