首页
/ RustFS 后台服务清点指南:On-Demand Migration 后台 Worker 的期望来源、状态表面与副作用审计

RustFS 后台服务清点指南:On-Demand Migration 后台 Worker 的期望来源、状态表面与副作用审计

2026-09-09 12:08:55作者:谭伦延

本文以 RustFS 仓库中的 docs/architecture/background-services-inventory.md 为骨架,围绕 On-Demand Migration(按需迁移,ODM)模块的后台 Worker 展开。它回答一个具体问题:当你要新增、移动或审计一个后台 Worker 时,如何在"一行记录"内说清楚它的期望来源(desired source)、当前状态输入(current-status inputs)、状态表面(status surface)和副作用(side effects)。读完本文,你将掌握 RustFS 后台服务清点清单的规则、ODM 三大后台服务的逐行剖析,以及如何用契约词汇表为其他服务补全清点条目。

清点清单的定位:它回答什么问题

background-services-inventory.md 的定位非常明确:它是按服务维护的行级清单,而 background-controller-contract.md定义词汇表与状态模型的契约页。二者分工如下:

  • 契约页拥有词汇表(desired / current / status / reconcile / side effects)和状态模型,并给出各服务的耦合注意事项;
  • 本清单页拥有每个服务的具体行数据——即契约词汇表所定义的那五个维度,针对每个被审计的服务各填一行。

文档强调了一个容易踩坑的边界:Scope 只覆盖"已被审计"的服务。目前清单只收录了 On-Demand Migration 的 Worker(对应 rustfs/backlog#2147);scanner、heal、lifecycle、replication、notification、capacity、config-reload 等服务目前只在契约页的 "Coupling Notes" 中描述,尚未进入逐行清点。

这里有一条非常重要的默认规则:"清单中没有该服务的行"只表示"尚未被清点",绝不等于"该服务没有副作用"。审计一个服务之前,你无法从"没有行"推断出"没有副作用"。

词汇表:五个维度的含义

契约页定义了清点行所依托的五个术语。理解它们,才能读懂清单中每一列:

术语 含义 边界
Desired(期望) 来自环境变量、持久化配置、模块开关、feature flag、bucket 配置或 admin 配置的静态意图 只读;收集期望状态时绝不规范化或改写配置
Current(当前) 观察到的本地运行时状态:configured、disabled、running、degraded、stopping、unknown 只读;绝不通过会产生存储或网络副作用的探针去推断
Status(状态) 可机器检查的快照:计数器、worker 数、队列压力、上次周期、上次错误、取消来源、关闭句柄形态 无副作用;表面缺失时上报为 unknown,绝不猜测
Reconcile(调和) 对 desired、current、status 的比较,产出一个计划 已发布的计划只做报告;唯一允许的 worker 变更是 none
Side effects(副作用) 写操作、删除、队列准入、目标激活、外部 I/O、指标上报、就绪发布、对等信号、配置重载扇出 任何 controller 接触服务之前必须先声明

状态模型方面,快照使用代码能证明的最窄状态:NotConfigured(无有效期望来源)、Disabled(存在来源但显式禁用)、StartingRunning(按现有运行时状态确实在运行,而非仅仅配置为启用)、DegradedStoppingStoppedUnknown(无安全状态表面,优先于猜测)。

清点清单的硬性规则

清单页明确指出,每一行都必须遵守从契约页继承的规则:

  1. 状态收集是无副作用的:状态收集绝不启动、停止、缩放或唤醒某个 worker,绝不写入存储数据、对象元数据、目标状态、队列条目或持久化配置,绝不发布就绪或对等重载信号;
  2. 缺失表面上报为 unknown,而不是猜测
  3. 副作用在 controller 接触服务之前声明——先声明,后操作;
  4. 取消来源与关闭句柄形态,要和期望的 enabled/disabled 状态分开报告;
  5. 对同一快照重复调用 reconcile 必须返回同一计划;
  6. scanner、heal、lifecycle、replication 的状态不得隐藏其队列与准入耦合。

On-Demand Migration:三个后台服务逐行剖析

清单当前收录了 On-Demand Migration 的三个后台服务。ODM 的整体行为、配置与排障在 docs/operations/on-demand-migration.md 中描述,这里只做逐行的清点式记录。

Write-back pull pipeline(写回拉取管线)

这是 ODM 的核心 Worker:负责把源对象拉下来并写成本地对象。

维度 内容
期望来源 桶的 on-demand-migration.json 配置(enabledpolicy.max_concurrent_pullspull_queue_capacitymultipart_part_size_bytesbandwidth_limit_bytes_per_sec),叠加进程级开关 RUSTFS_ON_DEMAND_MIGRATION_ENABLED
当前状态输入 rustfs/src/on_demand_migration/sys.rs 中的每桶运行时状态:该桶的状态是否已安装、其 source client 是否构建成功、取消令牌(cancellation token)、队列深度、在途拉取许可(in-flight pull permits)
状态表面 GET /rustfs/admin/v3/on-demand-migration/{bucket}/status(返回 inflight_pullsqueue_depthcounters.pulled_*counters.pull_failures_total)以及 rustfs_on_demand_migration_* 指标系列
副作用 对源发起 GET / HEAD / GetObjectTagging 流量;通过内部 put 路径执行本地对象写入,因此会消耗桶配额、套用桶默认 SSE、版本控制、Object Lock 默认保留,并触发 ObjectCreated 通知和出站复制调度

源码佐证:实现位于 rustfs/src/on_demand_migration/pull.rs,本地写入被委托给应用层的 OdmWriteBack trait 对象(实现位于 rustfs/src/app/object/on_demand_migration_put.rs),由二进制在启动时通过 OnDemandMigrationSys::set_write_back 注入。从源码结构看,pull.rs 定义了 PullQueue(有界队列,容量即 pull_queue_capacity)、dispatch(dispatcher 循环)、commit_inline(inline 提交)和 spawn_pump(泵任务)。几个硬编码常量直接对应文档中的行为描述:PULL_MAX_RETRIES = 3PULL_RETRY_BASE_DELAYS = [1s, 4s, 16s]MAX_MULTIPART_PARTS = 10_000

Backfill job(回填任务)

只做读穿(read-through)时,只有客户端实际读过的对象会被迁移。回填任务则遍历源桶的 listing,把其余对象全部通过写回管线拉取到本地。

维度 内容
期望来源 一次 admin start 请求加上桶配置;当配置的 updated_at 变化或配置被删除时,任务即失效
当前状态输入 桶元数据前缀下持久化的 checkpoint(.rustfs.sys/buckets/<bucket>/on-demand-migration-backfill.json)、其 state 字段、owner lease
状态表面 桶状态端点中的 backfill 段,以及 rustfs_on_demand_migration_backfill_* 指标系列
副作用 对源发起 ListObjectsV2 分页;向写回管线做队列准入(因此继承写回管线的全部副作用);checkpoint 写入

源码佐证:实现在 rustfs/src/on_demand_migration/backfill.rs(对应 rustfs/backlog#2159)。checkpoint 相关的常量非常具体:BACKFILL_CHECKPOINT_FILE = "on-demand-migration-backfill.json"BACKFILL_LEASE = 60s(每次保存续约)、BACKFILL_SAVE_INTERVAL = 10sBACKFILL_SAVE_EVERY_KEYS = 1000BACKFILL_LIST_PAGE_SIZE = 1000。checkpoint 通过 If-Match 比较并交换(CAS)写入,保证并发取消或接管不会被覆盖;continuation_token 只有在某页排队的拉取全部成功后才前移,失败后停留在失败页,崩溃恢复不会跳过失败的拉取(已存在的键会被跳过)。BackfillState 枚举包含 pendingrunningpausedcancelledcompletedcompleted_with_failuresfailed 七种状态。另外,失败键只以 xxh3 哈希记录(key_hash),对象键只出现在 trace 级日志中。

Backfill recovery loop(回填恢复循环)

负责在节点重启或 owner lease 过期后接管未完成的回填任务。

维度 内容
期望来源 持久化 checkpoints 中所有 state = running 的集合;在每个节点上运行
当前状态输入 checkpoint owner lease 是否过期
状态表面 接管通过同一 backfill status 段报告;一次接管会发出一个 warn 级 lease 事件
副作用 认领 lease 并恢复回填任务(继承其副作用);扫描 checkpoints 本身是只读的

源码佐证:注册点位于 rustfs/src/startup_background.rsinit_on_demand_migration_backfill_runtime,它构建 SysBackfillContextsBackfillRunner::for_local_node,并通过 install_global_backfill_runner 发布进程级 runner;恢复循环由 spawn_backfill_recovery_loop 挂到存储层的后台取消令牌上。backfill.rs 中的 run_backfill_recovery_loopBACKFILL_RECOVERY_INTERVAL = 60s 为周期扫描,若有接管被推迟(锁忙或无状态)则缩短到 5s 重试;takeover_due 判定接管条件:lease 已过期、owner 为本节点(重启后必然已死),或完全无 owner。开始与接管通过命名空间锁 odm-backfill/<bucket> 在集群范围内串行化。

关键设计:pull pipeline 没有自己的独立循环

清单文档特别强调了一个容易被误解的设计事实:拉取管线本身没有独立的循环。这与回填恢复循环不同——恢复循环是一个真正的周期性循环,而拉取管线是事件驱动的:

  • 桶的队列 dispatcher 是惰性启动的:在第一个后台拉取到来时才启动,当桶状态被重建或移除时取消;
  • 每个 inline pull 在一个超出请求生命周期的任务中提交:客户端断开连接不会截断已存储的对象。这是因为 commit_inline 在独立任务中持续从源 draining 并写入本地,客户端中断不影响写回;
  • Worker 不会重新读取开关或配置:无论是 RUSTFS_ON_DEMAND_MIGRATION_ENABLED 开关还是桶配置,都不是由 Worker 周期性地去轮询的。唯一的期望状态路径是 bucket-metadata publish hook 重建状态——配置一变,publish hook 就触发状态重建,旧的 dispatcher 被取消,新的状态携带新配置生效。

源码佐证sys.rsOnDemandMigrationSys 的生命周期跟随桶元数据缓存,通过 BUCKET_CONFIG_PUBLISH_HOOK 注册的发布钩子驱动;钩子在每条缓存安装路径(初始加载、admin 更新、对等重载、刷新循环、惰性加载)上都会触发。变更检测按配置值(PartialEq)比较而非只比较 updated_at,且桶的 incarnation 也参与比较——即使配置完全相同,重建桶也必须取消旧工作。由于客户端构建是异步的(TLS 材料可能从磁盘读取),publish 同步移除旧状态并 spawn apply 执行安装,用每调用一代号(generation number)保证慢的旧安装永远不会覆盖新安装。

状态表面与观测:从端点字段到指标系列

虽然清单行只概括了状态表面,但落到实际观测时,GET /rustfs/admin/v3/on-demand-migration/{bucket}/status 的返回结构(实现于 rustfs/src/admin/handlers/on_demand_migration.rs)可以拆解为与清单各维度一一对应:

  • 配置视图configured(是否有配置)、enabled(配置自身的开关)、module_enabled(进程级开关)、providerendpoint_hostupdated_at
  • 运行时快照breaker.statecountersrequests_totalpulled_bytes_totalpulled_objects_totalpull_failures_totalsource_latency)、last_source_errorclassat)、inflight_pullsqueue_depthserved_by_source_ratiobackfill

注意两个恒为 null 的字段并非 bug:breaker.opened_at(运行时持有的是单调时钟而不是墙钟)和 served_by_source_ratio(没有按桶划分的 GET 总数可供求比值,诚实的 null 好过编造的 0)。当桶在本节点没有活动状态时(模块关闭或尚无任何读取),运行时字段为 null,而 providerendpoint_host 仍反映已保存的配置。

指标侧,所有序列都按桶打上 rustfs_on_demand_migration_* 前缀,且只在配置了源的桶上出现:requests_total(按 bucketopoutcome 打标)、pulled_bytes_totalpulled_objects_total(按 path 区分 inline / background / backfill)、pull_failures_total(按 reason 区分 source_not_foundqueue_fulletag_mismatchquota 等)、inflight_pullsqueue_depthsource_latency_seconds_distributionbreaker_state(0=closed、1=half-open、2=open)。回填任务另有 rustfs_on_demand_migration_backfill_* 系列。

如何为其他后台服务补全清点行

清单文档的核心使用场景是"新增、移动或审计后台 Worker"。以现有三行为模板,为其他服务补行的步骤如下(每一步都可以用契约页的 Coupling Notes 验证):

  1. 确定 Desired source:该服务的期望来自哪里?环境变量、持久化配置、模块开关、feature flag、bucket 配置还是 admin 请求?注意配置的哪个字段变化会使期望失效(如 backfill 的 updated_at);
  2. 确定 Current-status inputs:本节点上哪些运行时状态能证明"它现在在做什么"?状态是否安装、客户端是否构建、令牌是否有效、队列多深、在途多少;
  3. 定义 Status surface:状态端点哪个段、哪些指标系列承载可机器检查的快照?记住规则——无副作用、缺失报 unknown
  4. 声明 Side effects:在 controller 接触它之前,把所有写操作、队列准入、外部 I/O、通知与复制调度列全。

契约页的 Coupling Notes 已经给出了待清点服务的关键事实来源,例如:scanner 由 init_data_scannerrustfs/src/startup_lifecycle.rs)启动并隐含触发 heal 入队;heal/AHM 拥有自己的取消令牌(rustfs/src/startup_background.rscreate_ahm_services_cancel_token);replication 有"关通道停 worker"与"取消令牌停 worker"两套关闭契约;lifecycle 过期/迁移/过期分片清理由 ECStore::init 绑定运行时令牌;动态配置重载是 admin 触发的扇出而非循环。这些描述正是未来为这些服务写清点行时"期望来源与副作用"两列的原料。

后续阅读

登录后查看全文
热门项目推荐
相关项目推荐

项目优选

收起
kernelkernel
deepin linux kernel
C
33
18
docsdocs
暂无描述
Markdown
900
5.83 K
ops-transformerops-transformer
本项目是CANN提供的transformer类大模型算子库,实现网络在NPU上加速计算。
C++
1.14 K
2.75 K
pytorchpytorch
作为 Ascend for PyTorch 社区的核心组件,TorchNPU 是昇腾专为 PyTorch 打造的深度学习适配插件,使 PyTorch 框架能够直接调用昇腾 NPU,为开发者提供昇腾 AI 处理器的超强算力。
Python
860
1.35 K
ops-nnops-nn
本项目是CANN提供的神经网络类计算算子库,实现网络在NPU上加速计算。
C++
927
1.85 K
jiuwenswarmjiuwenswarm
JiuwenSwarm 是一款基于openJiuwen开发的智能AI Agent,它能够将大语言模型的强大能力,通过你日常使用的各类通讯应用,直接延伸至你的指尖。
Python
3.84 K
1.02 K
kernelkernel
openEuler内核是openEuler操作系统的核心,既是系统性能与稳定性的基石,也是连接处理器、设备与服务的桥梁。
C
533
603
ops-mathops-math
本项目是CANN提供的数学类基础计算算子库,实现网络在NPU上加速计算。
C++
1.37 K
1.46 K
AscendNPU-IRAscendNPU-IR
AscendNPU-IR是基于MLIR(Multi-Level Intermediate Representation)构建的,面向昇腾亲和算子编译时使用的中间表示,提供昇腾完备表达能力,通过编译优化提升昇腾AI处理器计算效率,支持通过生态框架使能昇腾AI处理器与深度调优
C++
548
397
cann-learning-hubcann-learning-hub
CANN 学习中心仓,支持在线互动运行、边学边练,提供教程、示例与优化方案,一站式助力昇腾开发者快速上手。
Jupyter Notebook
1.04 K
525