前置芝士
Task
完成 Raft 的持久化。
Step
如果前面做得好,只需要完成持久化。
- 持久化至 Service 层
- Crash 后从 Service 层恢复
1
|
readPersist(data []byte)
|
- 如果和我一样前面差点意思,就要小修一下
guide
- AppendEntries Handler 中,Follower 引入 XTerm 和 XIndex 来快速调整 nextIndex。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
|
if args.PrevLogIndex > rf.getLastLogL().Index {
reply.Success, reply.Term = false, rf.currentTerm
reply.XIndex = rf.getLastLogL().Index + 1 // Follower's nextIndex
return
}
// entry logAt prevLogIndex whose term doesn't match prevLogTerm
DPrintf(dInfo, "S%v prevLogTerm:%v, prevLogIndex:%v,log[prevLogIndex].Term:%v", rf.me, args.PrevLogTerm, args.PrevLogIndex, rf.logAt(args.PrevLogIndex).Term)
if args.PrevLogTerm != rf.logAt(args.PrevLogIndex).Term {
reply.Success, reply.Term = false, rf.currentTerm
reply.XIndex, reply.XTerm = rf.commitIndex+1, rf.logAt(args.PrevLogIndex).Term
for i := args.PrevLogIndex; i > rf.commitIndex+1; i-- {
if reply.XTerm != rf.logAt(i-1).Term {
reply.XIndex = i
return
}
}
DPrintf(dInfo, "S%v XIndex = %v", rf.me, rf.commitIndex+1)
return
}
|
- Leader 对 XTerm 和 XIndex 的处理
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
|
if rf.sendAppendEntries(peer, args, &reply) {
rf.mu.Lock()
defer rf.mu.Unlock()
if reply.Term > rf.currentTerm {
//changeState but not update election timer
} else if args.Term == rf.currentTerm {
if reply.Success {
// update nextIndex, matchIndex & commit
} else if reply.Term == rf.currentTerm{
// adjust nextIndex
if reply.XIndex != NULL {
DPrintf(dHeart, "S%v <- S%v heartbeat XIndex:%v, XTerm:%v", rf.me, peer, reply.XIndex, reply.XTerm)
if reply.XTerm == NULL {
rf.nextIndex[peer] = reply.XIndex
} else {
ok := false
for i := rf.nextIndex[peer] - 1; i > rf.lastIncludedIndex() && reply.XTerm <= rf.logAt(i).Term; i-- {
if rf.logAt(i).Term == reply.XTerm {
ok = true
rf.nextIndex[peer] = i + 1
break
}
}
if !ok {
rf.nextIndex[peer] = reply.XIndex
}
}
}
}
}
}
|
- electionTimer 不能在每次变成 Follower 的时候重置。
If election timeout elapses without receiving AppendEntries RPC from current Leader or granting vote to Candidate: convert to Candidate.
The distinction turns out to matter a lot, as the former implementation can result in significantly reduced liveness in certain situations.
electionTimer 重置仅在以下情况发生:
- 刚成为 Candidate。
- 给别人投票后。
- Follower 收到 AppendEntries 或者 InstallSnapshot,并且 args.Term 等于自己当前的 term(在前置芝士有谈到,其实这个 bug 是我在 2C 的时候才修的)。
我在 Follower 收到 AppendEntries 或者 InstallSnapshot,并且 args.Term 大于 currentTerm 的时候也重置了 electionTimer。改为不重置后,直观的结果是跑 2C 的 test 平均快了 15s,并且不会出现fail to reach agreement
了。