sofastack / sofa-jraft

A production-grade java implementation of RAFT consensus algorithm.
https://www.sofastack.tech/projects/sofa-jraft/
Apache License 2.0
3.56k stars 1.14k forks source link

rheakv快照问题 #653

Open zhaorui9303 opened 3 years ago

zhaorui9303 commented 3 years ago

预制条件: rheakv开启pd模式并开启快照,三副本,底层存储采用rocksdb

操作步骤: 1.先启动两个rheakv服务 server1和server2,server1和server2会启动默认的region -1; 2.然后往server1中添加数据直到达到分裂阈值,此时pd会触发分裂。server1和server2上的region变为 region -1 和region 1000000 ; 3.等待server1和server2完成写快照后启动server3,因为server3为首次启动,所以会启动默认region -1;

问题描述: server3启动后会从region -1的对应leader上同步日志,因为 -1已经生成过快照,而快照中只保存了rocksdb中的数据,并没有保存分裂操作,所以server3无法同步到分裂操作,导致每个store上的分区不一致。

fengjiachun commented 3 years ago

好像没看出有什么问题呀,没有承诺过每个 store 上都需要有全部分区

zhaorui9303 commented 3 years ago

那这样region100000 其实只有两个两个副本存活,后续如果继续添加数据,属于region1000000分区的数据只会落到server1和server2中,如果server2所在节点宕机了,那region1000000对应的raft组也就无法进行工作了,继续添加数据如果命中region1000000的话就会出现报错了

fengjiachun commented 3 years ago

如果 server2 从未初始化过,那么会基于本地 conf 启动,本地 conf 不包含 region 1000000, 我认为这种情况没问题,就应该是这个逻辑

如果 server2 已经初始化过,现在只是重启恢复 region1,那么它的 conf 会从 pd server 上获取,这个如果要修复的话可能需要在 split 成功后往 pd server 写一下 server2 的 conf 数据(由发起 split 的 leader 执行)?这样在 server2 启动是就会启动 region 1000000

yufan022 commented 2 years ago

如果 server2 已经初始化过,现在只是重启恢复 region1,那么它的 conf 会从 pd server 上获取,这个如果要修复的话可能需要在 split 成功后往 pd server 写一下 server2 的 conf 数据(由发起 split 的 leader 执行)?这样在 server2 启动是就会启动 region 1000000

@fengjiachun Hi,请问下这里重启节点从pd server拉取最新的配置(只有pd有最新分裂后region信息)传递给启动节点的操作,这里要业务层自己来实现么(在启动时业务自己调用pd server拿最新配置)?在代码中没找到拉取配置这块儿相关的逻辑。

fengjiachun commented 2 years ago

如果 server2 已经初始化过,现在只是重启恢复 region1,那么它的 conf 会从 pd server 上获取,这

从 pd 获取 region 的代码在这里

https://github.com/sofastack/sofa-jraft/blob/master/jraft-rheakv/rheakv-core/src/main/java/com/alipay/sofa/jraft/rhea/StoreEngine.java#L177

yufan022 commented 2 years ago

https://github.com/sofastack/sofa-jraft/blob/0a79d749791e28f58b83262e7287a0cd4c4f3b21/jraft-rheakv/rheakv-core/src/main/java/com/alipay/sofa/jraft/rhea/StoreEngine.java#L671 @fengjiachun 例如节点配置中“regionEngineOptionsList”配置了2个region,此时pd发生split为3个region。那么此时如果节点宕机,直接重启,配置文件2个region!=pd的3个region,会报错,无法启动?

因为rOptsList是从本地配置取的,region数量2,而regionList = store.getRegions();是从pd获取的region数量为3,好像这里通不过校验?感觉是我遗漏了哪里

fengjiachun commented 2 years ago

是分裂以后重启吗?能否给出再现问题的步骤?

yufan022 commented 2 years ago

@fengjiachun 这里只是在阅读代码发现的疑问

此处强制校验必须相等 rOptsList.size() == regionList.size() https://github.com/sofastack/sofa-jraft/blob/0a79d749791e28f58b83262e7287a0cd4c4f3b21/jraft-rheakv/rheakv-core/src/main/java/com/alipay/sofa/jraft/rhea/StoreEngine.java#L671

其中rOptsList来自本地配置文件 https://github.com/sofastack/sofa-jraft/blob/0a79d749791e28f58b83262e7287a0cd4c4f3b21/jraft-rheakv/rheakv-core/src/main/java/com/alipay/sofa/jraft/rhea/StoreEngine.java#L669

regionList来自storestore从pd获取 https://github.com/sofastack/sofa-jraft/blob/0a79d749791e28f58b83262e7287a0cd4c4f3b21/jraft-rheakv/rheakv-core/src/main/java/com/alipay/sofa/jraft/rhea/StoreEngine.java#L670 https://github.com/sofastack/sofa-jraft/blob/0a79d749791e28f58b83262e7287a0cd4c4f3b21/jraft-rheakv/rheakv-core/src/main/java/com/alipay/sofa/jraft/rhea/StoreEngine.java#L177

如果本地配置最初写的2个region,运行中被pd分裂为3个region之后,节点宕机重启时似乎671行校验永远通不过? rOptsList.size 2 != regionList.size 3

yufan022 commented 2 years ago

@fengjiachun 我来重新简单描述下问题,在走读代码过程发现的疑问:当PD主动触发分裂后,PD中获取最新region数量比本地配置的regionEngineOptionsList要多,那此时如果该节点宕机重启,此处校验似乎无法通过? image

fengjiachun commented 2 years ago

@fengjiachun 我来重新简单描述下问题,在走读代码过程发现的疑问:当PD主动触发分裂后,PD中获取最新region数量比本地配置的regionEngineOptionsList要多,那此时如果该节点宕机重启,此处校验似乎无法通过? image

明白了,你说的对,是有这个问题,重启后因为没有 options 配置而无法启动,看来要将 region options 放到 pd,愿意的话欢迎贡献一个 PR

yufan022 commented 2 years ago

好的,后续我单提一个issue和pr,这里有思路和建议么?应该不需要将整个本地regionEngineOptionsList都放到pd中吧

fengjiachun commented 2 years ago

好的,后续我单提一个issue和pr,这里有思路和建议么?应该不需要将整个本地regionEngineOptionsList都放到pd中吧

还没有细想,不过至少新分裂出来的 region 的 RegionEngineOptions 应该要放进 pd 的,可以在 com.alipay.sofa.jraft.rhea.StoreEngine#doSplit 后将信息注册到 pd 中,启动时再从 pd 拉回来;可以看看能否先给出设计方案