4A
思路
4A可以分为两个部分实现。
第一部分我们只需要使用Raft去实现一个KVServer就行,就像lab2一样,不需要考虑各种可能的错误(网络延迟、网络分区、重启等等)。
具体实现上,大部分代码和lab2类似,需要注意的一个点可能是:KVServer的多个RPC同时从channel中获取已commit的Op时,该如何实现才能使得RPC获得相对应的Op?针对这一问题,我们可以单开一个go routine(read routine)去读apply channel,KVServer的接收到客户端的RPC请求后,只负责Start(),然后在index上去wait;当read routine读到Op时,唤醒对应index上的RPC即可。
第二个部分,我们需要去考虑各种可能的错误,从而做一些措施以应对它们。可能存在的问题以及应对方案如下(供参考):
- Q:Server A收到请求后,调用Start(),但是在这个log被commit之前,server A挂掉了,已经不是leader了
A:client可以主动超时,换一个server去发请求;server可以检测自己是否还是leader,若发现自己在start之后已经不是leader了,那么及时返回err给client。 - Q:处理duplicate request,client请求超时后,会重发该请求,需要保证每个请求仅执行一次(主要针对PUT和APPEND)
A:在client的RPC请求中,携带一个独一无二的序列号seqId;在server中维护当前已经执行完的最后一个请求序列号lastSeqId;若当前命令的seqId <= lastSeqId,那么说明这个命令已经执行过了,直接返回结果即可。GET请求也需要去重,避免raft插入多条相同日志,导致日志膨胀,空间浪费。 - Q:如何优化clerk寻找leader的方式
A:记录上一次请求成功的server Id,每次请求从这个id开始尝试,大概率是leader。
注意点
- 如果说你的TestSpeed4A超时了,那么可能是因为Raft的Start是依靠心跳去同步log的,同时心跳间隔又过长,从而超时。那么降低心跳间隔即可。
- 为什么apply一个op之前需要duplicate检查?考虑下面这种情况:虽然在Start之前通过了duplicate检查,但是Start之后,apply之前,却没通过duplicate,也就是说这个index的log已经被apply过了,不需要再apply一次。
4B
4B的实现比较简单,就是支持snapshot,需要考虑的就是snapshot中需要包含什么信息(①整个kv数据库的快照 ②duplicate去重表)。
有一个需要注意的地方:以前在实现lab3D raft snapshot的时候,没有去考虑AppendEntries RPC中,PrevLogIndex < firstLogIndex的情况,这种情况是可能存在的(当follower已经同步完成,并snapshot了一些log,但是leader发了已经被snapshot的log时),会导致代码异常。遇到这种情况,直接抛弃这个RPC即可。

被折叠的 条评论
为什么被折叠?



