Lab3主要是实现一个容错的KV数据库,并用Lab2的Raft服务,在每台运行Raft的peer上构建一个状态机.
3A
- 跟着课程提示一步步走,补全client.go中的sendRPC和server.go中的处理方法,跑basic3Atest遇到如下错误
--- FAIL: TestBasic3A (16.32s)
test_test.go:293: get wrong value, key 0, wanted:
x 0 0 yx 0 1 yx 0 2 yx 0 3 y
, got
x 0 0 yx 0 1 y
test_test.go:126: failure
test_test.go:293: get wrong value, key 0, wanted:
x 0 0 y
, got
x 0 0 yx 0 1 yx 0 2 yx 0 3 y
test_test.go:126: failure
test_test.go:293: get wrong value, key 0, wanted:
, got
x 0 0 y
test_test.go:126: failure
test_test.go:382: history is not linearizable
是因为在server的appendput收到rpc调用start之后马上返回客户端client了,但是此时raft并没有达成一致,这时候client执行get请求就是线性不一致的,需要检测apply chan,拿到commit成功的消息之后再返回client OK.
- 然后由于网络问题client有可能会重复发送请求,需要在发送put和append rpc时附加一个递增的requestid,server对每个requestid的请求只执行一次
在执行 Test: ops complete fast enough (3A) 测试时候遇到超时错误,是因为调用raft Start函数写日志的时候没有发送心跳去主动向follow同步日志.
--- FAIL: TestSpeed3A (62.01s)
test_test.go:419: Operations completed too slowly 60.986227ms/op > 33.333333ms/op
- 这个测试是测试网络分区的情况下是否能正常工作
--- FAIL: TestOnePartition3A (0.81s)
test_test.go:522: Get in minority completed
1. 首先客户端请求put 1:13,5台raftserver,leader 1收到put请求后向其他4台follow发送append rpc,follow收到rpc成功添加到log,然后返回leader成功,leader把commitid++,把1:13发送到applychan,状态机收到请求后state变为1:13,再返回客户端put成功.
2. 客户端收到put成功后,设置网络分区:[0 2 3] [4 1],原先的1号leader在发送心跳的时候带上了自己的commitid=1,然后4号follow看到leader的commitid比自己的大,把自己的日志也提交状态机了,现在4号的state为1:13.
3. 0 2 3号由于没收到心跳,超时重新开始选举,3号节点当选为leader
4. 这时候往4 1节点分别发送put 1:15和get 1请求,测试要求是两个请求都不能成功,但是这里4号节点的state为1:13,返回了1:13 response,报错
这里被卡了很久,一度认为是测试案例写错了,其实网络分区之后,少数分区的leader数据有可能是落后的,不能再返回给client,逻辑修改为get请求也走start提交到log,这样少数分区的leader拿到get的log如果没有大多数节点的同步,就不会返回数据给client
3B
当日志太大的时候实现快照功能,maxraftstate为最大保存日志的字节数,当收到快照apply的时候调用CondInstallSnapshot询问raft是否安装此快照,代码只要增加这么一点,但是lab2里没有暴露的错误会在这一个lab显现.
if kv.maxraftstate != -1 && kv.rf.GetStateSize() > kv.maxraftstate {
raft.PrettyDebug("TRCE", "S%d RAFT state size over maxraftstate:%d", kv.me, kv.maxraftstate)
w := new(bytes.Buffer)
e := labgob.NewEncoder(w)
e.Encode(kv.state)
e.Encode(kv.lastRequestId)
go kv.rf.Snapshot(commandIndex, w.Bytes())
}
if msg.SnapshotValid {
raft.PrettyDebug("TRCE", "S%d receive Snapshot msg", kv.me)
SnapshotTerm := msg.SnapshotTerm
SnapshotIndex := msg.SnapshotIndex
raft.PrettyDebug("TRCE", "S%d send CondInstallSnapshot rpc SnapshotTerm:%d, SnapshotIndex:%d", kv.me, SnapshotTerm, SnapshotIndex)
if ok := kv.rf.CondInstallSnapshot(SnapshotTerm, SnapshotIndex, msg.Snapshot); ok {
r := bytes.NewBuffer(msg.Snapshot)
d := labgob.NewDecoder(r)
d.Decode(&kv.state)
d.Decode(&kv.lastRequestId)
}
- 在raft服务器重启之后没有读取状态机的快照,只读取了raft的快照,然后只保存了新写入的日志
--- FAIL: TestSnapshotRecover3B (6.28s)
test_test.go:296: get wrong value, key 0, wanted:
x 0 0 yx 0 1 yx 0 2 yx 0 3 yx 0 4 y ... x 0 589 y
, got
test_test.go:129: failure
test_test.go:147: 0 missing element x 0 0 y in Append result
总结
LAB3整体代码量不大,逻辑也不是很复杂,主要还是对lab2实现的检验,如果2C 2D有一些小bug在lab2的test没有校验出来,这里会报错
标签:get,Lab3,go,kv,yx,test,leader From: https://www.cnblogs.com/autumnnnn/p/17173092.html