Raft 进行单节点变换时遇到的问题
通常在实际使用 Raft 过程中,难免会遇到节点变更的问题。
Raft 论文[1] 提出了如何进行节点变更的方法,也就是使用joint consensus
,在论文第六章有详细的说明,并且有正确性的证明。
单节点变化
同时在作者的博士论文[2]中,提出了一种简化版本,即单节点变化的流程,具体可见论文第4章。
图来源参考文献[2]
因为如果直接进行节点状态变化,上述例子是从3个节点扩展为5个,那么可能在中间某个时间段会存两种 majorities,会有两个 leader,不满足 Election Safety。究其原因是因为这两个 majorities 不存在交集。所以为了简化,可以将节点变化减少为一个,每次进行节点变化一个一个进行。
图来源参考文献[2]
如果是一个节点的变化,那么老配置和新配置的 majorities 之间肯定存在交集,从而避免上述问题的产生。
单节点变化的BUG
但是这样是否就一定没有问题?具体的单节点变化流程,大家可以参考文献[2]的描述,在此不详细展开。在论文所描述的步骤中,当节点接受到新的配置后,就立即转向新的配置,无论该配置是否已经committed了,当然这是没有问题的。问题出在按照论文所描述的步骤,会导致已经提交的日志被覆盖。作者也是在发现这个问题后,将它及时公布出来,完整细节参考文献[3]。
这里举其中的一个例子:
1 | 从4个节点移除一个节点,并且移除一个节点 |
原始讨论中还提了其他例子,可以深入看看。
解决方法
作者给出了一种解决方法,即节点成为 leader 后,必须先发送一个 NO_OP
日志,避免当前配置下,其他未提交的日志不会覆盖当前已经提交的日志。例如上述步骤 3,S2 发送 NO_OP
后,待NO_OP
提交,那么老配置下的大多数都接受到了,那么据可以阻止没有接受到 NO_OP
当选为 leader,避免覆盖已经提交的日志。
其实NO_OP
的原本作用就是这个:
In a typical Raft implementation, a leader appends a no-op entry to the log when it is elected. This change would mean rejecting or delaying membership change requests until the no-op entry is committed.
也就是在文献[2]提及的这种情况,日志 2 已经被复制到大多数了,但是可能会被日志3覆盖。
图来源参考文献[2]
当然除了上述的方法,那就是老老实实用 join consensus
的方法。
其他的讨论
关于这个问题还有很多有趣的讨论,可以扩展阅读
参考文献
[1]. Ongaro, Diego, 和John Ousterhout. 《In Search of an Understandable Consensus Algorithm》, 不详, 18.
[2]. Ongaro, Diego. 《Consensus: Bridging Theory and Practice》, 不详, 258.
[3]. 《bug in single-server membership changes》. 见于 2021年5月11日. https://groups.google.com/g/raft-dev/c/t4xj6dJTP6E.
Author: DongSheng
Link: http://ehds.github.io/2021/05/11/raft-single-peer-change/
License: 知识共享署名-非商业性使用 4.0 国际许可协议