Xline command 去重机制(二)—— RIFL 实现

在上一篇文章Xline command 去重机制(一)—— RIFL 介绍中,我们从 command 去重机制的契机开始,介绍了去重的必要性以及目前 Xline 的去重机制存在的一些问题,同时讲解了 RIFL(Reusable Infrastructure for Linearizability) 的工作原理,并对其进行了一些性能分析。本文将在此基础上进一步更深讲解。

CURP 中实现 RIFL 存在的问题

RIFL 的目的是作为一个系统的基础设施,提供 RPC 的 exactly-once semantics,自然也可以应用到 CURP 系统中。在 CURP 的 paper 中,多次提到了 RIFL,以及 RIFL 迁移到 CURP 上需要对其进行的一些更改。

这里推荐没有阅读过之前 Xline 源码解析的读者先去阅读并了解一下 CURP 的原理,再继续阅读会更容易理解。

在 CURP paper §C.1 Modifications to RIFL 中,描述了以下两处更改:

首先,由于 witness 的 replay 机制的存在,任何 command 可能会被重复地被新的 master 接受到,如果我们依赖于 RIFL 提供的去重机制,在 replay command 的时候的乱序可能会导致比较靠后的 acknowledgments 拒绝掉了比较靠前的 command 的 replay。例如,恢复出的 Cmd(first_incomplete=5), Cmd(first_incomplete=1),如果按照 5 -> 1 的顺序 replay,那么 1 会被忽略掉。所以在 witness replay 阶段,不进行 processAck。

其次,RIFL 中,server 在 client crash 导致 client_id 过期时会清理这个 client_id 下的所有完成记录,这时在 witness replay 中,过期的 client_id 的 command 会被忽略。所以 client_id 的过期可能需要延迟到 command 同步到 backup server 上之后。

本文的内容是介绍在 Xline 的 CURP 系统中如何实现 RIFL 并解决一些问题。

Xline 中命令去重的具体实现

Lease Server 的实现

在 RIFL 中,LeaseManager 模块将充当一个 client 存活性的见证者,在 Xline 中,去依赖另一套系统提供 Lease 的机制会令人感到困惑,眼下看来,只有将 Lease Server 实现在 Xline 内部才可以解决这个问题。而在三种节点角色中,Leader 是最适合的。

  1. 实现 Lease Server 之前,我们需要明确需要的功能:
  2. Client 在发送第一个 proposal 时需要获取到自己的 client id,所以它需要向 Lease Server 注册并获得一个 client id
  3. Client 需要每隔一段时间向 Lease Server 发送心跳,以确保 lease 租期长期有效
  4. Client 在自己的 client id 失效时,需要从 Lease Server 处重新获取一个新的 client id
  5. Leader 在检查一个 proposal 是否过期时,需要检查 Lease Server 中该 client id 是否过期
  6. Lease Server 应当在 client id 失效时,将其删除回收

确定了上诉的功能之后,可以得到 Lease Server 的 RPC 定义:

message ClientLeaseKeepAliveRequest {// The optional client_id, 0 means a grant requestuint64 client_id = 1;
}message ClientLeaseKeepAliveResponse {// The refreshed(generated) client_iduint64 client_id = 1;
}service Protocol {...rpc ClientLeaseKeepAlive (stream ClientLeaseKeepAliveRequest)returns (ClientLeaseKeepAliveResponse);
}

RPC 的发送端是一串 stream,stream 中除了第一个申请 client id 的请求以外,接下来的请求都是心跳,用于给 lease 续租。

只有在 Lease Server 发现 client id 过期,或者不存在时,则会生成一个新的 client id 并返回给 client,否则的话,server 将会一直不返回消息。

另外,这也变相解决了 RIFL 论文里描述的一个问题:RPC Server 反复向 Lease Server 检查 Lease 的情况会成为一个瓶颈。

Tracker 的实现

回顾一下 RIFL 中的一些组件,可以看到有两个组件十分相似:RequestTracker 和 ResultTracker。

对于 client 端的 RequestTracker,它需要生成一系列连续的 sequence number,并且记录这些 sequence number 的接受情况,进而推动 first incomplete 增加,确认已经收到的位置并发送给 server 进行回收。

同样的,server 端的 ResultTracker 需要记录来自一个 Client 的 sequence number,并检查之前是否重复,最后根据 client 传来的 first incomplete 回收在此之前所有的记录。

我们很容易想到下面关于 sequence number 序列的一个线性的数据结构设计:

绿色表示 client/server 收到来自 server/client 的 返回/请求

红色表示 client/server 暂时未收到来自 server/client 的 返回/请求

First incomplete 则是第一个 没有收到RPC 的序列号,由于 first incomplete 是由 client 首先推动,再同步到 server 端,所以 server 上的 first incomplete 通常会落后一点(上图中用虚线表示)。

考虑到每个格子只有“绿色”和“红色”两种颜色,这种数据结构设计的最优实现是使用 bitvec(位图),可以节约很多内存的开销,并且在遍历的时候可以 batch 加速。

具体的数据结构如下:

/// Layout:
///
/// `010000000101_110111011111_000001000000000`
///  ^        ^                ^    ^
///  |        |________________|____|
///  |________|         len    |____|
///     head                    tail
#[derive(Debug, Clone)]
struct BitVecQueue {/// Bits storestore: VecDeque<usize>,/// Head length indicatorhead: usize,/// Tail length indicatortail: usize,
}/// Track sequence number for commands
#[derive(Debug, Default, Clone)]
pub(super) struct Tracker {/// First incomplete seq num, it will be advanced by clientfirst_incomplete: u64,/// inflight seq nums proposed by the client, each bit/// represent the received status starting from `first_incomplete`./// `BitVecQueue` has a better memory compression ratio than `HashSet`/// if the requested seq_num is very compact.inflight: BitVecQueue,
}

Tracker 的恢复

上面这些核心的组件正常工作时,一切都显得十分美好,但如果发生了问题,还会这样吗?

比如,leadership transfer 时,新 leader 拿不到旧 leader tracker 中的信息怎么办?很显然,这会出现问题,我们为了性能,每次去重并没有访问状态机,而是维护了一套基于内存的 tracker,就不得不考虑这套数据结构该如何在系统迁移中恢复了。

RIFL 论文中没有详细地介绍如何实现 tracker 的恢复,这是我们实现上遇到的具体的问题。RIFL 作为一个通用机制没有对这种情况展开讨论。不过 RIFL 论文中提到了一点,完成记录需要持久化:

In order to implement exactly-once semantics, RIFL must solve four overall problems: RPC identification, completion record durability, retry rendezvous, and garbage collection

另外,还提出一点,完成记录与操作对象需要存储在一起

RIFL uses a single principle to handle both migration and distributed operations. Each operation is associated with a particular object in the underlying system, and the completion record is stored wherever that object is stored.

如果我们能从操作对象或者完成记录那恢复出 tracker ,就可以避免 tracker 丢失导致误判了。

首先一个问题是完成记录存在哪里,可能这个问题早已有了答案,那就是在 log 里,这条 log 会在系统中被各个节点同步,并应用到自己的状态机上。

但 CURP 系统会有一些区别,Witness 节点上也会存有 proposal,而完成记录也可以来自于这些 proposal,在 CURP 系统的恢复阶段,我们可以从 Witness 中恢复的 proposal 里恢复出 tracker,例如,当我们收到 Cmd(client_id=1, first_incomplete=5) 时,就可以将 id 为 1 的 client 的 ResultTracker 更新到 5(恢复阶段注意不要 processAck,这会导致后续 first_incomplete 小于 5 的 command 被过滤)。基于这种机制,我们就不用担心提前 commit 的 proposal 会因为还没持久化而无法恢复出完成记录。

而在具体的实现中,我们结合了上面的两种情况:从 log 中恢复和从 recovered commands 中恢复。这样才可以保证我们完全恢复了 tracker。

真的完全恢复了吗?

这张图将一个 client 发送的 sequence number 序列分成三段,tracker 恢复的目标就是 client 向 server 确认完成的位置以及后面所有这个 client inflight 的 sequence number。

在这三段中,第一段可以不用恢复,client 都已经确认的话,说明已经返回给用户了,所以 client 不可能会重试这段 command。可能之前有重试过这个 command,并且重试的请求在网络中 delay 了很久,但也不会到达新的 leader 上。

第三段我们可以从 Witness 的 Speculative Pool 中恢复。

而对于第二段,默认情况下读取 log 可能恢复不全,在 log 有 compact 的机制下,我们不能预测 compact 的位置是否在第一段中,为了解决彻底解决这个问题,我们需要对 log compact 做出限制:不允许 compact 任何存活的 client 还未确认的 sequence number 之后。

Lease Server 的可用性问题

在之前有提到,为了不让 Xline 依赖于另一套 Lease 系统,我们把 Lease Server 实现在了 Leader 上,既然 Leader 上的 tracker 面临着可用性的问题,那 Lease Server 自然也有可用性的问题了吗?

其实没有 :D,换一个角度来想,Lease Server 充当着所有 client 存活的见证者,如果 Lease Server 崩溃了,我们不认为 Lease Server 崩溃了,我们认为所有的 client 都崩溃了,从而迫使所有 client 放弃重试,对用户反馈出这种情况,并让用户自己决定是否重试。

事实上,这是一种摆烂的做法,在这种做法下,我们甚至不用费尽心思去解决 tracker 的恢复(新的 leader 上肯定是不接受任何重试 RPC 的)。

既然我们实现了 tracker 的恢复,其中恢复流程得到的 client id 则可以拿来恢复租约,来做到 Lease Server 的恢复。

RIFL 中尚未完成的部分

完成记录持久化

RIFL 要求完成记录需要持久化,并且可以随着系统迁移。这样在一个新的系统中,也能检查出重复并返回给用户之前的执行结果。

我们并没有实现完成记录的持久化和迁移,如果实现这个功能,则需要将 command 的执行结果存储到对应的 log entry 中,对 log 这种数据结构来说,反复 seek 并插入执行结果的操作是不友好的。

所以在 leadership change 之后,新的 leader 检查到了重复的命令,无法获取到之前的完成记录。

可以考虑后续对 log 数据结构进行优化,来实现完成记录的持久化和迁移。

回收机制优化

在看到之前的这张图时,可能敏捷的你会发现这种结构会有一个弊端:队头阻塞

实现 HTTP 协议老前辈们已经摸着石头过河解决了这类问题了:Multiplexing。

不过 RIFL 的作者似乎不想趟这滩浑水

“The garbage collection mechanism could be implemented using a more granular approach that allows information for newer sequence numbers to be deleted while retaining information for older sequence numbers that have not completed. However, we were concerned that this might create additional complexity that impacts the latency of RPCs; as a result, we have deferred such an approach until there is evidence that it is needed.”

事实上这也不是很大的问题,不像 HTTP pipelining,没有收到返回并不影响后续请求的发送(如上图),这可能会导致的结果是 server 上处理完的 command 的结果无法及时回收,占用过多的内存。RIFL 作者提到可以添加一个上限(论文中是 512)来解决这样的问题,不过考虑到这种问题不是无法解决的,后续应该需要进一步优化。

命令去重下的一些结构的优化

RIFL 不仅仅可以作为一个 RPC 的去重机制使用,也可以拿来优化一些 Xline 中的结构。

事实上,去重想法的契机便是发现了 Xline 目前一些组件的定时 GC 机制可能会导致正确性的问题,这些问题的触发条件会在 madsim 的测试环境下被放大,使我们不得不去重新引入一套新的方法来解决这些问题。

目前,Xline 后台的 GC Task 包括 Speculative Pool 的 GC 和 Command Board 的 GC,结合去重的机制,可以将这两种间隔 GC 的 task 更换成 client 确认后主动 GC,这样可以防止不保守的主动 GC 会导致一些正确性上的问题。

Speculative Pool GC 优化

在回收 Speculative Pool 中残留 command 的时候,可以将原来不保守的定时 GC 改成主动询问 Leader 回收,Leader 上可以通过 RIFL 机制一定程度确定一个 command 是否已经 commit。

如果 RIFL 机制判断 command 已经确认,则这个 command 一定 commmit 了,对于一个已经 commit 给 client,但 client 没有确认的 command,RIFL 并不能立刻判断出它已经 commit,则需要在等一段时间,client 确认了这些 command 的时候,Leader 再去检查 command 是否 commit 即可。如果在这段期间内 client 崩溃,Leader 也可以通过 Lease Manager 来判断出 client 已经掉线,则认为它所有 inflight 的 command 全部失效,我们可以安全地清理掉这个 client id 下所有的 command.

CommandBoard GC 优化

同样的,CommandBoard 也有一段 GC 定时任务,这个 GC Task 可能会在极端条件下导致回收了 client 没来得及收到的完成记录,有了 RIFL 机制后,我们可以把定时 GC 换成 client 确认后才进行 GC,这样可以保证 client 一定收到了完成记录。

Summary

在 Xline 的 CURP 系统中,本文深入探讨了 RIFL(Reusable Infrastructure for Linearizability)作为基础设施,为 RPC 提供 Exactly-Once 语义的实现及相关问题的解决方案。主要包括 Lease Server 的设计、Tracker 的构思、优化与可靠恢复,以及在命令去重场景下的后台 GC Task 结构优化。之后,去重机制会进一步优化完成记录持久化以及回收机制的优化,提供更完整的 Exactly-Once 语义的保证和更低的性能开销。

往期推荐

Xline v0.6.1: 一个用于元数据管理的分布式KV存储

Xline command 去重机制(一)—— RIFL 介绍

Karmada 管理有状态应用 Xline 的早期探索与实践

Xline于2023年6月加入CNCF 沙箱计划,是一个用于元数据管理的分布式KV存储。Xline项目以Rust语言写就。感谢每一位参与的社区伙伴对Xline的帮助和支持,也欢迎更多使用者和开发者参与体验和使用Xline。

GitHub链接:

https://github.com/xline-kv/Xline

Xline官网:www.xline.cloud

Xline Discord: 

https://discord.gg/mJdTjzfD

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/577043.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

华清远见STM32U5开发板助力2024嵌入式大赛ST赛道智能可穿戴设备及IOT选题项目开发

第七届&#xff08;2024&#xff09;全国大学生嵌入式芯片与系统设计竞赛&#xff08;以下简称“大赛”&#xff09;已经拉开帷幕&#xff0c;大赛的报名热潮正席卷而来&#xff0c;高校电子电气类相关专业&#xff08;电子、信息、计算机、自动化、电气、仪科等&#xff09;全…

大话设计模式之策略模式

策略模式是一种行为设计模式&#xff0c;它允许在运行时选择算法的行为。这种模式定义了一族算法&#xff0c;将每个算法都封装起来&#xff0c;并且使它们之间可以互相替换。 在策略模式中&#xff0c;一个类的行为或其算法可以在运行时改变。这种模式包含以下角色&#xff1…

零拷贝技术探讨

零拷贝技术是一种用于提高数据传输效率的网络技术&#xff0c;主要应用于网络服务器中。它通过减少数据在操作系统内核空间和用户空间之间的复制次数来提高性能。 在传统的网络服务器中&#xff0c;当客户端向服务器发送请求时&#xff0c;服务器会从磁盘读取数据&#xff0c;…

武汉星起航:借亚马逊平台优势助力商家精准营销

在全球经济一体化的背景下&#xff0c;跨境电商已成为推动国际贸易发展的重要引擎。作为跨境电商领域的佼佼者&#xff0c;亚马逊平台以其全球化销售渠道和强大的数据分析能力&#xff0c;为商家提供了前所未有的商机。武汉星起航电子商务有限公司深知亚马逊平台的优势&#xf…

2024最新Guitar Pro 8.1中文版永久许可证激活

Guitar Pro是一款非常受欢迎的音乐制作软件&#xff0c;它可以帮助用户创建和编辑各种音乐曲谱。从其诞生以来就送专门为了编写吉他谱而研发迭代的。 尽管这款产品可能已经成为全球最受欢迎的吉他打谱软件&#xff0c;在编写吉他六线谱和乐队总谱中始终处于行业领先地位&#x…

MySQK索引

1.认识索引 索引&#xff1a;提高数据库的性能&#xff0c;索引是物美价廉的东西了。不用加内存&#xff0c;不用改程序&#xff0c;不用调sql&#xff0c;只要执行正确的 create index &#xff0c;查询速度就可能提高成百上千倍。但是天下没有免费的午餐&#xff0c;查询速度…

Aurora IP的Framing帧接口和Streaming流接口

本文介绍Aurora IP配置时要选择的接口类型以及两种接口类型之前的区别。 Aurora IP接口有两种模式&#xff1a;Framing帧接口&#xff0c;Streaming流接口 目前一直在用的都是Framing帧接口。 Framing帧接口和Streaming流接口的主要区别是什么呢&#xff1f; 顾名思义&#x…

气象预测新篇章:Python人工智能的变革力量

Python是功能强大、免费、开源&#xff0c;实现面向对象的编程语言&#xff0c;在数据处理、科学计算、数学建模、数据挖掘和数据可视化方面具备优异的性能&#xff0c;这些优势使得Python在气象、海洋、地理、气候、水文和生态等地学领域的科研和工程项目中得到广泛应用。可以…

win10微软拼音输入法 - bug - 在PATH变量为空的情况下,无法输入中文

文章目录 win10微软拼音输入法 - bug - 在PATH变量为空的情况下&#xff0c;无法输入中文概述笔记实验前提条件100%可以重现 - 无法使用win10拼音输入法输入中文替代的输入法软件备注备注END win10微软拼音输入法 - bug - 在PATH变量为空的情况下&#xff0c;无法输入中文 概述…

【数据结构 | 图论】如何用链式前向星存图(保姆级教程,详细图解+完整代码)

一、概述 链式前向星是一种用于存储图的数据结构&#xff0c;特别适合于存储稀疏图&#xff0c;它可以有效地存储图的边和节点信息&#xff0c;以及边的权重。 它的主要思想是将每个节点的所有出边存储在一起&#xff0c;通过数组的方式连接&#xff08;类似静态数组实现链表…

海外媒体发稿:3种媒体宣发套餐内容推广方法

现如今&#xff0c;伴随着信息技术的不断进步和推广&#xff0c;新闻媒体宣发变成企业品牌推广的重要手段之一。为了方便让新闻信息新闻资讯传递给目标群体&#xff0c;公司一般会选择不同的套餐内容和推广方法。下面我们就详细介绍3种新闻资讯新闻媒体宣发套餐内容推广方法。 …

springboot通过threadLocal+参数解析器实现保存当前用户登录信息

首先先介绍一下threadLocal ThreadLocal 线程局部变量&#xff0c;创建一个线程变量后&#xff0c;针对这个变量可以让每个线程拥有自己的变量副本&#xff0c;每个线程是访问的自己的副本&#xff0c;与其他线程的相互独立。 大致知道threadLocal就可以了&#xff0c;然后我…