6.824 Lab2C

前置芝士

Task

完成 Raft 的持久化。

Step

如果前面做得好,只需要完成持久化。

  1. 持久化至 Service 层
1
persist()
  1. Crash 后从 Service 层恢复
1
readPersist(data []byte)
  1. 如果和我一样前面差点意思,就要小修一下 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了。

updatedupdated2024-04-302024-04-30