[membership] fix peers change failed when cluster- estart in joint status #4
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
details: baidu#407
issue
change_peers
过程中继续重启,可能导致无法恢复。bad case (from braft wechat group):
leader
在即
leader
完成第一阶段日志leader
开始第二阶段的日志(leader
,继续后续流程。cause
braft 实现中,在节点变更的第二阶段,会将$C_{new}$ 作为日志继续给当前的所有 follower 发送,即使是当前 $C_{new}$ 中不包含的节点。如果能够在提交第二阶段日志时, 不复制 $C_{new}$ 日志到不属于其配置的节点($diff(C_{new,old}, C_{new})$), 那么在集群恢复后,这些节点由于不持有最新的 $C_new$ , 故而可以重新发起选举,完成后续流程。$C_{new}$ 已经被成功提交,那么不属于 $C_{new}$ 的节点由于持有第一阶段的配置 $C_{old, new}$ , 无法获得来自 $C_{new}$ 节点中大多数的投票,故而无法选举 leader (预期的行为)。 而 $C_new$ 由于已经被提交,所以 $C_{new}$ 中的节点能够正常选举 leader, 因此完成了 $C_{new}$ 未被成功提交,那么属于 $C_{new,old}$ 中的节点是可以从 $C_{new}$ 中获得大多数投票,从而完成选举。 如果选举的 leader 包含 $C_{new}$ , 那么第一次日志提交后( flush configuration 等),即可完成 membership change;如果选举的 leader 只包含 $C_{new,old}$ , 重新进入 $C_{new}$ 并完成
安全性
a. 如果集群重启前,
membership change
流程, 集群恢复正常。b. 如果集群重启前,
joint stage
, 并且接着开始复制membership change
的 stable 状态。braft 实现中,如果节点 id 不属于在当前 conf,不允许节点发起选举。如果放松这个限制的话,也可以避免上述问题。$C_{new}$ 的节点发起投票,并且能够获得来自 $C_{new}$ 的大多数投票,当该 leader 完成第一次日志复制后( flush configuration), $C_{new}$ 日志会被提交到 $C_{new}$ 节点中(想当于继续完成了重启前未完成的 membership change 的第二阶段)。 而该 leader 会在完成 stable 阶段完成 step down,不参与后续流程。而此时 $C_{new}$ 节点中也已经持有最新的日志,其他无关节点无法继续参与 $C_{new}$ 的后续事务(因为不包含最新日志)。所以也是安全的。
安全性
a. 如果不属于 conf 的节点可以发起投票,并且当选 leader,那么说明该节点包含当前 conf 中最新的日志,并且获得 conf 中大多数人的同意(自己的投票不会被计数),满足 leader 的安全性,见 raft §5.4.3 Safety argument。此时该节点可以看作是一个代理节点,负责转发 raft 请求,但是不参与 raft 的投票和 commit 日志的决策,不影响 raft 的安全性。
对于本例中,重启后不属于
solutions
a. 如何控制在第一阶段的
stop_replicator
, 将节点设置为readonly
或者发送前日志/接受日志时进行判断等等,这个会增加较多的代码逻辑。b. 如果当前 leader 不在
AppendEntries
需要从LogManager
中获得新的日志进行复制, 这两者存在一定的矛盾,代码逻辑不好做修改。由于当前的 braft 中没有注入错误故障的模块,在构造一些比较复杂的用例时,无法准确控制错误发生的时机,所以引入了
SyncPoint
模块 (copy from rocksdb),提供回调机制来控制节点的行为。使用方法:
test plan
NodeTest.change_peers_restart_cluster_before_stable_stage
模拟了上述 bad case 的用例。
在该修改前,测试会在重启节点之后一直保持无法选主状态,直到超时。
本修改之后,符合预期行为。