这两天一直在啃raft的论文,当看到5.4.2节的时候,一直纠结raft是怎么解决上图中这个问题。即序列c中s1在term4提交了索引2,但是序列d中s5却在term5中提交了它的索引2,从而覆盖了s1在term4中的索引2,破坏了Leader Completeness和State Machine Safety.
那么raft是如何解决这个问题的呢,它是只允许leader提交当前term中的日志,而之前term中的日志由Log Matching性质来保证。这样能确保接收到日志的服务器的term是最新的,从而那些旧的服务器没有机会竞争到leader来改变这些已经确认过的日志。
上图中的问题是,s1在term4提交了之前term2中的索引2,但是没有成功提交索引3,此时那些接收了日志的服务器的最新term仅为2,导致s5有机会竞选到leader,并使用自己的索引2去覆盖了s1的索引2。要解决这个问题的话就要使s1在提交了索引2后,使s5丧失竞争到leader的机会,由于竞争leader的规则是看谁的日志更新(即term和日志索引更高)。那么只要使s1在提交了索引2后,那些接受了s1索引2的服务器的日志比s5更新,就可以使s5得不到成为leader的机会。但是s1这条索引2日志的term仅仅为2,小于s5索引2日志的term为3。
解决办法就是s1在term4期间,不去主动提交索引2,而是通过提交索引3来间接提交索引2,这样就能保证接收了s1日志的服务器的term是4,即最新的,s5就失去了竞选leader的机会,就没有办法来改变索引2了。
那么s1怎么通过提交索引3来间接提交索引2呢,答案就是Log Matching。Log Matching就是如果两个服务器在相同位置的日志是相同的,那么这个位置之前的日志也是相同的。关于这个性质是如何做到的,可以查看论文的第5.3节--日志复制。简单地说就是leader在复制一条日志给服务器时,会顺便检查服务器上这条日志的前面一条日志是否和自己的一致。如果不一致的话leader就会将前面一条日志也复制给服务器,复制的过程中同样也会执行检查前一条日志是否相同的操作。这样的递归操作最终会保证leader和服务器上的所有日志一致。
raft论文的中文版地址
https://github.com/maemual/raft-zh_cn/blob/master/raft-zh_cn.md
看论文有问题欢迎交流。