MIT6.824 spring21 Lab3A总结记录

写在前面

lab3A也完成了,前前后后也改了几版才改成现在的样子。

老规矩,记录一下设计思路。

代码见:https://github.com/sun-lingyu/MIT6.824-spring21/tree/KV-3A

代码结构

 

 

 

 

 由于client的处理逻辑很简单,这里略去。

整体逻辑如上图。(图中的hreply是一个struct;h表示handler)

 

使用一个专门的applyListener goroutine监听applyCh。所有对KV状态的修改均由applyListener 进行。

当leader接受到RPC请求时:RPC Handler会先执行rf.start,将请求放入raft log。然后RPC Handler将等待raft达成共识,等待applyListener完成相应的状态修改/查询,然后返回reply。

 

图中没有画出来的:leader需要维护两个map:

1.pendingChannels:以log index为key,以hreply channel为value。每一个在leader处,已经执行了start,并且正在等待raft达成共识的RPC handler,都有一个与start返回的index相对应的channel。这是为了applyListener能够与RPC handler通信。

2. pendingMap:以log index为key,以Op为value。每一个在leader处,已经执行了start,并且正在等待raft达成共识的RPC handler,都有一个与start返回的index相对应的Op。这是为了applyListener能够进行duplicate detection。

 

 当applyListener从applyCh收到了一个Op,它首先执行duplicate detection。

接下来,若这个Op不重复,且是PutAppend,则修改KV状态。

接下来,若它发现:这个Op的log index在其维护的pendingChannels和pendingMap中有相应的value,这就代表有相应的RPC handler在等待。此时applyListener首先对比:pendingMap中的Op和从applyCh接收的Op是否相同。

若不相同,则表示有新的leader在这个log index处commit了新的entry。这表明本机已经不再是leader。应该遍历所有的pendingChannels键值对,告知所有等待的RPC Handler这个消息(即:向所有等待的RPC Handler发送ErrWrongLeader)。

若相同,则applyListener向对应的RPC Handler发送OK。若对应的RPC是Get,还要同时发送查询到的value值。

 

上面描述的处理逻辑还不完整。我将在下面的两个部分中加以补全。

Duplicate Detection

如何发现重复的PutAppend请求?

我的实现方式是:每一个client都维护两个值:

1. 一个独特的ID(用递增的int64表示)

2. 一个version number。

每当client要执行一个新的PutAppend请求,该client会将其version number增加1。当client向server发送PutAppend调用时,附上version number。

这样,同一个client的重复请求就可以通过ID+version number加以识别。

在server端,也维护一个以client ID为key,以该client的version number为value的map。每当server成功执行PutAppend请求,都将map中相应ID的version number增加1.

这样,server就可以通过version number来判断client的请求是否已经成功执行。

如果server遇到了已经成功执行过的PutAppend请求,它将直接返回OK。

New Leader Detection

在做这次实验的时候,我的实现在

partitions, one client (3A)

 这个测试用例中总是卡住,不能继续向下执行。

在经过各种尝试,都没有效果之后,决定看看别人的博客。

最后终于在https://zhuanlan.zhihu.com/p/130671334 这里找到了问题所在😂

卡住的原因如下:

在我上面描述的实现中,只有当applyListener从applyCh中收到了新的Op,它才能够发现自己已经不再是leader。

若新leader没有commit任何新entry,那么旧leader的KV server将永远不会发现:它已经不再是leader。

因此系统将卡住,无法继续。

 

为了修复这个问题,我采用了与上面知乎专栏中一样的方法:

修改raft leader,使其在刚刚选举成功时向applyCh发送一条特殊的消息。上层service收到这条消息后,将执行一次rf.Start,在raft log中放置一条特殊的命令。随着这条命令的commit,所有的KV server都将发现有新的server选举成功。

这种实现较为简单,但是其修改了下层raft,我觉得不够完美,但是可以接受。

写在最后

Lab3A比较简单。但是仍然需要精心设计来保证其正常工作。

New leader detection是一个比较难的点,需要额外注意。我主要的时间都花在了这里。

 

posted @ 2021-04-08 20:07  sun-lingyu  阅读(584)  评论(0编辑  收藏  举报