Xline Jepsen 测试分析

news2025/2/4 12:55:32

Table of Contents

  1. 摘要
  2. 背景介绍
    1. Xline
    2. Jepsen
      1. Checkers
      2. Nemesis
  3. Jepsen 测试设计
    1. 数据一致性
      1. Serializability
      2. Linearizability
      3. Strict Serializability
    2. Jepsen etcd test
      1. Registers
      2. Sets
      3. Append
      4. WR
    3. Jepsen Xline test
  4. 测试结果分析
    1. 测试结果
    2. 异步落盘
    3. Revision生成
      1. 背景
      2. 旧的实现分析
      3. 1-RTT生成revision为什么不可行?
      4. Xline中的修复
  5. References

01、摘要

在本篇文章中, 我们主要会介绍Jepsen在测试分布式KV存储Xline中的应用。 包括对混沌工程框架Jepsen的介绍, 对分布式系统一致性模型的讨论, 以及对有关测试结果的分析。

02、背景介绍

首先我们先来了解一下Xline以及Jepsen测试框架的大致背景。

a. Xline

Xline是一个分布式的KV存储, 现在是CNCF的sandbox项目。Xline和etcd类似, 提供了一个一致性的的kv存储, 以及其他例如watch, 分布式锁的功能。

Xline提供了etcd兼容的API, 但它和etcd最主要的区别是共识协议上的。Xline使用了CURP作为共识协议, 这使它能够在多数情况下都能够仅在1-RTT(Round Trip Time)内就达成共识, 而RAFT达成共识至少需要2-RTT。 因此Xline在高延迟环境下能够达到更好的性能。

b. Jepsen

Jepsen是一个用于分布式系统的验证的框架, 属于混沌工程的范畴。 它提供了一致性检查以及错误注入(fault injection)的功能。具体来说, Jepsen进行的是一项黑盒测试, 在测试中它会模拟真实部署环境, 在这个环境下生成一系列对于数据库的操作, 在测试结束后使用一致性检查器对于操作的结果进行检查, 检验结果满足这个数据库的一致性保证。

i. Checkers

Jepsen使用checkers来对执行结果进行一致性检查。Jepsen目前有两种checkers, 一个是Knossos, 另外一个是Elle。Knossos用来检查结果是否是线性一致的(Linearizable), 而Elle是用来检查数据库事务的一致性的(Transactional Consistency)。注意这些checker并不能保证检测出所有不一致的情况, 因为判定结果是否是线性一致, 以及事务一致性中的可串行化检查(Serializability Checking)都是NP完全问题[1][2]。Jepsen的checkers会限定计算的规模以在较短时间内完成测试。

ii. Nemesis

Jepsen中fault injection的组件叫做nemesis。Jepsen有一些内置的nemesis:

  • kill, 可以kill掉某些节点上数据库的进程
  • pause, 可以pause某些节点上数据库的进程
  • partition, 制造网络分区, 可以是任意两个节点之间, 例如我们可以分区多数/少数节点
  • clock, 可以打乱某些节点上的时钟

这些组件可以模拟在分布式系统部署环境中常见的软硬件错误。 同时, Jepsen作为一个非常灵活的框架, 它支持用户自定义自己的nemesis, 例如, 对于etcd来说, 我们可以让集群成员变更也成为一个nemesis, 达到在测试中定时地增加/删除节点的目的。因此nemesis不仅仅可以用作fault injection, 它也可以用来触发系统中可能出现的一些事件。

03、Jepsen 测试设计

接下来我会对etcd Jepsen测试设计进行详细的分析, 以及介绍我们是如何在Xline上应用Jepsen的测试。

a. 数据一致性

首先我先简单介绍一下三种一致性模型作为下面测试分析的背景, 它们分别是Serializability, Linearizability 以及 Strict Serializability。 这些一致性模型在Jepsen官方网站中有更为详细的介绍[3]。

i. Serializability

Serializability 是一个事务模型, 它是多个对象而言的(例如在etcd中就是多个keys), 并且每一个事务的操作都是原子的。同时它具有几个特性:

  • 内部一致性(Internal Consistency)即一个事务的一个读操作可以观测到前面所有写操作的结果
  • 外部一致性(External Consistency)即一个事务T1中的读观测到另外一个事务T0中的写, 我们称之为T0对于T1可见(Visible), 这样就构成一个可见关系
  • 全序可见性(Total visibility)即所有的可见关系构成一个全序关系, 这其实说明了有一些事务之间是没有这个关系的, 对于所有事务加上这个可见关系构成了一个偏序集

ii. Linearizability

Linearizability是对单个对象而言的, 并且每个操作也是atomic的。 它的所有操作形成一个实时的顺序(Real-Time Ordering)。这里的实时顺序就是指的是这些操作结果的顺序是反映出操作完成时的具体时间的。

iii. Strict Serializability

Strict Serializability则是最强的一致性保证。它可以是看作是 Serializability 和 Linearizability的结合, 即在多个对象上的Linearizablity。那么其实也就是说我们这时候不仅仅是保证全序可见性了, 而是所有的事务都排成了一个实时的顺序。

b. Jepsen etcd test

首先我来简单介绍一下Jepsen官方对于etcd的测试设计。etcd使用的是严格可序列化模型(Strict Serializability Model). Jepsen为etcd编写了以下测试:

i. Registers

Register是Knossos为了检查线性一致性内建的模型, 因为线性一致性是对单个对象而言的, 所以它将单个对象具体化成为一个register, 这个register支持read/write/compare-and-set这三种操作, Knossos会检验对regsiter所有操作的结果是否是线性一致的。

ii. Sets

Sets测试用来检测stale reads。etcd支持设置允许stale reads来达到更好的读性能, 但默认情况下是不允许的。Sets测试只有一个操作compare-and-set, 它对单一的key进行多次操作, 最后检查结果是否是可串行化的, 即检查每个CAS操作都是原子地发生的。

iii. Append

Append测试用来检查严格可序列化, 它有两种操作read/append。Append的意思就是把存储中key的value作为一个list。其中append操作是向这个list中append一个元素。实现append的方式就是先从etcd中读出一个key的值, 然后在一个事务中检查值是否有改变, 如果没有改变就写入append过后的新值。

在这个测试中, 不仅所有的事务都要按照原子地方式进行, 而且Jepsen会同时检查这些事务是否是按照真实时间顺序发生的。

iv. WR

WR测试使用事务向多个key进行read/write, 它同样是检查严格可序列化。

c. Jepsen Xline test

Jepsen测试框架主要分为四个部分: DB, Client, Checkers和Nemesis 每一个部分都是一个单独的接口可以供用户进行实现。由于Xline实现了etcd兼容的API, 所以我们在编写测试的时候直接复用了Jepsen对于etcd的测试。在此测试的基础上, 我们实现了Jepsen中Xline的DB的接口。同时Xline也有自己使用CURP共识的client SDK, 我们同样也对此也实现了Jepsen中client的接口。所以测试实际上是分为对etcd client的兼容测试, 以及Xline native的client的测试(目前还未完成此项测试)。

04、测试结果分析

接下来我会详细讲解Xline Jepsen测试结果以及我们找到的问题。

a. 测试结果

在最初的测试中, 我们遇到的问题可以说是比较多的, 其中问题最多的是和Xline中对于事务操作的部分, 其中有一些问题是很细微的bug, 另外一些则是设计上存在的漏洞。这些问题都花费了大量时间进行调试和识别。

其中我会详细解释两类主要的问题, 分别是异步落盘的问题和revision生成中的问题。

b. 异步落盘

etcd落盘都是同步的, 及当一个节点拿到log的时候, 它会同步地把log持久化到储存设备上, 然后再去执行log中的命令, 在执行完成后再把执行结果同步的持久化到储存设备, 这样做的好处就是集群可以容忍超过多数节点同时关闭/断电而不影响正确性。

有别于etcd的假设, Xline的假设是集群始终存在大多数节点, 而不考虑所有节点都失效的情况, 这也和一些基于内存的Raft实现类似。这给了我们一些优化空间, Xline初始的设计是所有落盘操作都是异步的。但是这么做会有几个问题:

  • 执行的顺序更难推断 由于最后落盘执行结果是异步进行的, 所以我们并不能准确知道logs的执行顺序。在Xline的Read State实现中(在follower本地读, 用于减少leader负载), 一个follower受到一个只读操作后, 它会向leader要一些信息: 步骤一: 如果当前还有未执行完成的commands, 那么拿到他们的ids 步骤二: 如果所有command已经执行完了, 那么拿到当前已经执行完成后的log index 接下来follower会在本地等待这些信息代表的系统状态达成后再进行这个读操作。
  • 这时, 我们之前所述的不确定的执行顺序就引入了不少的复杂性, 导致最开始的实现存在bug:
    • 没有等待这个index之前的所有index代表的log都执行完就去执行了读操作。
    • 实际上步骤一中仅仅拿到command id还不够, 因为log index可能比command id代表的系统状态还要新, 因为我们log的执行是异步的, 没法推断出他们的顺序!
  • 会出现log存储和KV存储不一致的情况 考虑这样的情况: 因为log持久化和KV持久化是异步进行的, 那么在某个节点上KV持久化可能比log持久化先完成, 那么这时候如果节点因为某种原因重启了, 它读出的是没有持久化之前的log, 这会导致在恢复过程中, 同一条log被执行了两次, 而并不是所有的操作都是幂等的, 例如在同一条带有条件的事务(Predicate-Based Trasaction)执行两次是不幂等的. 这也说明了KV持久化必须是在log持久化之后才能进行的。

从以上问题可以得出结论, 异步持久化会带来很多额外的状态需要考虑, 使得系统状态难以推断和分析, 并且潜在的对性能带来一定影响。我们现在正在考虑使用同步的方案来代替异步落盘来简化系统实现, 尽管这可能会牺牲一部分性能, 但是性能始终应该是在正确性之后才需考虑的。

c. Revision生成

在最开始的设计中, 我们希望Xline能够在兼容etcd的同时, 也能够保持1-RTT达到共识的性能。etcd中有revision这一概念, 表示当前的系统修改的次数, 对于每个kv请求, 都需要返回一个revision。因此我们最开始是(错误的)实现了1-RTT生成revision的方法。而最终事实证明, 在1-RTT中生成revision是不可行的。这一实现也导致Jepsen中涉及revision的测试(具体来说是append)无法通过。

下面我来介绍我们旧的实现是怎么样的, 以及这个实现为什么是错误的, 最后讨论1-RTT生成revision为什么不可行。

i. 背景

首先我们需要有一些CURP共识[4]的背景。CURP实现1-RTT的原理是它在每个节点上有一个witness, 在command执行前, client需要把command记录到大多数witness上, 接下来直接在leader执行完不需要复制到follower上即可返回给client, 因为command的信息以及记录在了witness上面, 及时集群少数节点崩溃也能够从witness上恢复出原来的command。而witness上记录的command有两个特点:

  1. command之间都是commute的
  2. command之间没有顺序

这其实就是和Generalized Paxos[5]的实现非常接近, 都是利用commands之间的commutativity来优化请求的时延。而commutativity这一特性也就决定了上述的witness中command的两个特点。因为commutativity的意思就是两个commands之间修改的key不能有交集, 不commute的commands必然是串行执行的。而commute的commands之间是可以并发执行的, 这就表示它们之间没有顺序。

ii. 旧的实现分析

旧的实现中, 每个节点都在本地给每个命令分配revision, 这时候就要保证对于所有commands, revision分配的顺序必须相同。在每个节点的执行阶段前, 我们就会按照log index的顺序给每个节点分配revision。这乍一看可能是正确的, 既然按照log index的顺序分配, 那么我们也不就在各个节点上分配revision的顺序也相同了吗? 但是, 我们忽略了一件事情, 在witness上可能还记录了commands的一些信息! 如果某个节点crash了从witness上恢复的时候, 它并不知道恢复的commands之间的顺序, 这样就会导致revision分配在不同节点上出现不同的顺序。

iii. 1-RTT生成revision为什么不可行?

Revision的想法对于Raft是很自然的, 就是将整个分布式系统抽象为一个状态机, revision的数值就表示当前状态机所在的状态。这表明对于系统的所有的修改操作, 它们是有一个全局的顺序的。

对于CURP共识协议来说, 由于记录在witness上的commands是无序的(由于client的请求是并发的), 想要进行排序那么就必须额外地引入另外一个RTT来在各个witness上进行同步. 这样记录和排序的两步过程至少需要两个RTT。

和Generalized Paxos相同, CURP使用的commutativity就使得commands之间可能是没有顺序的, 那么所有commands形成的是一个偏序关系, 称为command history[5]。在这种情况下, revision的想法就不太适用了。

Generalized Consensus and Paxos论文中, 作者其实也证明了在Generalized Consensus下想要确定任意两个命令之间的顺序是需要至少两个message delays的(Learning is impossible in fewer than 2 message delays), 下面是作者的证明[5]:Suppose l learns a value proposed by p in one message delay. Then it is possible that every message sent by p was lost except for ones received by l. If all messages from p and l to other processes are lost, then there is nothing to prevent another learner from learning a different value proposed by another proposer, violating consistency.

iv.Xline中的修复

  1. etcd兼容性
  2. 经过以上讨论, Xline其实是没办法在兼容etcd revision的情况下依然保持1-RTT的高性能的. 所以在etcd的兼容层中, 我们需要等待每一个command都commit之后再给它分配revison, 这样和etcd相同需要两个RTT。
  3. Append测试
  4. Append测试中的事务测试使用了Revision来确保事务的原子性。Append测试要求的是严格可系列化。在之前的讨论中, CURP共识对于所有commands来说并没有一个全局的顺序, 所以这其实是违反了严格可序列化的要求的, CURP的偏序关系事实上是只保证了可序列化。因此, 在Xline的测试中(非etcd兼容), 我们不能直接使用Append测试。或者说编写Xline的Jepsen测试应当将一致性模型更改为更弱的可序列化模型。

05、References

[1]P. B. Gibbons and E. Korach, “Testing shared memories,”Siam journal on computing, vol. 26, no. 4, pp. 1208–1244, 1997.

[2]C. H. Papadimitriou, “The serializability of concurrent database updates,”Journal of theacm(jacm), vol. 26, no. 4, pp. 631–653, 1979.

[3]“Jepsen consistency.” Available: https://jepsen.io/consistency


 

[4]S. J. Park and J. Ousterhout, “Exploiting commutativity for practical fast replication,” in16th usenix symposium on networked systems design and implementation (nsdi 19), Boston, MA: USENIX Association, Feb. 2019, pp. 47–64. Available:https://www.usenix.org/conference/nsdi19/presentation/park

[5]L. Lamport, “Generalized consensus and paxos,” 2005.

06、往期回顾

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

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

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/XyFXGpSfvb

如果您有兴趣加入达坦科技Rust前沿技术交流群 ,请添加小助手微信:DatenLord_Tech

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

九、Qt图表使用

一、QCharts概述 Qt图表提供了:折线图、样条曲线图、面积图、散点图、条形图、饼图、方块胡须图、蜡烛图、极坐标图。1、QChart介绍 Qt Charts基于Qt的QGraphics View架构,其核心组件是QChartView和QChartQChartView是显示图标的视图,基类为…

如何用gpt快速做好数据分析?

由于技术限制,目前InfinitePaper AI仅支持上传1份文件,且大小不超过10M。但是,在强大的代码解释器面前,这都是小问题。我们只需要将可能用到的文件打包成压缩文件上传即可,之后要求GPT直接解压就能正常完成后续需求。 …

Vue学习之使用HBuilderX创建并使用vue3.0项目

Vue学习之使用HBuilderX创建并使用vue3.0项目 下文将简述如何使用HBuilderX创建并使用vue3.0项目,包含项目创建、目录介绍、如何引用组件、首页自定义设置。 1、创建vue3.0项目 具体操作之前章节已经阐述过不在冗余介绍,创建时选择vue3项目即可。vue2…

2023年03月CCF-GESP编程能力等级认证Python编程二级真题解析

Python等级认证GESP(1~6级)全部真题・点这里 一、单选题(共15题,共30分) 第1题 以下存储器中的数据不会受到附近强磁场干扰的是( )。 A:硬盘 B:U 盘 C:内存 D:光盘 答案:D 第2题 下列流程图,属于计算机的哪种程序结构?( ) A:顺序结构 B:循环结构 C:…

《Lua程序设计》-- 学习10

环境(Environment) 具有动态名称的全局变量 全局变量的声明 由于Lua语言将全局变量存放在一个普通的表中,所以可以通过元表来发现访问不存在全局变量的情况。 正如前面所提到的,我们不允许值为nil的全局变量,因为值为…

C语言·贪吃蛇游戏(上)

1. 游戏任务 使用C语言在Windows环境的控制台中模拟实现小游戏贪吃蛇 游戏中要包含以下功能: 1. 贪吃蛇地图绘制 2. 贪吃蛇上下左右移动和吃食物 3. 蛇撞墙,或撞到自身死亡 4. 计算得分 5. 蛇身加速、减速 6. 暂停游戏 2. Win32 API 介绍 Windows是一种多…

【MATLAB源码-第131期】基于matlab的淘金优化算法(GRO)机器人栅格路径规划,输出做短路径图和适应度曲线。

操作环境: MATLAB 2022a 1、算法描述 淘金优化算法(GoldRush Optimizer,简称GRO)是一种启发式优化算法,它受到淘金过程的启发。在淘金过程中,淘金者在河流或矿区中寻找金矿,通过筛选沙砾来寻…

病历管理系统

技术架构: StrutsSpringHibernate 有需要该项目的小伙伴可以私信我你的Q。 功能描述: 企业财务管理系统主要用于电子病历来提高医院各项工作的效率和质量,促进医学科研、教学;减轻各类事务性工作的劳动强度,使他们…

2024美赛选题建议+ABCDEF思路分享

选题建议 A题 题目见: https://zxljhy.blog.csdn.net/article/details/135985116https://zxljhy.blog.csdn.net/article/details/135985116建议: A题是机理分析,需要去找到合适的数据对物种发展规律进行研究,结合生态学原理和动…

Windows SDK(三)GDI图形绘制基础

GDI介绍 GDI:图形设备接口:Windows的子系统,负责在视讯显示器和打印机上显示图形。 GDI常见名词解释: 直线 曲线 填入区域:直线或曲线构成的封闭区域,可以用画刷进行填充 位图:点阵图像 文字 映…

DEV-C++ ege.h库 绘图教程合集

一、目录 DEV-C ege.h库 绘图教程(一)——基本绘图函数 DEV-C ege.h库 绘图教程(二)——高级绘图函数(上) DEV-C ege.h库 绘图教程(三)——高级绘图函数(下)时…

探索自然语言处理在改善搜索引擎、语音助手和机器翻译中的应用

文章目录 每日一句正能量前言文本分析语音识别机器翻译语义分析自然语言生成情感分析后记 每日一句正能量 努力学习,勤奋工作,让青春更加光彩。 前言 自然语言处理(NLP)是人工智能领域中与人类语言相关的重要研究方向&#xff0c…

day07-CSS高级

01-定位 作用:灵活的改变盒子在网页中的位置 实现: 1.定位模式:position 2.边偏移:设置盒子的位置 left right top bottom 相对定位 position: relative 特点: 不脱标,占用自己原来位置 显示模…

最长的指定瑕疵度的元音子串 - 华为OD统一考试

OD统一考试(C卷) 分值: 200分 题解: Java / Python / C 题目描述 开头和结尾都是元音字母(aeiouAEIOU)的字符串为元音字符串,其中混杂的非元音字母数量为其瑕疵度。比如: “a” 、 “aa” 是元…

视频怎么加水印?分享两个简单的加水印的方法

在数字媒体时代,视频已经成为信息传播的重要方式。许多人在创作视频是会加上自己独特的水印,防止视频被盗用。水印作为数字版权保护技术的一种,可以有效地防止视频被非法复制、传播或篡改,从而保护创作者的权益和利益。下面我分享…

小程序中picker多列选择器

需求&#xff1a;实现类似省市联动的效果&#xff0c;选择第一列后&#xff0c;第二列数据变化 html部分: <view class"section"><view>多列选择器</view><picker mode"multiSelector" bindchange"bindMultiPickerChange"…

动环系统断电告警的防误报

机房一般接入的市电为三相380伏特&#xff0c;也有用单向220伏特的。UPS本身提供断电告警的功能&#xff0c;这个告警在各种种类的UPS中都是提供的&#xff0c;不同电压的市电输入都支持&#xff1b;三相电另外有缺相告警事件。但这些告警事件存在抖动或者误判。 瞬间的低压或…

C语言标准库所有字符串操作库函数汇总

以下是C语言标准库中字符串操作相关的API列表&#xff0c;这些函数通常在 <string.h> 头文件中定义&#xff1a; 1. strlen - 计算字符串长度&#xff0c;不包括结尾的空字符\0&#xff1a; size_t strlen(const char *str); 2. strcpy - 复制字符串&#xff1a; c…

网络编程套接字(3)

网络编程套接字 简单的TCP英译汉服务器地址转换函数字符串IP转整数IP整数IP转字符串IP关于inet_ntoa函数并发场景下的inet_ntoa函数绑定失败问题TCP协议通讯流程数据传输的过程数据交互四次挥手的过程端口连接 简单的TCP英译汉服务器 之前我们是以回调的方式处理任务的&#x…

再谈启动一个Activity大致时序图

太多了&#xff0c;笔者不想写&#xff0c; 读者可通过PlantUML插件查看如下PUML文件生成的时序图。 补充说明下&#xff0c;Android31版本。 startuml https://plantuml.com/sequence-diagram skinparam dpi 800 scale 15000 width scale 5000 heightautonumber Launcher La…