sofastack / sofa-jraft

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

sofa-jraft如何处理第三态请求(超时请求) #1071

Closed boringhello closed 7 months ago

boringhello commented 7 months ago

网上文章通常有两个处理办法: 1.对于每一个请求都加上一个唯一的序列号的标识, 然后server的状态机会记录之前已经执行过序列号. 当一个请求超时的时候, 默认的client 的逻辑会重试这个逻辑, 在收到重试的逻辑以后, 由于server 的状态机记录了之前已经执行过的序列号信息, 因此不会再次执行这条指令, 而是直接返回给客户端。这个方法个人感觉效率上不是很好,要多持久化操作信息。

  1. 只提供幂等操作(Put, Delete),但是网上有这样的说法(如下)使我感到困惑,不知道这说法对不对。困惑:线性一致性对于单个请求(Put)来说也是成立的,但是如果以MustPut的视角(不断请求直到成功) 来说确实有下面的问题。

有人可能认为,只要写请求是幂等的,那重复执行多次也是可以满足线性一致性的,实际上则不然。考虑这样一个例子:对于一个仅支持 put 和 get 接口的 raftKV 系统,其每个请求都具有幂等性。设 x 的初始值为 0,此时有两个并发客户端,客户端 1 执行 put(x,1),客户端 2 执行 get(x) 再执行 put(x,2),问(客户端 2 读到的值,x 的最终值)是多少。对于线性一致的系统,答案可以是 (0,1),(0,2) 或 (1,2)。然而,如果客户端 1 执行 put 请求时发生了上段描述的情况,然后客户端 2 读到 x 的值为 1 并将 x 置为了 2,最后客户端 1 超时重试且再次将 x 置为 1。对于这种场景,答案是 (1,1),这就违背了线性一致性。归根究底还是由于幂等的 put(x,1) 请求在状态机上执行了两次,有两个 LZ 点。因此,即使写请求的业务语义能够保证幂等,不进行额外的处理让其重复执行多次也会破坏线性一致性。当然,读请求由于不改变系统的状态,重复执行多次是没问题的。

所以,我的问题主要是对第二点幂等是否满足线性一致性的疑惑,以及sofa-jraft对超时请求的处理策略。希望大佬能抽空解答一下

shihuili1218 commented 7 months ago

这是业务需要考虑的问题,jraft本身不会区分某个请求是正常发送还是重试发送。

另外你所举例,不是共识算法讨论的线性一致性场景,这是业务上线性一致性。解释下 (1,1)的场景,是因为客户端1的重试,而重试对于共识算法来说也是一个普通的请求,对于共识算法来说,它是满足线性一致性的。

你说的情况,刨根揭底就是业务需要实现幂等,无非考虑在哪个阶段做。

boringhello commented 7 months ago

感谢你的解答,但是这里

你应该先查询,未达成共识再考虑重试逻辑

是说Get查看是不是value等于Put的value吗,这样还是可能出现(1,1),因为Get查询的值已经Put成功, 但是被别的操作(Put x 2)修改了

shihuili1218 commented 7 months ago

不是,我说的查询是jraft提供的readIndex方法

boringhello commented 7 months ago

对啊,readindex走共识,但是Put x 2已经先共识了

shihuili1218 commented 7 months ago

所以你在readIndex时,会发现Put x 2已经先共识了,那么不重试了啊。

boringhello commented 7 months ago

还是不太理解,你说的是这个吗:在事先已经知道x为0, 要改为1的情况下,发现变为2是可以不用重试?但是这个可能ABA 破坏这个线性一致性。