MIT 6.824 Lab2C Raft之持久化
阿新 • • 發佈:2021-11-14
書接上文Raft Part B | MIT 6.824 Lab2B Log Replication。
實驗準備
- 實驗程式碼:
git://g.csail.mit.edu/6.824-golabs-2021/src/raft
- 如何測試:
go test -run 2C -race
- 相關論文:Raft Extended
- 實驗指導:6.824 Lab 2: Raft (mit.edu)
實驗目標
- 完成
persist()
和readPersist()
函式,編碼方式參照註釋。 - 優化
nextIndex[]
回退方式,否則無法通過所有測試。
一些提示
- 測試涉及伺服器故障和RPC失敗等不確定事件,多次執行測試確保通過。
- 需要持久化的部分包括
currentTerm
、votedFor
、log
。 - 有關
nextIndex[]
回退優化可以檢視Students' Guide to Raft。 - 在Lab2A和Lab2B中測試未能發現的錯誤可能會在Lab2C中暴露出來。
持久化
這部分其實很簡單,程式碼中的註釋已經很清晰了,當然你要注意data race問題。
func (rf *Raft) persist() { w := new(bytes.Buffer) e := labgob.NewEncoder(w) e.Encode(rf.currentTerm) e.Encode(rf.votedFor) e.Encode(rf.log) rf.persister.SaveRaftState(w.Bytes()) } func (rf *Raft) readPersist(data []byte) { if data == nil || len(data) < 1 { return } r := bytes.NewBuffer(data) d := labgob.NewDecoder(r) d.Decode(&rf.currentTerm) d.Decode(&rf.votedFor) d.Decode(&rf.log) }
nextIndex優化
Part B中對於失敗的AppendEntries請求,讓nextIndex自減,這樣效率是比較慢的。
優化點1
如果follower.log
不存在prevLog
,讓Leader下一次從follower.log
的末尾開始同步日誌。
優化點2
如果是因為prevLog.Term
不匹配,記follower.prevLog.Term
為conflictTerm
。
- 如果
leader.log
找不到Term為conflictTerm
的日誌,則下一次從follower.log
中conflictTerm
的第一個log的位置開始同步日誌。 - 如果
leader.log
找到了Term為conflictTerm
leader.log
中conflictTerm
的最後一個log的下一個位置開始同步日誌。
nextIndex
的正確位置可能依舊需要多次RPC才能找到,改進的流程只是加快了找到正確nextIndex
的速度。
AppendEntries中有邏輯如下。
reply.Term = rf.currentTerm
reply.Success = false
if len(rf.log) <= args.PrevLogIndex {
reply.ConflictIndex = len(rf.log)
reply.ConflictTerm = -1
return
}
if rf.log[args.PrevLogIndex].Term != args.PrevLogTerm {
reply.ConflictTerm = rf.log[args.PrevLogIndex].Term
for i := 1; i <= args.PrevLogIndex; i++ {
if rf.log[i].Term == reply.ConflictTerm {
reply.ConflictIndex = i
return
}
}
}
Heartbeat中有邏輯如下。
if !reply.Success {
if reply.ConflictTerm == -1 {
rf.nextIndex[id] = reply.ConflictIndex
} else {
conflictIndex := -1
for i := args.PrevLogIndex; i > 0; i-- {
if rf.log[i].Term == reply.ConflictTerm {
conflictIndex = i
break
}
}
if conflictIndex != -1 {
rf.nextIndex[id] = conflictIndex + 1
} else {
rf.nextIndex[id] = reply.ConflictIndex
}
}
}
實驗總結
Part C並不算是Raft演算法的核心部分,關於nextIndex的優化本文是參照了Students' Guide中的方式。
如果你完成了持久化和回退優化兩個部分依然無法通過所有測試,那可能要仔細的檢查Part A和Part B是否遺漏了某些細節。
最後,為了證明我不是在亂寫,附上我的測試結果。