TOC
Open TOC
lab3
https://pdos.csail.mit.edu/6.824/labs/lab-kvraft.html
basic (3A)
并发测试
主要是利用 raft server 写一个能够容错的 kv service
client 有 GET / PUT / APPEND
方法,会通过 rpc 访问 server
server 则通过 lab2 暴露的接口与 raft server 交互
首先需要明确的是,lab2 实现的 raft server 实现的语义是 At-Least-Once,也就是说,对于一个 command,可能会被多次 apply
考虑如下情形,client 发送 command 给 raft,raft 在 apply 之后和响应 client 之前掉线了,那么 client 将会重发 command,然而该 command 实际已经存在于 raft log 中
所以,需要在 lab3 中实现 Exactly-Once 的语义,更具体的,需要实现 GET / PUT / APPEND
方法的 linearizability
为此,client 访问 server 时,需要提供 clientId
和 commandId
,其中
clientId
是一个随机值,用于区分不同的 clientcommandId
记录了 client 发送的 command 序号
当 server 处理 client 的请求时,若该请求为重复的非读请求,则直接返回记录的结果
考虑到只有
PUT / APPEND
操作才会改变 KVServer 的状态
为此,每个 server 需要维护如下的数据结构
记录每个 client 最近一次 command 的 response
否则,通过 Start
接口向 raft server 发起共识
直接发送整个 request 结构体,便于后面处理
最后通过返回的 index 索引对应的 channel,并阻塞读取
另外,server 需要一个 goroutine 监听来自 raft server 的 ApplyMsg
由于 Start
发送的 command 携带了全部 request 信息,所以仍然可以且需要判断该请求是否为重复的非读请求
注意到 raft server 可能多次 commit (apply) 同一个 command,需要在 kv server 层 apply 之前进行去重
若否,就可以真正的改变 kv server 的状态了,然后通过 commandIndex
索引对应的 channel
在写入 channel 前,还需要利用 GetState
接口检查 raft 的 authority,为此需要在 ApplyMsg
中添加新的字段 commandTerm
一些细节
- server 需要记录
lastApplied
,避免状态机回退 - 当阻塞读取 channel 时,需要引入 timeout 机制
存在的问题
- channel map 会无限膨胀,需要定期清理
snapshot (3B)
并发测试
明确一下概念
kv server | raft server (persister) | |
---|---|---|
state | kv table | log |
snapshot | kv table |
kv server 通过调用 Snapshot
向 raft server 提供 snapshot
而调用的依据就是 raft server 的 state 大小,即 log 的大小
需要改动的地方如下
- 在初始化时,若 raft server 的 persister 携带 snapshot,则通过 snapshot 恢复 kv server 的 state
- 注意到 raft server 的 state 主要就是 log,因此当接收到来自 raft server 的
ApplyMsg
时进行判断即可 - 当接收到
SnapshotValid
的ApplyMsg
时,恢复 kv server 的 state 即可
另外,除了 kv table,上述数据结构 clientId -> (commandId, response)
也是 state 的一部分
test
with
-race
3A (without TestSpeed3A)
3B (without TestSnapshotSize3B and TestSpeed3B)
存在的问题
- 由于在测试代码中,单个 client 对
GET / PUT / APPEND
的调用是非并发的 - 而目前的 raft 实现只会在 heartbeat 时发起共识
- 那么对于 client 的一个请求,至少需要一个 heartbeat 的时间才能响应,实际观察到的时间在 300ms 左右
- 那么在测试 2 分钟的时限内,大概可以响应 400 个请求
- 对于超出该请求次数的测试用例,将会超时
- 实际上在
Start
中发起共识也没有什么优化
ref
https://github.com/OneSizeFitsQuorum/MIT6.824-2021
https://frederick-s.github.io/2022/06/04/mit-6.824-lab3-implementation/
https://web.stanford.edu/~ouster/cgi-bin/papers/OngaroPhD.pdf