08. Raft PartB 日志同步
实现要求
在 PartA 的基础上,加入日志。即:
- 在进行领导者选举时,要加入日志的比较。
- 领导者收到应用层发来日志后(
raft.Start),要通过心跳同步给所有 Follower。 - 在收到多数 Follower 同步成功的请求后,Leader 要推进
CommitIndex,并让所有 Peer Apply。
写完后在 src/raft 文件夹,使用 go test -run PartB -race 来测试代码逻辑是否正确、是否有数据竞态。
实现要点
- 由于实现细节很多,在第一步要保证
TestBasicAgreePartB()测试能够过。该测试用到的代码流程是,先实现 Start(),然后通过 AppendEntries RPC 发送给所有 Follower,最后在每个 Peer 上再将所有已提交日志按顺序发送到 applyCh 中。 - 实现领导者选举时的限制,具体可以参考论文中 5.4.1 节。
- 一种常见的错误现象是,已经有 Leader 当选,但还是不断的有 Peer 发起选举。可以检查下选举超时和心跳间隔的配置是否正确、候选人当选 Leader 后是否立即发起心跳、发起选举时是否检查了自己是 Leader。
- 你可能会用一些逻辑来频繁的检测是否满足某种条件。建议不要实现为死循环,可以插入一些 Sleep:
time.Sleep(10 * time.Millisecond)或者直接使用 Golang 的条件变量(附录1 sync.Cond)。 - 一定尽量有条理的组织你的代码,才能够使得出现 Bug 尽快定位,这方面我们之前章节详细讲过,可以参考:代码组织(02.Raft 代码总览) 和 PartA 中示例实现的一些组织章法。
- 如果你的某个测试过不了,可以参考
config.go和test_test.go的测试逻辑,即 tester 如何制造混沌环境来对 Raft 提供的接口进行调用的。 - 所有关键的事件:角色变化、追加日志、应用日志等改变全局变量的各个关键环节,最好都打好日志。这样出现问题就能根据日志时间线来大致定位问题位置。
- 在测试的时候,可以先整体跑
go test -run PartB,如果发现某个出现问题,再打开日志环境变量单独跑,如VERBOSE=0 go test -run TestRPCBytesPartB | tee out.txt以避免多个测试用例日志混在一起。
到此,你就可以自己写这部分代码然后做测试了,后面部分是实现部分,建议看到这里后,先自己实现一遍,然后跑测试。哪怕最终做不出来也先趟趟雷,才能带着问题去理解为什么要那么实现。
评论
