注意

本文档适用于 Ceph 开发版本。

ECBackend实现策略

其他初始设计笔记

EC 池的初始设计(并且对于没有启用 EC 覆盖调试标志的 EC 池仍然适用)将 EC 池限制在可以轻松回滚的操作上:

  • CEPH_OSD_OP_APPEND: 我们可以通过将前一个对象大小作为 PG 日志事件的一部分来本地回滚追加操作。

  • CEPH_OSD_OP_DELETE: 要回滚删除操作的可能性要求我们保留已删除的对象,直到所有副本都在删除事件中持久化。因此,纠删码后端需要将对象存储在提供给文件存储的键中,包括它们被创建时的版本。当所有副本都提交到删除对象的日志事件时,可以修剪旧版本的对象。

  • CEPH_OSD_OP_(SET|RM)ATTR: 如果我们包含要设置或删除的属性的先前值,我们可以本地回滚这些操作。

日志条目包含一个结构,解释如何本地撤销由该操作表示的操作(参见 osd_types.h:TransactionInfo::LocalRollBack)。

PGTemp和Crush

主节点能够请求一个临时的行为集映射,以允许最新的 OSD 在新主节点回填(以及其他原因)时处理请求。纠删码 PG 需要能够为此原因指定一个主节点,而无需将其放在行为集的第一个位置。它还需要能够在其请求的行为集中留下空白。

时对等的规则。

  • OSDMap::pg_to_*_osds 需要单独返回一个主节点。对于大多数情况,这可以继续是 acting[0]。

  • MOSDPGTemp(以及相关的 OSD 结构)需要能够指定一个主节点以及一个行为集。

  • 大部分现有代码库假设 acting[0] 是主节点,并且行为集中的所有元素都是有效的。这需要清理,因为行为集可能包含空白。

特定的行动集位置

使用复制策略,PG 的所有副本都是可互换的。使用纠删码,行为集中的不同位置具有不同的纠删码方案的一部分,并且是不可互换的。更糟的是,crush 可能会导致块 2 被写入已经包含一个(旧的)块 4 副本的 OSD。这意味着 OSD 和 PG 消息需要在类型如 pair<shard_t, pg_t> 的术语下工作,以便在单个 OSD 上区分不同的 PG 块。

由于对象名称到文件存储中对象的映射必须是 1 对 1 的,我们必须确保块 2 中的对象和块 4 中的对象具有不同的名称。为此,对象存储必须在对象键中包含块 ID。

Core changes:

  • 对象存储ghobject_t 需要还包括一个块 ID使其更像

  • coll_t 需要包括一个 shard_t。

  • OSD pg_map 和类似的 PG 映射需要在 spg_t(本质上

  • 对于客户端到 PG 的消息,OSD 需要有一种方法来知道哪个 PG 块应该接收消息,因为 OSD 可能同时包含同一 PG 的主节点和非主节点块

Object Classes

从对象类读取 EC 池将通过调用特殊的 SYNC 读取返回 ENOTSUP。

刷写

然而,EC 池的主要问题是,在副本上发送存储块的 crc32 并不是特别有帮助,因为不同副本上的块可能存储不同的数据。由于我们不支持除 DELETE 之外的重写,因此我们有一个选项,即通过每个追加操作在每个块上维护一个 crc32。因此,每个副本只需计算其自己的存储块的 crc32,并将其与本地存储的校验和进行比较。然后,副本向主节点报告校验和是否匹配。

对于重写,所有清理操作目前都被禁用,直到我们弄清楚要做什么(参见 doc/dev/osd_internals/erasure_coding/proposals.rst)。

Crush

如果 crush 无法为行为集中的下线成员生成替代品,则行为集应在该位置有一个空白,而不是将行为集中的其他元素移出位置。

ECBackend

主要操作概述

一个 RADOS 放置操作可以跨越单个对象的多个条带。必须有代码将应用程序级别的写入 tessellate 成一组每个条带写入操作——一些完整条带和最多两个部分条带。不失一般性,对于本文档的其余部分,我们将专注于编写单个条带(完整或部分)。我们将使用符号“W”来表示条带内正在写入的块数,即 W <= K。

处理写入 EC 条带的操作有三个数据流。选择哪个数据流取决于写入操作的大小以及所选奇偶校验生成算法的算术属性。

  1. 整个条带被写入/覆盖

  2. 执行读-修改-写操作。

整条带写入

这是一个简单的情况,并且已经在现有代码中执行(对于追加操作而言)。主节点在 RADOS 请求中接收条带的所有数据,计算适当的奇偶校验块,并将数据和奇偶校验块发送到它们的目标分片,这些分片将它们写入。这本质上就是当前的 EC 代码。

读-改-写

主节点确定哪些 K-W 块要保持不变,并从分片中读取它们。一旦所有数据都收到,它将与接收到的新数据结合,并计算新的奇偶校验块。修改后的块被发送到它们各自的分片并写入。RADOS 操作得到确认。

OSD对象写入和一致性

无论选择上述算法,数据的写入是一个两阶段过程:提交和回滚。主节点发送描述操作的日志条目(参见

对于现有条带的覆盖,回滚信息的形式是一个稀疏对象,其中包含使用 clone_range 填充的覆盖范围的老值。这本质上是一个占位符实现,在现实生活中,bluestore 将有一种高效的原始操作用于此。

回滚部分可以延迟,因为我们一旦所有副本都提交了,就报告操作已提交。目前,每当我们发送写入时,我们还指示所有先前提交的操作应该回滚(参见

范围缓存

能够在同一对象上管道写入非常重要。为此,有一个缓存,用于缓存可缓存操作写入的范围。每个范围保持固定,直到引用它的操作被提交。管道阻止 rmw 操作运行,直到从管道中刷新不可缓存事务(克隆等)。

有关缓存状态如何对应于高级不变量(关于并发操作可以在何种条件下引用同一对象)的详细说明,请参阅 ExtentCache.h。

管道

阅读 src/osd/ExtentCache.h 应该已经给出了一个很好的想法,即操作如何可能重叠。处理写入操作涉及多个状态,并且有一个重要的不变量,它没有被 PrimaryLogPG 在高级别强制执行,需要由 ECBackend 管理。重要的不变量是我们不能在同一对象上同时运行不可缓存和 rmw 操作。为了简单起见,我们简单地强制执行任何包含 rmw 操作的操作都必须等待所有正在进行的不可缓存操作完成。

今后在这里还有改进的空间。

更多细节,请参阅 ECBackend::waiting_* 和

由 Ceph 基金会带给您

Ceph 文档是一个社区资源,由非盈利的 Ceph 基金会资助和托管Ceph Foundation. 如果您想支持这一点和我们的其他工作,请考虑加入现在加入.