RocksDB 远程压缩并发 MANIFEST 轮转导致的打开失败:OpenAndCompact 重试机制解析
RocksDB 远程压缩并发 MANIFEST 轮转导致的打开失败OpenAndCompact 重试机制解析【免费下载链接】rocksdbA library that provides an embeddable, persistent key-value store for fast storage.项目地址: https://gitcode.com/gh_mirrors/ro/rocksdb导读本文讲解 RocksDB 远程压缩CompactionService/DB::OpenAndCompact在 worker 以只读方式打开 primary 目录时因 primary 并发轮转CURRENT/MANIFEST 文件而偶发失败的问题以及 RocksDB 为此引入的“secondary 打开有限次重试”修复方案。读完本文你将理解该竞态产生的文件系统语义背景、Status::TryAgain/PathNotFound/NotFound三类瞬态错误的含义、OpenAndCompactOptions::max_secondary_open_retries参数的正确用法以及这一修复与remote_compaction_manifest_floor加固机制的关系。本文以 unreleased_history/bug_fixes/remote_compaction_secondary_open_retry.md 为骨架并结合 db/db_impl/db_impl_secondary.cc 与 include/rocksdb/options.h 等源码展开。一、问题背景远程压缩如何打开 primary 目录1.1 远程压缩的基本流程RocksDB 的CompactionService允许把压缩任务卸载到另一台主机或另一个进程执行从而把 primary 上的后台负载转移出去。核心入口是DB::OpenAndCompact()它“以只读方式打开数据库并执行压缩且不修改原始 DB”压缩结果输出到源数据库目录下的子目录而非安装回原 LSM 树。相关声明见 include/rocksdb/db.h。从 db/db_impl/db_impl_secondary.cc 的实现看OpenAndCompact()的执行分为几个明确步骤反序列化压缩输入CompactionServiceInput::Read()从input中恢复 primary 序列化好的压缩任务信息目标 CF、输入文件等加载选项通过LoadOptionsFromFile()读取 primary 的 OPTIONS 文件OptionsFileName(name, compaction_input.options_file_number)再以CompactionServiceOptionsOverride覆盖相关配置确定输出目录RemoteCompactionJobDir()依据use_session_tmp_dir_for_remote_compaction决定输出路径是name/session_tmp/output_directory还是name/output_directory过滤列族只打开 default CF 与目标 CF因为远程压缩只需 MANIFEST 中的 LSM 状态无需 WAL 重放以 secondary 方式打开 primary 目录跳过 WAL 恢复随后在该视图上执行压缩。1.2 secondary 打开与 MANIFEST 跟踪关键在第 5 步worker 调用DBImplSecondary::OpenAsSecondaryImpl()打开primary 的实时目录只读。secondary 依赖ReactiveVersionSet跟踪 primary 的 MANIFEST 演进其中MaybeSwitchManifest()见 db/version_set.cc负责在发现CURRENT指向新 MANIFEST 时切换文件句柄。二、竞态根源无 read-after-unlink 语义的文件系统2.1 primary 如何轮转 MANIFEST每次 MANIFEST 轮转primary 都会写出新的 MANIFEST 文件把新写入的CURRENT文件rename 覆盖到旧的CURRENT之上完成指针切换视清理策略删除旧 MANIFEST。对运行中的 secondary 而言它可能正处于“读CURRENT→ 打开对应 MANIFEST”的窗口期内于是出现两种经典竞态读取CURRENT时拿到了旧值随后旧 MANIFEST 被 primary 删除再打开文件即失败读取CURRENT后 primary 立刻切换旧 MANIFEST 被删除打开时同样失败。2.2 POSIX 与远程/对象存储的语义差异在 POSIX 文件系统上“打开之后删除/替换”是安全的已打开的文件描述符仍可继续读取未打开但刚被 rename 覆盖的文件路径在删除前也有一段可用窗口这就是 “read-after-unlink” 语义。但远程/对象存储remote/object stores不具备该语义被替换的对象会立即被报告为“不存在”。于是 secondary 打开时得到的不再是“文件不可读但进程可继续”而是明确的错误状态状态触发场景Status::PathNotFound打开CURRENT或 MANIFEST 时目标对象已不存在文件系统层的PathNotFoundStatus::NotFound同样的“对象已消失”在另一条路径上的等价表现Status::TryAgainMANIFEST 层面的专门映射见下文MaybeSwitchManifest()MaybeSwitchManifest()中专门做了这种映射当FileExists返回IsNotFound或打开 MANIFEST 返回IsPathNotFound时统一构造Status::TryAgain(The primary may have switched to a new MANIFEST and deleted the old one.)见 db/version_set.cc把“primary 可能刚切换 MANIFEST 并删除了旧文件”这一瞬态竞态显式暴露出来。2.3 为什么这是瞬态而不是损坏关键在于这只是读取时机撞上了轮转窗口并不是数据库损坏。下一次尝试读取时CURRENT已指向新 MANIFEST打开即可成功。因此正确做法不是报错而是在有限次数内重试。三、修复方案max_secondary_open_retries有限重试3.1 参数定义修复为OpenAndCompactOptions新增了max_secondary_open_retries字段声明位于 include/rocksdb/options.hstruct OpenAndCompactOptions { // Allows cancellation of an in-progress compaction. std::atomicbool* canceled nullptr; // Maximum number of times to retry opening the source DB as a secondary // after the initial attempt fails because CURRENT or MANIFEST was replaced // concurrently. A value of zero disables retries. // // Default: 2 uint32_t max_secondary_open_retries 2; // ... allow_resumption 等其余字段 };参数语义默认值为 2即初始尝试之外最多再重试 2 次共最多 3 次尝试设为 0 表示完全禁用重试保持旧行为一次失败即返回仅对瞬态的打开失败生效见下方状态过滤逻辑。3.2 重试逻辑的源码实现重试循环位于 db/db_impl/db_impl_secondary.ccfor (uint32_t retry_count 0;; retry_count) { s DBImplSecondary::OpenAsSecondaryImpl( db_options, name, output_path, column_families, handles, db, /*recover_wal*/false, /*trust_manifest_recovery*/floor_provided); if (s.ok() || !(s.IsTryAgain() || s.IsPathNotFound() || s.IsNotFound()) || retry_count options.max_secondary_open_retries) { break; } ROCKS_LOG_WARN(db_options.info_log, OpenAndCompact: secondary open of %s failed with %s (retry % PRIu32 of % PRIu32 ); the primary may have replaced CURRENT/MANIFEST concurrently, retrying, name.c_str(), s.ToString().c_str(), retry_count 1, options.max_secondary_open_retries); }逻辑要点每次循环调用OpenAsSecondaryImpl()执行一次 secondary 打开跳过 WAL 恢复因为远程压缩只需要 MANIFEST 里的 LSM 状态只有Status::TryAgain、Status::PathNotFound、Status::NotFound三类瞬态状态才会触发重试其他错误如损坏、权限问题、配置错误立即返回不会被重试掩盖重试次数达到max_secondary_open_retries后停止把最后一次的错误状态返回给调用方每次重试前输出一条ROCKS_LOG_WARN级别的日志包含失败状态与“第几次/共几次重试”信息便于在线上定位此类竞态发生的频率。注意循环条件中retry_count options.max_secondary_open_retries时也会 break配合循环后对s.ok()的判断保证重试次数严格有界。3.3 配套计时统计打开过程整体耗时被记录到OPEN_AND_COMPACT_DB_OPEN_MICROS直方图db/db_impl/db_impl_secondary.cc对应 include/rocksdb/statistics.h 中“Time spent opening the secondary DB insideDB::OpenAndCompact()”的说明。运维者可通过该指标观察重试对打开耗时的实际影响。四、不重试的代价primary 后台错误4.1 失败如何传导回 primary该修复之所以重要是因为对不回退到本地压缩的CompactionService实现而言worker 侧的任何失败都会成为primary 侧的后台错误background error。也就是说一次纯粹由文件系统时序造成的瞬态失败可能被放大为 primary 上的持续错误状态进而触发后续的错误处理路径如 stop writes。4.2 回退语义由集成方决定CompactionService::Start()的返回值CompactionServiceJobStatus控制失败后的行为。测试实现给出了两种典型选择见 db/compaction/compaction_service_test.cc返回kUseLocal任务回退到 primary 本地执行是“有本地兜底”的集成方式返回kFailure不本地兜底失败直接暴露为后台错误这是压力测试工具DbStressCompactionService的刻意选择。对于后一种集成方式max_secondary_open_retries的重试就是避免无谓后台错误的关键防线。五、与 MANIFEST floor 加固的关系5.1 两个不同层面的保护本修复与既有的remote_compaction_manifest_floor机制include/rocksdb/options.h互为补充解决的问题层面不同机制解决的问题失败处理remote_compaction_manifest_floor默认 trueprimary 调度压缩时把当时的 MANIFEST 位置文件号与大小随请求下发worker 若发现自己基于更旧的MANIFEST 视图如最终一致文件系统返回了过期CURRENT、或 MANIFEST 被截断重建压缩则拒绝执行回退到本地压缩避免基于错误的 LSM 形状产出错误结果max_secondary_open_retries默认 2打开瞬间撞上CURRENT/MANIFEST 替换的瞬态竞态有限次重试后成功继续重试耗尽才返回错误floor 参数还隐含开启了 worker 侧的 “trust the MANIFEST” 恢复模式floor_provided传给OpenAsSecondaryImpl的trust_manifest_recovery见 db/db_impl/db_impl_secondary.cc避免因某个并非压缩输入文件的文件瞬时不可用而失败整个任务。5.2 本修复的定位本文描述的重试逻辑处理的是floor 机制之前的打开阶段先要成功打开并建立 secondary 视图依赖ReactiveVersionSet的MaybeSwitchManifest之后才谈得上 floor 检查。因此两者是同一链路中不同节点的加固重试消化“打开时撞上轮转”的瞬时窗口floor 拒绝“基于过期视图压缩”的正确性风险。六、集成与验证方式6.1 如何设置该参数worker 侧调用DB::OpenAndCompact()时传入自定义的OpenAndCompactOptionsOpenAndCompactOptions options; options.max_secondary_open_retries 2; // 默认值可显式指定 // options.canceled canceled_flag; // 可选支持任务取消 DB::OpenAndCompact(options, db_path, scheduled_job_id, compaction_input, result, options_override);若希望关闭重试以复现/调试竞态可显式设为 0。6.2 测试覆盖测试侧已在 db/compaction/compaction_service_test.cc 中覆盖该参数测试压缩服务把max_secondary_open_retries_传入OpenAndCompactOptions见 db/compaction/compaction_service_test.cc并提供接口调整重试次数max_secondary_open_retries_字段定义见同文件 db/compaction/compaction_service_test.cc默认值与OpenAndCompactOptions一致。通过构造 concurrent CURRENT/MANIFEST 替换的时序利用TEST_SYNC_POINT如DBImplSecondary::OpenAndCompact::BeforeLoadingOptions:0/1、AfterOpenAsSecondary:0等同步点可验证重试后成功与重试耗尽后返回错误的两种路径。相关测试入口还包括 db/db_secondary_test.cc 与 db/compaction/compaction_job_test.cc均引用了OpenAndCompact可用于对照 secondary 打开与压缩任务行为。七、小结与运维建议根因OpenAndCompact()以只读方式打开 primary 实时目录时primary 通过 rename 覆盖CURRENT轮转 MANIFEST在无 read-after-unlink 语义的远程/对象存储上被替换对象立即报告为不存在表现为PathNotFound/NotFound/TryAgain。修复OpenAndCompactOptions::max_secondary_open_retries默认 2对这三类瞬态状态做有界重试避免并发轮转导致压缩失败进而演变为 primary 后台错误。运维建议线上若使用对象存储/远程文件系统承载 RocksDB 数据目录并启用CompactionService遇到TryAgain/PathNotFound类的偶发打开失败属正常竞态可关注OPEN_AND_COMPACT_DB_OPEN_MICROS指标与 INFO/WARN 日志中的 “retry N of M” 信息确认重试生效如竞态频率过高可评估适当增大max_secondary_open_retries但需权衡每次重试的开销。【免费下载链接】rocksdbA library that provides an embeddable, persistent key-value store for fast storage.项目地址: https://gitcode.com/gh_mirrors/ro/rocksdb创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
上一篇/下一篇内容由系统自动关联
返回资讯列表 →