raft 延伸阅读:从 Higher Terms 即真理,兼谈 PreVote

Raft 论文 In Search of an Understandable Consensus Algorithm (Extended Version) (链接:https://pages.cs.wisc.edu/~remzi/Classes/739/Spring2004/Papers/raft.pdf )第四页 Figure 2 Rules for Servers:

该约束要求所有节点如果在 RPC 请求中看到更高的 term,需要立即 update 本地 term,并且转为 follower。这条规则的使用并不受 prevote 影响,实际上——它和 prevote 没有任何关系,下面我们解释为什么。

例如在之前的某个文章,我们提到目前 raft2 中的实现里,processAskForVoteRequest 收到带着 higher term 的 rpc request 它不会 update 本地 term 也不转为 follower,这就是违反上述规则的一个实例。违反这条规则会如何?我们来看去年在 Nebula Raft 上处理过的一起真实案例,假设我们有一个三节点 raft 集群中有 r1、r2、r3 三个实例,出现以下情况:

  1. leader r3 宕机了
  2. r2 首选发起 election,但是 r2 上的 log 没有比 r1 更新,所以 r1 不会投票给 r2,r1 也不更新本地的 term 直接返回了
  3. r1 超时发起选举,但是 r1 的 term 比 r2 小所以 r2 不会给 r1 投票,r1 也无法当选
  4. 又因为 r2 先发起的投票,term 增长比 r1 快,同时 r1 收到 r2 更高 term 的 rpc 请求也不会 update 本地 term,所以 r1 的 term 始终比 r2 小

由此 r1、r2 可能陷入选举死循环永远都没法选举出 leader。

再来说 prevote,Raft 引入 prevote 的主要目的是防止 term 无序跳变。但是,防止 term 无序跳变不等于不让 term 跳变。更具体的讲,prevote 防止的是 Raft 在发生网络分区时,minority 分区中的节点的 term 的无序跳变。还是举个例子来说明,一个三节点的 Raft 集群 Leader,Follower1,Follower2,现在出现一下网络分区:

  1. Follower1 节点被隔离在一个网络分区
  2. Leader 和 Follower2 处在另外一个网络分区

在没有 prevote 机制的情况下,Follower1 因为收不到 Leader 心跳且拿不到过半数投票而无休止重复尝试选举,进而导致 Term 无限递增。如果存在 prevote,那么因为 prevote 失败所以节点不会发起实际的选举从而避免 Follower1 term 的无限递增。和正常 vote 一样,成功的 prevote 都意味着得到半数节点的投票。如果我们仔细研究,就能发现 prevote 最本质的作用其实是探测节点的网络可达性,更具体的讲就是保证过半数节点的网络可达性 ,只有先确认过半数节点可达那么发起一次正式 vote 才可能是有意义的。因此,prevote 甚至可以设计得及其无脑——可以把 prevote 设计成一个不带 term 的 rpc 都没问题,一个节点只要看到 prevote 请求就可以直接无脑 ACK 告诉对方我的网络可达(实际上,我认为这样的设计对 Raft 选主稳定性的影响微乎极微且可以很大程度简化 vote/prevote 中涉纠缠着的复杂逻辑判断)。

再来说 vote/prevote 中的 term 跳变和我们在开头一段提到的 Raft 论文中 Figure 2 中的 higher term 更新规则。首先,一个节点从 rpc 中看到更高的 term ——这是一个既定事实,一个它必须 follow 的既定事实。而因为 election timeout 触发 update term 进而发起选举这是节点判断当前主节点可能不可用,因此需要进入下一个 term 选出新主节点。要选出新主节点就必须先 update term,实际上 term 跳变正是 Raft 实现故障恢复的关键设计,它让 Raft 能继续运行。这两种场景,一种是面对既定事实,节点 follow 别人发起的一个新的 term;另一种是节点面对潜在的系统故障自发的 update term 进而发起新选举的尝试恢复集群的努力。从这个意义上讲,它们是两个事情,而 prevote 防止的是后者在 minority 网络分区中出现的 term 无限递增的问题。

综上,从我们所举的反例和分析中都可以得出结论:Raft term update 规则并不受 prevote 机制的影响,这依然是我们需要遵循的一条真理。

2 个赞