Closed yufan022 closed 2 years ago
附上当时的节点日志,否则这样的描述提供不了太大帮助,无助于分析问题。
@killme2008 Hi,我刚刚复现了一下,截取了详细的三节点日志,具体流程:
校验数据,此时以k1为例,发现三个节点数据不一致: worker1: asset=3 worker2: asset=3 worker3: asset=2
相关监控:
黄色worker1 leader
绿色worker2 follower
蓝色worker3 follower
相关日志:
worker3.log
worker2.log
worker1.log
触发onapply方法时会打印onapply update asset: asset[id=xxx,balance=xxx]
猜测:
在日志中发现worker2和3的区别是:
worker2先加载本地快照 -> 再从leader加载快照 -> 加载完快照后没有还原log,猜测从远程加载的installSnapshot
包含了最新变更所以没有重放log的日志,此时asset=3与leader一致。
而worker3从本地加载快照后没有从leader远程加载installSnapshot 接着重放了log 但是此时只重放到asset=2,没执行最新的asset=3。(但是奇怪的是,在worker3宕机前,已经执行了asset=1 asset=2 asset=3,这一点在日志中有体现[worker3.log 51行],但重启后没只执行了asset=2 丢了asset=3[worker3.log 121行])
此时我如果将worker3再次停止,然后将本地snapshot与raftlog全部删除,再重新启动,会用installSnapshot从远程拉取安装,启动后asset=3数据和1 2一致。
这种多数节点先后overload后,重启恢复之后发现三节点之间数据不一致的问题有很大概率可以复现,是我的操作流程有问题么?发生overload之后不能直接重启节点么
看了下,看起来没什么问题,最终日志都复制到了 1966588
这个 log index。
我猜测跟你的 state machine 实现或者 snapshot 实现有关系。
建议在 state machine 增加一些日志,可以在重启的时候开启,查看下 apply 的 log 信息是否正确(index,内容等)
好的,目前思路时我先把save/load snapshot和onApply时状态机的value都打印出来,先排除快照或onApply的问题,再缩小范围看到底缺了哪一条没执行。
添加了详细日志,复现了一次,发现save/load没有问题,符合预期,但是重启时确实有一部分Log没有再次执行:
2022-03-27 13:19:18 [JRaft-FSMCaller-Disruptor-0] INFO xxStorage:324 - onapply: asset[uid=30006,balance=3,updateId=3092764]
...
2022-03-27 13:19:57 [JRaft-FSMCaller-Disruptor-0] INFO xxStorage:324 - onapply: asset[uid=36,balance=4,updateId=3684338]
2022-03-27 13:20:02 [JRaft-FSMCaller-Disruptor-0] INFO xxStorage:324 - onapply: asset[uid=3006,balance=11,updateId=3773166]
2022-03-27 13:20:09 [JRaft-FSMCaller-Disruptor-0] INFO xxStorage:324 - onapply: asset[uid=306,balance=7,updateId=3888209]
2022-03-27 13:20:15 [JRaft-FSMCaller-Disruptor-0] INFO xxStorage:324 - onapply: asset[uid=36,balance=5,updateId=3996983]
2022-03-27 13:20:16 [JRaft-FSMCaller-Disruptor-0] INFO xxStorage:324 - onapply: asset[uid=306,balance=8,updateId=4023468]
2022-03-27 13:20:20 [JRaft-FSMCaller-Disruptor-0] INFO xxStorage:324 - onapply: asset[uid=300006,balance=10,updateId=4095418]
2022-03-27 13:20:23 [JRaft-FSMCaller-Disruptor-0] INFO AbstractStateSnapshotFile:49 - begin save snapshot updateId 4148143
2022-03-27 13:20:26 [JRaft-FSMCaller-Disruptor-0] INFO xxStorage:324 - onapply: asset[uid=306,balance=9,updateId=4193443]
2022-03-27 13:20:41 [xx-snapshot-executor1] INFO xxStorage:598 - Finish sst file /raft/test/raft_group_-1_8082/snapshot/temp/state_temp/default.sst with 9296166 keys.
2022-03-27 13:20:41 [JRaft-FSMCaller-Disruptor-0] INFO xxStorage:324 - onapply: asset[uid=30006,balance=4,updateId=4419925]
2022-03-27 13:20:44 [JRaft-FSMCaller-Disruptor-0] INFO xxStorage:324 - onapply: asset[uid=306,balance=10,updateId=4473126]
2022-03-27 13:20:46 [JRaft-FSMCaller-Disruptor-0] INFO xxStorage:324 - onapply: asset[uid=306,balance=11,updateId=4503209]
2022-03-27 13:20:57 [JRaft-Closure-Executor-15] INFO LocalSnapshotStorage:167 - Deleting snapshot /raft/test/raft_group_-1_8082/snapshot/snapshot_4648195.
2022-03-27 13:20:57 [JRaft-Closure-Executor-15] INFO LocalSnapshotStorage:231 - Renaming /raft/test/raft_group_-1_8082/snapshot/temp to /raft/test/raft_group_-1_8082/snapshot/snapshot_4648195.
2022-03-27 13:20:57 [JRaft-Closure-Executor-15] INFO LocalSnapshotStorage:167 - Deleting snapshot /raft/test/raft_group_-1_8082/snapshot/snapshot_3676298.
2022-03-27 13:21:04 [JRaft-FSMCaller-Disruptor-0] INFO xxStorage:324 - onapply: asset[uid=306,balance=12,updateId=4805868]
2022-03-27 13:21:07 [JRaft-FSMCaller-Disruptor-0] INFO xxStorage:324 - onapply: asset[uid=3006,balance=12,updateId=4862910]
2022-03-27 13:21:10 [JRaft-FSMCaller-Disruptor-0] INFO xxStorage:324 - onapply: asset[uid=300006,balance=11,updateId=4897732]
2022-03-27 13:21:13 [JRaft-FSMCaller-Disruptor-0] INFO xxStorage:324 - onapply: asset[uid=306,balance=13,updateId=4962692]
2022-03-27 13:21:15 [JRaft-FSMCaller-Disruptor-0] INFO xxStorage:324 - onapply: asset[uid=36,balance=6,updateId=4993334]
2022-03-27 13:21:23 [JRaft-FSMCaller-Disruptor-0] INFO xxStorage:324 - onapply: asset[uid=300006,balance=12,updateId=5098623]
2022-03-27 13:21:24 [JRaft-LogManager-Disruptor-0] ERROR StateMachineAdapter:72 - Encountered an error=Status[EBUSY<1009>: FSMCaller is overload.] on StateMachine xx.raftxx.raft.xxStateMachine,
it's highly recommended to implement this method as raft stops working since some error occurs, you should figure out the cause and repair or remove this node.
com.alipay.sofa.jraft.error.RaftException: FSMCaller is overload.
可以从日志中看出,updateId=4148143时开始快照,此时快照中内容应该为: uid 300006 balance = 10 uid 30006 balance = 3 uid 3006 balance = 11 uid 306 balance = 8 uid 36 balance = 5 快照成功后节点overload
overload后重启日志:
2022-03-27 13:26:01 [main] INFO JRaftServiceLoader:275 - SPI service [com.alipay.sofa.jraft.util.JRaftSignalHandler - com.alipay.sofa.jraft.ThreadPoolMetricsSignalHandler] loading.
2022-03-27 13:26:01 [main] INFO JRaftServiceLoader:275 - SPI service [com.alipay.sofa.jraft.util.timer.RaftTimerFactory - com.alipay.sofa.jraft.util.timer.DefaultRaftTimerFactory] loading.
2022-03-27 13:26:01 [main] INFO NodeImpl:555 - The number of active nodes increment to 1.
2022-03-27 13:26:03 [main] INFO FSMCallerImpl:201 - Starts FSMCaller successfully.
2022-03-27 13:26:03 [main] INFO SnapshotExecutorImpl:259 - Loading snapshot, meta=last_included_index: 4648195
last_included_term: 2
peers: "127.0.0.1:8081"
peers: "127.0.0.2:8082"
peers: "127.0.0.3:8083"
.
2022-03-27 13:26:03 [JRaft-FSMCaller-Disruptor-0] INFO AbstractStateSnapshotFile:155 - load uid 4148143
2022-03-27 13:26:06 [JRaft-FSMCaller-Disruptor-0] INFO xxStorage:654 - Start ingest sst file /raft/test/raft_group_-1_8082/snapshot/snapshot_4648195/state/default.sst.
2022-03-27 13:26:07 [JRaft-FSMCaller-Disruptor-0] INFO xxStorage:660 - load asset account 300006 10
2022-03-27 13:26:07 [JRaft-FSMCaller-Disruptor-0] INFO xxStorage:665 - load asset account 30006 3
2022-03-27 13:26:07 [JRaft-FSMCaller-Disruptor-0] INFO xxStorage:670 - load asset account 3006 11
2022-03-27 13:26:07 [JRaft-FSMCaller-Disruptor-0] INFO xxStorage:675 - load asset account 306 8
2022-03-27 13:26:07 [JRaft-FSMCaller-Disruptor-0] INFO xxStorage:680 - load asset account 36 5
2022-03-27 13:26:07 [JRaft-FSMCaller-Disruptor-0] INFO StateMachineAdapter:79 - onConfigurationCommitted: 127.0.0.1:8081,127.0.0.2:8082,127.0.0.3:8083.
2022-03-27 13:26:07 [JRaft-FSMCaller-Disruptor-0] INFO SnapshotExecutorImpl:448 - Node <dev--1/127.0.0.2:8082> onSnapshotLoadDone, last_included_index: 4648195
last_included_term: 2
peers: "127.0.0.1:8081"
peers: "127.0.0.2:8082"
peers: "127.0.0.3:8083"2022-03-27 13:26:07 [main] INFO NodeImpl:724 - Node <dev--1/127.0.0.2:8082> target priority value has changed from: 0, to: -1.
2022-03-27 13:26:07 [main] INFO NodeImpl:1085 - Node <dev--1/127.0.0.2:8082> init, term=2, lastLogId=LogId [index=5653728, term=2], conf=127.0.0.1:8081,127.0.0.2:8082,127.0.0.3:8083, oldConf=.
2022-03-27 13:26:07 [main] INFO RaftGroupService:136 - Start the RaftGroupService successfully.
2022-03-27 13:26:07 [main] INFO GrpcReceiverServer:121 - Start the GrpcReceiverServer successfully, port:18082
2022-03-27 13:26:07 [main] INFO log:170 - Logging initialized @6928ms to org.eclipse.jetty.util.log.Slf4jLog
2022-03-27 13:26:07 [Rpc-netty-server-worker-1-thread-1] WARN BoltRaftRpcFactory:94 - JRaft SET bolt.rpc.dispatch-msg-list-in-default-executor to be false for replicator pipeline optimistic.
2022-03-27 13:26:07 [dev--1/PeerPair[127.0.0.2:8082 -> 127.0.0.3:8083]-AppendEntriesThread0] INFO LocalRaftMetaStorage:132 - Save raft meta, path=/raft/test/raft_group_-1_8082/meta, term=3, votedFor=0.0.0.0:0, cost time=7 ms
2022-03-27 13:26:07 [dev--1/PeerPair[127.0.0.2:8082 -> 127.0.0.3:8083]-AppendEntriesThread0] WARN NodeImpl:1953 - Node <dev--1/127.0.0.2:8082> reject term_unmatched AppendEntriesRequest from 127.0.0.3:8083, term=3, prevLogIndex=5655729, prevLogTerm=3, localPrevLogTerm=0, lastLogIndex=5653728, entriesSize=0.
2022-03-27 13:26:07 [JRaft-FSMCaller-Disruptor-0] INFO StateMachineAdapter:89 - onStartFollowing: LeaderChangeContext [leaderId=127.0.0.3:8083, term=3, status=Status[ENEWLEADER<10011>: Raft node receives message from new leader with higher term.]].
2022-03-27 13:26:07 [dev--1/PeerPair[127.0.0.2:8082 -> 127.0.0.3:8083]-AppendEntriesThread0] INFO Recyclers:52 - -Djraft.recyclers.maxCapacityPerThread: 4096.
2022-03-27 13:26:07 [main] INFO Server:375 - jetty-9.4.45.v20220203; built: 2022-02-03T09:14:34.105Z;
2022-03-27 13:26:07 [main] INFO ContextHandler:921 - Started o.e.j.s.ServletContextHandler@51b01550{/,null,AVAILABLE}
2022-03-27 13:26:07 [main] INFO AbstractConnector:333 - Started ServerConnector@5de6cf3a{HTTP/1.1, (http/1.1)}{0.0.0.0:28081}
2022-03-27 13:26:07 [main] INFO Server:415 - Started @7078ms
2022-03-27 13:26:20 [JRaft-FSMCaller-Disruptor-0] INFO xxStorage:324 - onapply: asset[uid=36,balance=6,updateId=4993334]
2022-03-27 13:26:27 [JRaft-FSMCaller-Disruptor-0] INFO xxStorage:324 - onapply: asset[uid=300006,balance=11,updateId=5098623]
2022-03-27 13:26:49 [JRaft-FSMCaller-Disruptor-0] INFO AbstractStateSnapshotFile:49 - begin save snapshot uid 5155676
2022-03-27 13:27:14 [xx-snapshot-executor1] INFO xxStorage:598 - Finish sst file /raft/test/raft_group_-1_8082/snapshot/temp/state_temp/default.sst with 11311338 keys.
可以看到重启时加载快照中updateId=4148143,并且快照中状态与save快照的日志相同,说明加载快照内容也是正确的: load asset account 300006 10 load asset account 30006 3 load asset account 3006 11 load asset account 306 8 load asset account 36 5
但时中间跳过了中间很多条(重启前 开始快照一直到overload发生),最后只执行了updateId=4993334和5098623的两条,此时中间很多条没执行,好像丢了。
虽然最终Index是最新的,但是中间updateId=4193443,4419925,4473126,4503209,4805868,4862910,4897732,4962692这几条都没有执行,所以造成这个节点数据不一致。
看情况似乎是发生overload前一批的log会有部分保存失败丢失?而且目前来看状态机和快照的实现暂时没发现问题,因为除了发生overload这种情况,数据一致性都是没问题的。
你要确认的是你所谓的丢失的日志,有没有传入 onApply,还是传入,因为你的逻辑忽略不执行了,等等。
在日志提交给 fsm caller 之前,日志都已经保存了的,所以你说的情况是不存在的(commit 的才会提交给 state machine);并且每次都是从 snapshot 重放到最新的,重放过程中如果发现日志丢失也会报错,不会顺利重放到最新的,建议还是再看看你的 onApply
逻辑。
@killme2008 🙏感谢回复,这里再次验证后,如您描述是onApply前后不一致导致的问题,发现是某些场景重启读取快照时状态机RocksDB未清理干净 有脏数据,导致重启状态机时有部分log没有onApply成功导致数据不一致。
不过这里发现另外一个关于leader选举的问题: 描述:停止全部节点,重启后,似乎选举出了错误的leader,或者是cli获取到了错误的leader。
操作过程:
routeTable.refreshLeader routeTable.selectLeader
,发现获取的leader为worker3worker3节点中的快照比较旧,所以需要恢复时间较久,1 2节点快照时间较新 恢复时间较快。 此时在worker3追赶上最新进度之前,理论上应该只有1,2节点先追赶到最新index才能被选为主吧?
日志: worker2-leaderbug.log worker1-leaderbug.log worker3-leaderbug.log
描述:
14:30分使用cli调用selectLeader,发现获取到的leader为worker3(此时leader3还未onApply追赶最新进度,应该不能成为leader)。
同时观察三个节点的日志。
14:20:47 worker3 become leader of group
, 但是没有onLeaderStart
日志。
14:36:08 kill worker3,worker1会出现become leader of group
和onLeaderStart
日志。
此时check三节点内数据,1 2节点已还原最新index,并且一致。3节点还未还原到最新index,并且数据不一致。但是cli获取到的是worker3为leader。
这个是正常现象, refreshLeader 后,得到的leader 永远都只是瞬态的,一般都会建议你设计一个 redirect 重定向协议,服务端收到请求,发现不是 leader,可以把最新的leader 信息发给客户端,或者强制客户端重新刷新 route table,再获取。
这里就是用routetable refresh之后拿到的worker3,不过此时worker3还没还原到最新index,状态数据是旧的。
cli通过获取leader拿到的worker3,是真的成为leader了么?
看worker3的日志14:20:47 worker3 become leader of group
似乎是成为leader了?
你看到那一刻的状态,可能是 worker 停止过程中, worker3 被选为 leader 的瞬间状态,而不是重启后的
log 的 commit 和 apply 是两个过程,并不需要等待 apply 追上才发起选举的。
就是说其实现在新的leader还没有产生,cli拿到的leader是类似于过期数据?等到新leader产生后才会拿到准确leader?
你看到那一刻的状态,可能是 worker 停止过程中, worker3 被选为 leader 的瞬间状态,而不是重启后的
这里每次停止我都是先shutdown worker3,发现leader转移到了1或2,此时cli获取到的也是1或2。再shutdown 1和2。
不过这里worker3确实好像成为了leader,因为有看到become leader of group
日志,理论上此时worker3不能成为leader吧?因为状态机数据还没重放完毕。
worker3:
2022-03-30 14:20:47 [JRaft-RPC-Processor-2] INFO NodeImpl:1222 - Node <dev--1/172.21.43.116:8083> become leader of group, term=11, conf=172.21.41.216:8081,172.21.42.61:8082,172.21.43.116:8083, oldConf=.
而且此时即使cli刷新route table拿到的leader也是worker3
前面都已经说完了,没有更多补充的,选举并不需要等 apply 追上,跟 state machine 状态也没有什么关系。 cli 看到的状态就是当时的状态, worker 3 最多可能就是因为 apply 追的慢,导致 onLeaderStart 通知延迟了。
从你的协议角度就是按照我给的建议来设计,不需要关心这些东西。
懂了,选举不用关心状态机是否应用到最新。
关于选举:所以当时CLI查询到的Leader
是准确的,就是worker3
是Leader
。状态机落后并不影响选举,因为log
是最新的,所以worker3
可以当选leader
,最新提交task
的onApply
最终会在未来时间点上执行。
关于业务:因为我的业务update
请求的是通过RPC
触发提交node.apply(task)
,但是要等到状态机onApply
到RocksDB
中才执行Response
。所以当这种情况的启动时,Leader
当选了但是还未回放onApply
到最新log
,此时RPC
请求都会超时,因为刚刚这次update
请求的onApply
还要等一段时间才能执行到,短时间不能执行Response
,所以这次RPC
会超时。但实际task
是成功提交了的。只有当worker3
追赶完毕,此时新的RPC
才不会超时,因为追赶完毕后很快onApply
到状态机了并且执行response
延迟很短所以不会超时。
关于一致性:目前设计的查询RPC是没走node.apply(task)
,直接查询状态机RocksDB
。那这种情况当worker3
当选Leader
,在leader
刚启动并且在回放历史log
时接收到了RPC
查询,此时通过Leader
查询的数据相当于是历史数据,不能保证强一致性(因为选举不需要等statemachine
追上)。需要让查询也走node.apply(task)
,这样才能保证这种情况不会查询到历史数据。
不知道我的理解正确么?
1 和 2 是 ok 的。 2 的情况避免可以通过 onLeaderStart 来判断是否回放完成,可以看下 demo,会设置 term 作为 leader 是否就绪的标记。
3 可以走线性一致读 Node#readIndex
,具体阅读文档吧。
了解,通常情况新当选的leader应该不会有延迟log情况的。 除非是集群重启这种情况,这种情况如果当选的leader需要回放的log数量比较多,意味着最新RPC的onApply要等很久,RPC会超时。
这种情况能想到的方案是:
这样正确么,或者有其他建议么?
问题3当前的设计可能有问题,这种情况不能保证一致性,我看一下Node#readIndex
。
正常情况下,你的节点重启不是一批的吧? 通过 transferLeader 保证集群可用,分批重启,那就不存在所谓追不上的问题。 apply 通常来讲都不应该延迟很多,可以监控这种情况。
是的,另外发现如果一些不需要等待状态机更新的RPC,也可以直接onCommit提高效率。
没有更多问题了,非常感谢您的帮助!issue我关闭了。
问题: 压测过程中因overload发现多数节点不可用,重启集群后数据不一致问题
描述: 压测过程,因为抖动导致fsm disruptor ringbuffer被打满,jraft抛出overload异常节点变成不可用。此时关闭压测脚本,逐步重启每个节点,经过短暂等待读取快照与同步日志后,校验集群数据,发现数据不一致。(1.3.10增加了block模式,还可以增大ringbuffer size来解决overloa的问题,但是此处主要咨询节点重启后数据不一致的问题)
具体操作顺序:
例如此时经过校验发现:2节点数据与1 3不一致,此时我手动删除2节点的状态机与raft数据,再次重启,观察日志发现2节点主动在1或3的Leader中通过installSnapshot与日志同步数据,完成后再次校验发现2的数据和1 3一致了。
想咨询一下数据不一致是因为我重启的操作有问题么?overload异常后正确的处理方式是怎样呢?是需要找到最后一任Leader的节点手动重启(从本地快照+log恢复),然后将其他两个节点的状态机与raft本地数据全部删除后再重启(从Leader远程恢复)么?不清理数据直接重启的操作是错误的么?
多次复现,偶尔就会出现有个别节点数据不一致的情况,检查日志发现在启动之后,新leader产生后,有检查到reject term_unmatched AppendEntriesRequest日志,不知道是否有影响