目录
前言
做2020的MIT6.824,完成了实验Raft Lab3B,通过了测试,对于之前的Raft实现的实验请参考Raft Lab 2A, Raft Lab 2B 和 Raft Lab 2C 以及Raft3A
Lab3B主要需要完成日志压缩的需求,以保证实用性以及性能上的需求。总的来说,这个实验需要改进的地方很多,尤其是我的代码其实之前一直上从index=0开始的,到这个实验卡壳才发现index=1,于是又修改了之前的Raft代码,也参考了网上大家的已有实现,然后才逐渐实现日志压缩的需求的。
一、Overview
1.1 流程
- KVServer发现log size大于设定好的阈值,通知对应的Raft server discard log,并把log的snapshot传过去
- Raft server收到KVServer的通知,截断snapshot之前的log,并通知persister保存KVServer传过来的snapshot
- leader在发送心跳的时候如果发现有新的snapshot persist了,通知followers InstallSnapshot
- follower 收到InstallSnapshot,与本地log进行对比,跟新log,并通知persister保存leader传过来的snapshot
- follower通知对应的KVserver,reset kvStore保持其一致性
二、Implementation details
2.0.0 新的properties
这个paper Figure13 其实也有讲到,但是本实验并不要求offset,所以就剩下如下的新的属性了,这些属性会在后面InstallSnapshot具体讲到,其中lastIncludedIndex以及lastIncludedTerm也被我放到了KVServer和Raft的属性中了
Arguments:
term leader’s term
lastIncludedIndex the snapshot replaces all entries up through and including this index
lastIncludedTerm term of lastIncludedIndex
data[] raw bytes of the snapshot chunk, starting at offset
Results:
term currentTerm, for leader to update itself
type KVServer struct {
...
lastIncludedIndex int
}
type Raft struct {
...
//snapshot
lastIncludedIndex int
lastIncludedTerm int
}
2.0.1 Helper functions
由于snapshot的引入,log本身的长度就跟index不一致了,需要加上snapshot的长度,于是引入了一些helper functions
func (rf *Raft) getLog(i int) LogEntry {
return rf.log[i-1-rf.lastIncludedIndex]
}
func (rf *Raft) getLogLen() int {
return len(rf.log) + rf.lastIncludedIndex
}
func (rf *Raft) convertedIndex(i int) int {
return i - 1 - rf.lastIncludedIndex
}
func (rf *Raft) getLastLogIndex() int {
return rf.getLogLen()
}
func (rf *Raft) getLastLogTerm() int {
lastLogIndex := rf.getLastLogIndex()
if lastLogIndex <= rf.lastIncludedIndex {
return -1
} else {
return rf.getLog(lastLogIndex).Term
}
}
2.1 log size detection
KVServer发现log size大于设定好的阈值,通知对应的Raft server discard log,并把log的snapshot传过去
为流程的第一步,检测log size是否过大需要discard,如果满足条件,生成snapshot via getSnapshot(),并且通知下层Raft discard log via kv.rf.DiscardEarlyEntries(kv.lastIncludedIndex, snapshot)
func (kv *KVServer) snapshotMonitor() {
for {
if kv.killed() || kv.maxraftstate == -1 {
return
}
if kv.rf.IsExceedLogSize(kv.maxraftstate) {
//save state
kv.mu.Lock()
snapshot := kv.getSnapshot()
kv.mu.Unlock()
//tells Raft that it can discard old log entries
if snapshot != nil {
kv.rf.DiscardEarlyEntries(<