注意

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

部分对象恢复

部分对象恢复提高了基于日志的恢复(与回填相比)的效率。原始的基于日志的恢复根据 pg_log 的差异来计算 missing_set。

如果 pg_log 指示对象已修改,则应从 OSD 恢复整个对象,无论对象中实际有多少内容被修改。这意味着一个 4M 的对象,即使内部只修改了 4k,也应该恢复整个 4M 对象,而不是修改的 4k 内容。此外,即使对象映射本身没有修改,也应该恢复对象映射。

部分对象恢复旨在解决上述问题。为了实现目标,需要做两件事:

  1. 需要记录对象修改的位置

  2. 也需要记录对象的对象映射是否被修改

引入 ObjectCleanRegion 类来执行我们想要的操作。clean_offsets 是一个 interval_set<uint64_t> 类型的变量,用于指示对象中的未修改内容。clean_omap 是一个 bool 类型的变量,指示对象映射是否被修改。new_object 表示 osd 不存在于对象中。max_num_intervals 是 clean_offsets 中区间的上限,以确保 clean_offsets 的内存成本始终有界。

如果 clean_offsets 中的区间数量超过边界,则将修剪最短的 clean 区间。

例如。max_num_intervals=2, clean_offsets:{[5~10], [20~5]}

那么新区间 [30~10] 将驱逐出最短的区间 [20~5]

最终,clean_offsets 变为 {[5~10], [30~10]}

部分对象恢复程序

首先,OpContext 和 pg_log_entry_t 应该包含 ObjectCleanRegion。在 do_osd_ops()、finish_copyfrom()、finish_promote() 中,ObjectCleanRegion 中的相应内容应该标记为脏,以便跟踪对象的修改。同时更新 OpContext 中的 ObjectCleanRegion 到其 pg_log_entry_t。

其次,pg_missing_set 可以正确构建和重建。在配对过程中计算 pg_missing_set 时,还应合并每个 pg_log_entry_t 中的 ObjectCleanRegion。

例如。对象 aa 有 pg_log:

26’101 {[0~4096, 8192~MAX], false}

26’104 {0~8192, 12288~MAX, false}

28’108 {[0~12288, 16384~MAX], true}

对象 aa 的 missing_set:合并上述 pg_log --> {[0~4096, 16384~MAX], true}。这意味着 4096~16384 被修改,并且在版本 28’108 上对象映射也被修改。

此外,OSD 可能会在合并日志后崩溃。因此,我们需要读取日志并重建 pg_missing_set。例如,pg_log 是:

对象 aa:26’101 {[0~4096, 8192~MAX], false}

对象 bb:26’102 {[0~4096, 8192~MAX], false}

对象 cc:26’103 {[0~4096, 8192~MAX], false}

对象 aa:26’104 {0~8192, 12288~MAX, false}

对象 dd:26’105 {[0~4096, 8192~MAX], false}

对象 aa:28’108 {[0~12288, 16384~MAX], true}

原本,如果 bb、cc、dd 被恢复,而 aa 没有被恢复。因此,我们需要为对象 aa 重建 pg_missing_set,并找到 aa 在版本 28’108 上被修改。如果对象信息中的版本 26’96 < 28’108,我们不需要考虑 26’104 和 26’101,因为整个对象将被恢复。然而,部分对象恢复也应该要求我们重建 ObjectCleanRegion。

知道对象是否被修改是不够的。

因此,我们还需要在之前遍历 pg_log,也就是说 26’104 和 26’101 也 > object_info(26’96),并基于这三个日志:28’108、26’104、26’101 重建对象 aa 的 pg_missing_set。合并日志的方法与上述相同。

最后,基于 pg_missing_set 完成推送和拉取过程。根据 pg_missing_set 中的 ObjectCleanRegion 更新 recovery_info 中的 copy_subset。copy_subset 表示需要拉取和推送的内容的区间。

这里的复杂部分是 submit_push_data,并且应该考虑几种情况。我们需要考虑的是如何处理对象数据,对象数据由 omap_header、xattrs、omap、data 组成:

情况 1:首先 && 完整:由于对象恢复是在单个 PushOp 中完成的,我们希望保留原始对象并直接在对象上覆盖。对象不会被删除,也不会触及新对象。

问题 1:由于对象没有被删除,旧 xattrs 仍然存在于旧对象中,但可能在新对象中更新。对于相同的键覆盖或添加新键是正确的,但删除键是错误的。为了解决这个问题,我们需要删除对象中的所有原始 xattrs,然后更新新的 xattrs。

问题 2:由于对象没有被删除,对象映射可能会根据 clean_omap 恢复。因此,如果恢复 clean_omap,我们需要删除对象的旧 omap,原因与上述相同,因为 omap 更新也可能是一个删除。因此,在这种情况下,我们应该做:

  1. 清除对象的 xattrs

  2. 如果需要恢复 omap,清除对象的 omap

  3. 将对象截断为 recovery_info.size

  4. 恢复 omap_header

  5. 恢复 xattrs,如果需要则恢复 omap

  6. 如果 fiemap 告知那里没有内容,则对原始对象打零

  7. 覆盖被修改的对象内容

  8. 完成恢复

情况 2:首先 && !完整:对象恢复应该在多次执行。在这种情况下,target_oid 将指示 pgid_TEMP 中的一个新 temp_object,因此问题略有不同。

问题 1:由于对象是新创建的,因此不需要处理 xattrs

问题 2:由于对象是新创建的,并且根据 clean_omap 可能不会传输对象映射。因此,如果 clean_omap 为 true,我们需要从原始对象克隆对象映射。问题 3:由于对象是新创建的,未修改的数据不会被传输。因此,我们需要从原始对象克隆未修改的数据。因此,在这种情况下,我们应该做:

  1. 删除 temp 对象

  2. 创建一个新的 temp 对象

  3. 为新的 temp 对象设置 alloc_hint

  4. 将新的 temp 对象截断为 recovery_info.size

  5. 恢复 omap_header

  6. 如果 omap 干净,从原始对象克隆对象映射

  7. 从原始对象克隆未修改的对象数据

  8. 对新的 temp 对象打零

  9. 恢复 xattrs,如果需要则恢复 omap

  10. 覆盖被修改的对象内容

  11. 删除原始对象

  12. 将新的 temp 对象移动并重命名为替换原始对象

  13. 完成恢复

由 Ceph 基金会带给您

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