题目:http://nil.csail.mit.edu/6.824/2022/labs/lab-shard.html
两个组件:
- replica groups:每个group负责一部分shards;
- shard controller:决定哪一个replica group负责哪一个shard。
shards数据要能够在replica groups之间迁移。
Part A: The Shard controller
Hint: The code in your state machine that performs the shard rebalancing needs to be deterministic. In Go, map iteration order is not deterministic.
为了保证相同的输出产生相同的rebalance结果,需要对gid进行排序,因为map的key输出顺序是不确定的。这也就是hin3中说的deterministic。
Part B: Sharded Key/Value Server
前期准备
随着模块数量的增多(shardkv-client、shardkv-server、shardctrl-client、shardctrl-server、raft)加上有的模块还存在副本,例如shardkv-server有三个group,每个group里有三个server,那么就会有九个状态机和raft实例。如果这些模块的日志都集中输出到一个文件中,势必会给debug带来不少的麻烦,因此为了方便定位问题,有必要对各个模块的日志输出策略做灵活的控制。
我的策略是在不同模块对应的结构体中,各自定义了一个*log.Logger
和name,name在输出日志时作为log prefix,这样就可以区分不同模块、不同副本打印的内容了。同时,各个模块还可以独立设置是否输出日志,以及日志输出路径。这样做带来的另一个好处是:如果我在机器上同时运行lab2和lab4的测试用例,lab2的raft日志我希望打印出来,而lab4我只关心server层的输出,不关心raft的日志,那么就可以把lab4的raft日志关闭,从而实现了不同lab对同一个raft实例日志开关的独立控制。
分片状态
Lab4 part B最重要的就是分配状态的迁移,管理好分配的状态是通过所有测试的关键,一个shard有四种状态:
- WORKING:表示当前节点负责shard
- MIGRATING:从其他节点转移到本节点后的shard状态,此状态的shard需要把数据迁移过来
- REQUEST_DELETE:数据已经迁移过来了,需要将原节点中shard清理掉,这时的shard处于这个状态
- OUTDATE:从本节点转移到其他节点后的shard状态,此状态的shard等待其他节点发起清理请求
四种状态的迁移图如下所示:
可以看出,一个shard的起始状态和终止状态都是WORKING
,也就是说,配置更新事件出发了shard状态的迁移,每个节点根据状态机做相应的操作,最终又收敛到了WORKING
状态。