1. 程式人生 > 其它 >MIT 6.824 Lab2C Raft之持久化

MIT 6.824 Lab2C Raft之持久化

書接上文Raft Part B | MIT 6.824 Lab2B Log Replication

實驗準備

  1. 實驗程式碼:git://g.csail.mit.edu/6.824-golabs-2021/src/raft
  2. 如何測試:go test -run 2C -race
  3. 相關論文:Raft Extended
  4. 實驗指導:6.824 Lab 2: Raft (mit.edu)

實驗目標

  1. 完成persist()readPersist()函式,編碼方式參照註釋。
  2. 優化nextIndex[]回退方式,否則無法通過所有測試。

一些提示

  1. 測試涉及伺服器故障和RPC失敗等不確定事件,多次執行測試確保通過。
  2. 需要持久化的部分包括currentTermvotedForlog
  3. 有關nextIndex[]回退優化可以檢視Students' Guide to Raft
  4. 在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.TermconflictTerm

  1. 如果leader.log找不到Term為conflictTerm的日誌,則下一次從follower.logconflictTerm的第一個log的位置開始同步日誌。
  2. 如果leader.log找到了Term為conflictTerm
    的日誌,則下一次從leader.logconflictTerm的最後一個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是否遺漏了某些細節。

最後,為了證明我不是在亂寫,附上我的測試結果。