ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

Ceph OSD 内部机制解析:last_epoch_started 与 PG Peering 的一致性保障

Ceph OSD 内部机制解析:last_epoch_started 与 PG Peering 的一致性保障 存储分布式文件系统对象存储后端高可用【免费下载链接】cephCeph is a distributed object, block, and file storage platform项目地址https://gitcode.com/gh_mirrors/ce/ceph点击查看免费下载last_epoch_started是 Ceph OSD 中一对容易被混淆却又至关重要的状态字段info.last_epoch_started与info.history.last_epoch_started。它们共同决定了 PGPlacement Group在 peering 过程中如何判断哪些写入是已提交的、哪些日志条目是发散的divergent从而在不丢失已确认写入的前提下完成主从选优与日志合并。本文以仓库文档 doc/dev/osd_internals/last_epoch_started.rst 为主体结合 src/osd/osd_types.h、src/osd/PeeringState.h 与 src/osd/PeeringState.cc 的源码实现从定义、更新时机、find_best_info中的应用到边界案例逐层展开。读完本文你将能准确解释les/c/f这类 peering 日志的含义理解为何唯一记录了最新last_epoch_started的 OSD可能引发 PG incomplete以及osd_find_best_info_ignore_history_les为何被标记为极度危险。一、背景epoch、interval 与 PG peering 中的激活在 Ceph 的 OSDMap 演进模型中每个epoch对应一次集群 map 变更。当某个 PG 的 acting set负责该 PG 的一组 OSD在一个 epoch 区间内保持不变时就称该 PG 处于同一个interval区间中。每当 acting/up set 发生变化PG 会经历一次新的 peering 流程选定权威日志authoritative log恢复数据一致性然后在新 interval 中重新进入 active 状态并开始接受客户端写入。last_epoch_started记录的就是该 PG 在哪个 epoch 开始接受写入。理解它有两条关键线索它有两个载体pg_info_t中的last_epoch_started本地视角与pg_history_t中的last_epoch_started全局历史视角代码注释中直接引用了本仓库文档它被 peering 过程反复用作上下界既用来划定已提交写入的上界也用来界定发散日志可能出现的回溯范围。二、两个 last_epoch_started定义与分工原文档开篇即给出二者的严格定义这是理解全部后续逻辑的基础。1.info.last_epoch_started本地激活区间的精确记录info.last_epoch_started记录区间i的激活 epoche使得所有在i或更早区间提交的写入都已反映在本地 info/log 中且i之后没有任何写入反映在本地 info/log 中。换句话说这个字段是本 OSD 最后一次作为该 PG 的 acting 成员、并在该区间接受并提交写入的精确记录。它保证了一个很强的性质由于已提交的写入永远不会是发散的committed write is never divergent即使我们从权威 OSD 收到一个last_epoch_started更旧的 log/info我们也可以保留自己的info.last_epoch_started不动——因为中间的任何区间都不可能再提交过写入。这一点在 src/osd/PeeringState.cc 的proc_master_log()第 3441 行起中得到落实主 OSD 处理来自对端的权威日志时不会因为对方的last_epoch_started较旧而回退本地已确认的启动记录。在源码中pg_info_t::last_epoch_started的定义注释为last epoch at which this pg started on this osd本 OSD 上该 PG 最后一次启动的 epoch见 src/osd/osd_types.h。2.info.history.last_epoch_startedPG 整体的启动下界info.history.last_epoch_started记录的是PG 作为一个整体最近一次进入 active 并接受写入的区间的下界lower bound。在某个特定 OSD 上它同时也是本地 PG 日志中出现写入的那些区间的激活 epoch 的上界我们在接受写入之前更新它。这段定义包含两层含义全局下界无论从哪个 OSD 看只要该 PG 曾作为一个整体在某个区间启动过其history.last_epoch_started至少不会小于那个区间的激活 epoch本地上界本 OSD 上任何被记入 PG 日志的写入其所在区间的激活 epoch 不可能大于本 OSD 记录的history.last_epoch_started——因为 OSD 必须先更新该字段然后才接受写入。在 src/osd/osd_types.h 中pg_history_t::last_epoch_started的注释精确对应lower bound on last epoch started (anywhere, not necessarily locally)PG 在任意位置、不一定是本地启动的最后一个 epoch 的下界并且该注释直接引用了本仓库文档的路径docs.ceph.com/docs/master/dev/osd_internals/last_epoch_started/说明这段设计意图就是由本文档正式确立的。此外pg_history_t还配有last_interval_startedlast_epoch_started对应区间的首个 epoch二者成对出现src/osd/osd_types.h在 peering 日志中以les/c与lis/c的形式输出见 src/osd/osd_types.h 的operator实现。三、核心推导为何最小 last_update 上界是安全的原文档用一段严谨的推导说明history.last_epoch_started如何支撑 peering 判定客户端已确认的写入最多到哪所有已提交的写入都由 acting set 的全部成员提交all committed writes are committed by all acting set OSDs因此任何非发散的写入都保证在它所在的区间内所有 acting 成员都记录下了当时的history.last_epoch_started一旦 peering 从每个区间回溯到某个已见过的history.last_epoch_started各查询到一个 OSD就可以推出在最大的history.last_epoch_started之后不可能有任何区间把写入报告为已提交因为 OSD 在记录客户端写入之前必定先记录该字段于是所有满足info.last_epoch_started MAX(history.last_epoch_started)的 info 中最小的last_update必然是报告给客户端的已提交写入的上界。这条推导是整个 peering 选主逻辑find_best_info、calculate_maxles_and_minlua的理论基石它保证了无论各 OSD 的日志如何分歧peering 都不会把客户端已经收到确认的写入判定为未提交从而避免已确认数据丢失。四、更新时机激活消息与持久化的先后info.last_epoch_started与history.last_epoch_started的更新时机并不相同原文档特别强调info.last_epoch_started在收到初始激活消息initial activation message时更新history.last_epoch_started则要等到新的info.last_epoch_started被持久化之后可能与该区间的第一条写入一同落盘才更新。这样做的目的是确保在 acting set 的所有 OSD 都记录下最新的info.last_epoch_started之前我们不需要任何一个携带最新info.last_epoch_started的 OSD 参与判定。也就是说写入路径上字段的落盘顺序是先持久化启动标记再接受客户端写入而 peering 路径上则是允许暂缺最新启动标记的 OSD直到全体成员都追平。这种不对称设计避免了某个 OSD 刚记录启动就崩溃导致 peering 必须依赖它才能推进的脆弱局面。五、find_best_info 中的使用max 与 min 的双重边界peering 在收集完所有副本的pg_info_t后会调用find_best_info()选出权威 info。该函数在 src/osd/PeeringState.h 中声明其关键辅助函数是calculate_maxles_and_minlua()src/osd/PeeringState.h用于计算max_last_epoch_started最大 last_epoch_started与min_last_update_acceptable可接受的最小 last_update。1.max_last_epoch_started_found避免误标发散在find_best_info中我们计算max_last_epoch_started_found时会纳入info.last_epoch_started的值因为我们不想把一个在更早区间本应非发散的日志条目标记为发散——它可能已经被用来服务过读请求。试想某条日志写入发生在区间i当时该 OSD 在 acting set 中此时它是完全有效的如果因为 peering 选出的权威日志较旧就把它划为发散并要求回滚就可能回滚掉一个曾返回给客户端的读结果。把last_epoch_started纳入上界计算就是为了让 peering 尊重历史区间中的合法写入。2.activate()用对端的 last_epoch_started 界定回滚范围在activate()中我们使用对端的last_epoch_started值作为发散日志条目最多能回溯多远的边界。也就是说发散检测不需要扫描整个日志只需要检查从权威头到last_epoch_started界定的范围内是否存在与权威日志冲突的条目超出该边界的条目在更早区间产生一律视为安全。3.min_last_epoch_started_foundincomplete 判定与边界案例原文档随后给出一个真实的边界场景来自calc_acting的调试输出calc_acting osd.0 1.4e( v 473302 (292200,473302] local-les473 n4 ec5 les/c 473/473 556/556/556 calc_acting osd.1 1.4e( v 473302 (293202,473302] lb 0//0//-1 local-les477 n0 ec5 les/c 473/473 556/556/556 calc_acting osd.4 1.4e( v 473302 (120121,473302] local-les473 n4 ec5 les/c 473/473 556/556/556 calc_acting osd.5 1.4e( empty local-les0 n0 ec5 les/c 473/473 556/556/556逐项解读这些字段对照 src/osd/osd_types.h 中pg_info_t的序列化与 dump 逻辑v 473302last_update版本epoch 473版本号 302(292200,473302]PG 日志范围log_tail,log_head]local-les473/local-les477各 OSD 本地记录的info.last_epoch_startedles/c 473/473history.last_epoch_started/history.last_epoch_clean556/556/556history.last_epoch_marked_full等历史字段对应les/c/f的f输出。场景要点在某个区间acting set 是 osd.0 与 osd.4二者local-les473但osd.1 记录了info.les477——它是该区间唯一记录下最新激活 epoch 的 OSDosd.4 在该区间重启过osd.0没有及时收到激活消息因此二者都没有记录 477若按所有 OSD 的last_epoch_started都参与min_last_epoch_started_found计算则由于只有 osd.1 拥有 477peering 会认为无法找到足够多的最新启动见证者从而把 PG 标记为incomplete——尽管 osd.4 或 osd.0 本来都是合法可用的选择。原文档给出的规避原则是计算min_last_epoch_started_found时不把处于 incomplete 状态的对端的info.les纳入考虑。它本来就不在 acting set 中所以如果maybe_went_rw为真我们必然能从这个区间找到另一个 OSD如果那个 OSD 都不记得这个info.les那么我们也不可能用它服务过读请求。这条原则的本质是只有那些确实参与过区间激活、并可能服务过读写的 OSD其last_epoch_started才有资格作为最小上界的证据。incomplete未完成 peering的 OSD 尚未进入激活状态不构成证据也不应成为 peering 的阻碍。这也呼应了find_best_info中restrict_to_up_acting、exclude_nonprimary_shards等参数见 src/osd/PeeringState.h对候选集的控制。六、危险开关osd_find_best_info_ignore_history_les理解了last_epoch_started在 peering 中的核心作用就能读懂一个特殊配置项为何被标注为极度危险。该选项定义于 src/common/options/osd.yaml.in- name: osd_find_best_info_ignore_history_les type: bool level: dev desc: ignore last_epoch_started value when peering AND PROBABLY LOSE DATA default: false其long_desc明确警告这是一个极度危险的选项只应在开发者的指示下使用。它让 peering 忽略last_epoch_started的值从而可能让 OSD 相信某个 OSD 对 PG 内容持有权威视图——即使该视图实际又旧又过时通常会导致数据丢失因为会把一个过时的 PG 当作最新状态。在 src/osd/PeeringState.h 中history_les_bound标志注释为need osd_find_best_info_ignore_history_les正是用于在find_best_info/calculate_maxles_and_minlua中临时关闭history.last_epoch_started作为下界约束的通道。默认false意味着正常集群始终依赖last_epoch_started保护已提交数据只有在调试历史遗留问题时开发者才会在明确知道风险的前提下临时打开它。这从反面印证了last_epoch_started不是可有可无的元数据而是防止已确认写入被误判、防数据丢失的最后一道防线。七、总结last_epoch_started的设计可以概括为三句话info.last_epoch_started是本地视角的精确记录——本 OSD 最后一次启动接受的写入区间已提交写入永不发散因此旧权威信息也不能回退它history.last_epoch_started是全局视角的保守下界——先持久化启动标记、再接受写入的顺序保证了最小last_update上界推导成立peering 因此能安全判定已确认写入的范围边界处理尊重见证者原则——只有真正参与过区间激活的 OSD 才有资格约束min_last_epoch_started_foundincomplete 对端不应阻塞 peering。当你在 OSD 日志中看到les/c 473/473、local-les477这类字段时现在你应该能够还原出背后的完整故事这是 Ceph 在分布式系统的部分故障现实下用一对状态字段加上严格的更新次序换取不丢已确认写入这一最强一致性承诺的具体实现。想继续深入可以按图索骥阅读 src/osd/PeeringState.cc 中的proc_master_log()、find_best_info()与activate()以及 doc/dev/osd_internals 目录下其他 peering 相关文档。赞分享存储分布式文件系统对象存储后端高可用【免费下载链接】cephCeph is a distributed object, block, and file storage platform项目地址https://gitcode.com/gh_mirrors/ce/ceph点击查看免费下载相关推荐Ceph OSD 内部机制解析基于日志的 PGLog-Based PG复制与 PGBackend 统一架构Ceph OSD 内部机制解析基于日志的 PGLog Based PG复制与 PGBackend 统一架构 本篇文章聚焦 Ceph 分布式存储中 OSD存储分布式文件系统对象存储后端高可用Ceph PG 状态机与 Peering Interval 深入解析从 start_peering_interval 到 flushed 机制Ceph PG 状态机与 Peering Interval 深入解析从 start_peering_interval 到 flushed 机制 本文以 doc存储分布式文件系统对象存储后端高可用Ceph OSD 内部机制PastIntervals 与 OSDMap Trimming 深度解析Ceph OSD 内部机制PastIntervals 与 OSDMap Trimming 深度解析 导读 PastIntervals历史区间是 Ceph存储分布式文件系统对象存储后端高可用创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表