vlambda博客
学习文章列表

Raft5.4.2 如何提交之前term里的entry

 raft永远不会通过计算备份数量的方式对之前term的entry进行提交。只有leader的current term里的entry可以通过计算备份数量的方式进行提交。






5.4.2



如何提交之前term里的entry




图8. 该时间序列展示了为什么一个leader是无法通过log来确定过去term的entry是否被提交的。(a)时刻,S1是leader,它把entry复制到了部分服务器,如S2里。(b)时刻,S1宕机;S5成为了新的leader,它的term是3,由S3、S4以及它自己选出,并且在index2上接收了一个新的entry。(c)时刻,S5宕机,S1重启,并被选为了leader,于是它继续进行日志备份,此刻,它成功地把它的entry2备份到大部分的server上,但是未作提交。(d)时刻,假设S1又宕机,S5被S2、S3、S4选成了leader,然后把index2的entry全部覆写成了term3的命令。然而,我们再来作另一种假设,如果S1成功提交了entry2,在平行世界里的(e)时刻,由于entry2已经被提交,S5不可能再赢得选举,e时刻里所有的entry是可以被正常提交的。



5.3节提到,leader一旦确定当前term里一条entry被安全地存储在过半的server里面,它就会提交这条entry。如果leader在提交entry前宕机,之后的leader会尝试对这个entry进行备份。然而,leader是无法立即确定之前term里的entry是否被存储在了大部分server里。图8说明了这种情况:一个老的entry被存储在了大部分server里,但仍然会被一个新的leader覆写。


为了避免图8中的问题,raft永远不会通过计算备份数量的方式对之前term的entry进行提交。只有leader的current term里的entry可以通过计算备份数量的方式进行提交;一旦current term里的entry以这种方式被提交之后,根据日志匹配性质,它之前的所有entry会以一种间接的方式被提交。虽然确实有几种方式,leader可以通过通信来确定之前的log entry是否已经被提交(比如说,如果一个entry被所有的server存下来了,那这个entry就被认为是提交态的),但是raft采用了一种更为保守的方式来确保它的简单性。


由于log entry会存储它原本的term号,如果让leader备份之前term里的entry,会使raft引入额外的复杂性。在其他共识算法里,如果一个新的leader去备份前任term里的entry,它必须将这个entry里的term修改成新的term number。Raft的方法更为简单,因为entry里的term值会一直保持不变。另外,采用这种方法,和其他的算法相比,新leader可以发送更少的log entry。(其他算法里,新leader必须发送大量的log entry,去备份前任leader留下的日志条目)




额外的话


翻译到这里,我越来越困惑了。论文里根本没提网络分区的问题,一直都在假设leader/follower宕机了怎么办。

假设follower没有宕机,而是遇到了网络分区问题,一直收不到leader的心跳。按论文里的逻辑,它会转到candidate状态,然后自增term。当网络分区的问题解决后,它发送的RequestVote RPC请求里包含的term值一定大于任何一个server的current term。依照论文里图2的逻辑,任何一个server,在response/request看到任何一个比它大的term值,都要增大至这个term值,并且变成follower状态。

这样的话势必会触发新的选举。

但是6.824的lab2B里明确指出,触发bug的原因之一,有可能就是在leader还活着的时候,剩下的followers发起了新的选举。

在群里问了大佬之后,大佬们说,在raft的那篇博士论文里,提到了使用preVote来解决这个事情。




另外在raft.github.io里,有人做了raft可视化,通过模拟,发现如果有网络分区,follower自增term,于是触发新的选举。如果按论文里的逻辑去实现,这种不合理的情况得确会出现。

虽然依旧可以保证一致性,但是性能上会变差。