【问题标题】:What if log replication out-of-order of etcd raft?如果 etcd raft 的日志复制乱序怎么办?
【发布时间】:2021-06-26 04:24:42
【问题描述】:

我是etcd的新手,对日志复制有一些困惑:

  1. 例如,leader 发出 {term:2,index:3} 再发出 {term:2,index:4},多数也按顺序响应。但是由于网络延迟,leader接收响应乱序,先接收到{term:2,index:4}的响应。

etcd 如何处理这种情况?好像只是忽略日志{term:2,index:3},直接提交{term:2,index:4}。

func (pr *Progress) MaybeUpdate(n uint64) bool {
    var updated bool
    if pr.Match < n {
        pr.Match = n
        updated = true
        pr.ProbeAcked()
    }
    pr.Next = max(pr.Next, n+1)
    return updated
}
  1. 当响应数据包丢失时,etcd 如何重试(例如 {term:2,index:3} 的响应)?我在 etcd 项目中找不到任何代码 sn-p 来处理这个问题。

【问题讨论】:

    标签: etcd consensus raft


    【解决方案1】:

    您提出的问题比与 etcd 相关的问题更多(etcd 实现了 raft,因此它们仍然是相关的)。要获得对 raft 算法的高级理解,我强烈建议您查看 raft webpageraft paper(写得真好!)。我相信第 5.3 节“日志复制”会有所帮助。

    首先让我们打个基础:Leader 跟踪与每个 Follower 匹配的条目。它将信息保存在论文中的nextIndex[]matchIndex[](见图2)和etcd 中的ProgressMap

    // ProgressMap is a map of *Progress.
    type ProgressMap map[uint64]*Progress
    
    type Progress struct {
        Match, Next uint64
        ...
    }
    

    现在让我们跳到你的问题。

    1. 例如,leader 发出 {term:2,index:3} 然后发出 {term:2,index:4},大多数也按顺序响应。但是由于网络延迟,leader 接收到的响应乱序,首先接收到 {term:2,index:4} 的响应。 etcd 如何处理这种情况?好像只是忽略日志 {term:2,index:3},直接提交 {term:2,index:4}。

    这一切都取决于追随者的状态(从领导者的角度来看)。让我们深入了解StateProbeStateReplicate

    StateProbe 中,领导者试图找出应该发送给追随者的条目。它一次发送一条消息并等待响应(这可能是拒绝响应,在这种情况下,领导者必须减少与该关注者相关的Next 并重试)。在这种状态下,无法向同一个关注者发送 2 个不同的 MsgApp

    StateReplicate 中,领导者假设网络稳定并(可能)发送许多MsgApp 消息。让我们举个例子。

    Match := 2, Next := 2

    关注日志:[E1, E2](E代表“入口”)

    领队日志:[E1, E2]

    在这个状态下,leader 收到对条目 E3、E4 和 E5 的 put 请求。让我们假设最大批量大小为 2,因此所有新条目都不能在单个消息中发送。领导者将发送 2 条消息:(Index: 3, Entries: [E3, E4])(Index: 5, Entries: [E5])。第二条消息将在获得第一个消息的 ack 之前发送。如果在图片中,follower 收到第一条消息,检查它是否可以使用来自请求的Index 附加它(检查在(raft).handleAppendEntries &gt; (raftLog).maybeAppend &gt; (raftLog).matchTerm &gt; (raftLog).term 中执行),将条目附加到它的日志并发送 ack。稍后,follower 收到第二个请求并对其执行相同的操作(检查它是否可以附加它并发送 ack)。

    follower 在发送 ack 之前检查它是否可以附加条目的事实在这里很重要。一旦领导者对任何消息进行确认,就可以确保直到Index + len(Entries) 的所有条目都填充在跟随者的日志中(否则该消息将被拒绝而不是被确认)。多亏了这一点,第一次确认是否延迟(甚至丢失)并不重要。

    1. 当响应数据包丢失时,etcd 如何重试(例如 {term:2,index:3} 的响应)?我在 etcd 项目中找不到任何代码 sn-p 来处理这个问题。

    我现在将专注于 etcd,因为在 raft 论文中它被描述为“领导者无限期地重试 AppendEntries RPC”,这是相当不具建设性的。每隔一小段时间,leader 发送MsgHeartbeat 给follower,follower 回复MsgHeartbeatResp。作为MsgHeartbeatResp 处理的一部分,领导者会关注

    if pr.Match < r.raftLog.lastIndex() {
        r.sendAppend(m.From)
    }
    

    这应该读作:“如果有任何条目不存在于追随者身上,请将第一个缺失的条目发送给他”。这可以看作是重试机制,因为pr.Match 不会在没有跟随者 ack 的情况下增加。

    【讨论】:

    • 非常感谢,第二个问题我现在很清楚了。对于第一个问题,领导者如何保证在前一个条目被确认之前不会发送新条目?在stepLeader函数中,当MsgProp到来时,leader追加entry(也更新为Next),然后发送给peer,但不检查上一个entry是否响应。
    • 你是对的,确实。我上面描述的(只是稍微)简化了。事实上MsgApp 拥有许多Entry 而不是一个。这意味着如果我们没有对旧消息的确认,我们将再次发送这些条目(连同新的)。想象一下,领导者向追随者发送了条目 [E1, E2, E3]。然后它得到了增加E4的建议。由于 [E1, E2, E3] 没有被确认(因此 Next 这个对等点没有改变),它会发送 [E1, E2, E3, E4] 给追随者。请注意,对这些请求中的任何一个进行确认都是有效的。它将根据请求生成Next = Next + 3Next = Next + 4
    • 你上面描述的例子只发生在StateProbe?如果follower在StateReplicate,leader发送条目[E1,E2,E3]给follower并设置Next = Next + 3。然后它得到添加E4的提议,leader发送条目[E4]并设置Next = Next + 1。这是因为leader每次发出entry都会调用Progress函数OptimisticUpdate
    • 你是 100% 真实的。感谢您指出这一点(实际上教会了我一些东西:D)。我已经深入研究了StateReplicate 路径,发现如果您的图片上有描述,一切都会好起来的,因为追随者确认了这两条消息。领导者将首先获得第二个确认并相应地更新Match(因为第二个请求的确认实际上意味着“跟随者拥有索引4的所有条目”)。第二个出现的第一个 ack 将被简单地忽略,因为 MsgAppResp 中的 pr.MaybeUpdate(m.Index) 处理将是 false
    • 假设领导者获得了第二个确认,但第一个确认丢失了。真的很困扰,好像是直接更新follower的Match到4,没有重试索引3。
    猜你喜欢
    • 2010-12-31
    • 2020-10-28
    • 2022-11-25
    • 1970-01-01
    • 1970-01-01
    • 2016-10-12
    • 1970-01-01
    • 1970-01-01
    • 2011-01-20
    相关资源
    最近更新 更多