回复题主,帮你抓了个 bug 。
https://github.com/yzongyue/6.824-golabs-2020/blob/yzy/src/raft/raft_vote.go#L125 这里在 rf.changeRole()之前,应该确保 rf.role == Follower && rf.voteFor == -1 。
否则,该节点在 rf.changeRole()之前可能已经 approve 了其他节点在当前 term 的 vote request (并修改了 rf.voteFor ),然后在这里 rf.changeRole()的时候又投票给自己( rf.voteFor =
rf.me )。这样该节点就会投出两个不同的票,从而可能造成当前 term 有两个不同节点成为 leader 的情况。
详细例子:假设 3 个节点 A,B,C 。
1. A 成为 candidate, 发送 vote request 给 B 。
2. B 收到 A 的 vote request 时仍然是 follower,因此批准 vote request 。
3. A 收到 B 的 approval,认为自己成为 leader 。
4. B 成为 candidate,并收到 C 的 approval,认为自己成为 leader 。
这里有一个问题是,为什么 2 之后,4 仍然可能发生?在 B 批准 A 的 vote request 时,B 重置了自己的 electionTimer:
https://github.com/yzongyue/6.824-golabs-2020/blob/yzy/src/raft/raft_vote.go#L67 。所以 B 不应该再触发将自己变为 candidate 的逻辑
https://github.com/yzongyue/6.824-golabs-2020/blob/yzy/src/raft/raft.go#L415我的理解是,2 和 4 之中,事实上先发生的是 4 ( B 触发自己变为 candidate 的逻辑
https://github.com/yzongyue/6.824-golabs-2020/blob/yzy/src/raft/raft.go#L415 )。但很不幸,在调用 rf.startElection()时,go runtime scheduler 将本 goroutine 暂停,而切换到执行 2 的 goroutine (处理 A 的 vote request )!如果把 rf.startElection() inline,就不会出现这个 bug 。这也能解释为什么这个 bug 很难复现:这依赖于 go runtime scheduler 对这些 concurrent events 的执行时机。