news 2026/9/10 1:54:55

RustFS Scanner 数据用量发布权威性决策:配额准入如何获得可用的权威依据

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
RustFS Scanner 数据用量发布权威性决策:配额准入如何获得可用的权威依据

RustFS Scanner 数据用量发布权威性决策:配额准入如何获得可用的权威依据

【免费下载链接】rustfs🚀2.3x faster than MinIO for 4KB object payloads. RustFS is an open-source, S3-compatible high-performance object storage system supporting migration and coexistence with other S3-compatible platforms such as MinIO and Ceph.项目地址: https://gitcode.com/GitHub_Trending/rus/rustfs

本决策文档(ADR)记录了 RustFS 在2026-09-03做出的一项关键架构结论:Scanner 后台扫描产生的数据用量(data usage)是硬配额(hard quota)准入的唯一权威数据源,为此必须保留完整的发布协议与证明层(fence),而不是在扫描滞后时用 best-effort 数据将就。阅读本文后,你将理解配额写路径为何需要"可用性故事"、七大围栏各自的排除对象、八个持久化对象各自的属主与生命周期,以及升级窗口期内 quota 如何降级到用量底线(usage floor)并最终收敛回完整发布。

决策概述:保留 Scanner 发布协议作为配额权威

scanner-usage-authority-decision.md给出的是结论性决策记录,其完整论证与协议细节落在配套契约文档 scanner-usage-publication.md 中,两者共同构成该主题的"决策 + 契约"体系:

RustFS keeps scanner data usage as authoritative for quota admission.

该决策从 backlog 决策记录中选定了选项 A:在配额准入消费 scanner 用量期间,scanner 发布协议保持必要。决策明确把以下机制从"可删除的兼容性杂物"重新定性为协议不变式(protocol invariants)

  • 周期纪元(cycle epoch);
  • 发布 CAS(publication CAS);
  • 数据迁移围栏(data-movement fence);
  • 分层注册表围栏(tier-registry fence);
  • 观察快照层(observed snapshot layer);
  • 持久化用量底线(persisted usage floor)。

换句话说,这些证明层不是历史包袱,而是"让分布式后台扫描的结果可以被配额决策信任"的机制本身。删除任何一个,都需要先证明有替代机制能排除同样的陈旧输入。

为什么配额准入不能接受 best-effort 用量

决策理由(Rationale)直指问题本质:配额是写路径上的准入决策(write-path admission decision)

如果配额以 best-effort 的 scanner 数据为准,那么扫描临时滞后(scanner lag)就会直接转化为欠强制(under-enforcement)——扫描还没看到的新写入不会被计入用量,配额形同虚设。因此设计上不能靠删掉证明层来简化,反而需要一个权威用量的可用性故事(availability story)

可用性故事的主角是scanner usage floor(用量底线):当完整的权威快照不可用时(冷启动、升级恢复、不完整周期的修复期间),配额准入使用这个下界值。观察快照(observed snapshot)仍然服务于管理和可观测性,但永远不成为配额权威

这一判断与 QuotaChecker 的实现 完全一致:get_real_time_usage的查找顺序是——内存权威用量 → 持久化用量底线 → 失败关闭,后两级正是决策文档中"可用性故事"的落地。

发布所有权模型:三个身份缺一不可

一个 scanner 周期只有在同时满足两个条件后才拥有权威发布的资格:持有集群 scanner 领导权声明(leadership claim),并证明存储发布纪元(storage publication epoch)没有移动

  • 领导权声明持久化在 scanner 周期状态(cycle state)中,源码对应 leadership.rs 中的 reconcile_scanner_leadership_claim,它读取并对比.bloomcycle.bin的持久化修订;
  • 数据用量发布的准入权归ECStore所有,因为只有它知道 rebalance、decommission 等数据迁移操作是否改变了 scanner 结果允许描述的 generation。

Scanner 可以无发布所有权地计算用量,但绝不能把结果变成权威的、配额可见的状态。一次完整发布因此具有三个身份(identity):

  1. 拥有该周期的scanner leader epoch
  2. 围栏数据迁移的storage publication epoch
  3. 被替换用量对象上的per-object CAS revision

任何一个身份在提交前发生变化,结果就只能作为重试或观察候选,而不是权威基线。

PUT 扇出(fanout)的在途保护

普通 PUT rename 扇出同样追踪实例级在途工作(in-flight work):quorum ACK 并不释放它——真正的磁盘任务保留所有权直到 rename 结束,即使请求调用方已被取消。关键设计取舍是:

  • 扫描准入保持仅限迁移(movement-only),持续的 PUT 不会停止命名空间遍历和 scanner 驱动的生命周期发现;
  • 遍历后的本地发布检查和**远程发布租约(publication lease)**拒绝尚未结束的扇出;
  • begin/end 命名空间 generation使跨扇出的扫描与缓存计划失效;协调者在获取远程租约后、发布权威聚合前,会重查完整活动摘要(activity digest),以捕获"扫描最后一次探测与租约获取之间才结束的尾部"。

该机制不增加命名空间或迁移锁。已通过验证的旧快照仍可能先于新开始的写入;持续或停滞的 PUT 尾部会延迟权威用量发布,延迟通过现有重试调度恢复,而非新增即时唤醒协议。这个 PUT 尾部保护要求所有 writer 节点完成升级,且不证明失败尾部副本已自愈,也不把在途追踪扩展到 multipart 等其它命名空间变更路径。

围栏体系:七道独立证明

协议使用独立的围栏(fence),因为它们排除的是不同的陈旧输入。除非替代方案能证明同样的排除效果,否则不得合并:

围栏属主排除对象
Scanner 领导权声明scanner竞争中的 scanner leader 与陈旧周期写入者
存储发布纪元ECStore跨 rebalance、decommission 或其它数据迁移 generation 计算出的用量
发布租约经 ECStore 面活动探针的 scanner 对端尚未确认候选的远程脏用量或维护状态
CAS 修订底层配置对象存储对用量快照、scanner 缓存或周期状态对象的丢失更新
Per-set 新鲜度scanner 聚合混入陈旧与当前 set 结果的合并用量快照
分层注册表 generationscanner 分层核算依据不同暖分层注册表分类的字节数
用量底线身份scanner 发布与 ECStore 配额回退空值或遗留值变成看似可信的权威配额输入

无法为自身读取面证明所需围栏的读者必须**失败关闭(fail closed)**或走文档化的观察路径,绝不允许为缺失或损坏的权威对象合成空用量快照。

缓存执行身份:防止慢扫描覆盖新结果

结构扫描计划摘要(structural scan-plan digest)在普通 bucket 写入下可以保持稳定,因此有范围扫描可以保留未受影响的基线 bucket。但它不足以证明同一周期内可复用已完成的结果。Bucket 工作使用结合了结构计划与完整活动快照的执行摘要(execution digest),bucket 的脏 generation 纳入其缓存身份;已完成的 set 缓存与结构计划分开携带同一执行摘要。持久化 set-root 快速路径要求执行身份相等,并叠加既有的 source、cycle、leader、tier 与缓存结构检查。

Set 扫描还会捕获其起始缓存修订(starting cache revisions):当持久化执行结果不同、需要替换时,这些修订必须保持不变,否则慢扫描可能覆盖更新的已完成结果。既有缓存锁、条件保存与迁移准入仍然围栏提交。

实现层面,可选字段scan_execution_digest追加到 map 编码的缓存元数据中。遗留缓存仍可读,但缺少该身份就无法满足同周期 set-root 复用;旧版读者可以忽略新增的 map 键,但旧版写入者不执行该围栏——可读性不构成混合版本发布安全的保证。

持久化对象契约:每个对象都有唯一属主

持久化对象是兼容性契约的一部分,移除任何一个都需要兼容窗口与专门的清理条目(cleanup entry)。下表对象路径均位于元数据桶的 bucket-metadata 前缀下:

对象属主生命周期
.usage-cache.bin(每个 bucket/set 下)scanner 磁盘遍历由 scanner 依据对象元数据重建;数据缺失触发该 bucket/set 重扫;数据损坏不构成完整基线
.bloomcycle.binscanner 周期状态由 leader CAS 更新;状态缺失从未初始化周期开始;损坏或未来状态在自动重试前先被隔离
.usage.v2.json.usage.jsonscanner 权威发布.usage.v2.json是主要完整用量快照;.usage.json仅在携带有效持久化身份时作为遗留或伴随基线读取。两者都不能绕过 v2 纪元围栏;仅当基线身份与完成字段通过校验时,读者才可将其视为权威
.usage.observed.jsonscanner 观察路径在权威发布无法被证明但诊断快照仍有用时写入;永不是硬配额权威
bucket-metadata/.usage.jsonscanner 用量底线携带降级权威用量窗口内配额使用的持久化 per-bucket 底线;在下一次完整 scanner 发布前保持静态
.bloomcycle.bin.recovery-required.jsonscanner 周期恢复隔离无效周期状态并携带重试证据;仅 scanner 恢复代码可更新或清除
.scanner-cycle.lockscanner 运行时锁序列化周期级工作;锁对象缺失本身不是用量证据
.scanner-pause-backlog.jsonscanner 暂停与追赶账本在权威发布被数据迁移围栏期间追踪脏用量、发现的周期工作与全量扫描追赶;绝不授予发布准入

对象路径常量在源码中有明确出处,例如 data_usage_define.rs 中的路径定义 与 data_usage.rs 中的对象名常量:DATA_USAGE_OBJECT_NAME = ".usage.v2.json"DATA_USAGE_OBSERVED_OBJECT_NAME = ".usage.observed.json"LEGACY_DATA_USAGE_OBJECT_NAME = ".usage.json",恢复标记则派生为.recovery-pending.json/.recovery-required.json后缀。

观察快照与用量底线:降级路径的两层

观察快照:诊断与可用性层

观察快照仅在快照明确声明自身为部分或观察性质时才能被提供,且只提供给不会据此做出硬配额、持久性或删除决策的消费者。管理用量视图可以携带完整性标记(completeness flags)暴露该状态,让运维在权威发布被阻塞时仍能看到进度。配额强制不得将观察快照当作当前用量权威。

用量底线:静态下界而非新鲜计数

权威快照不可用时,配额准入可使用持久化用量底线。这是一个可用性回退,而非新鲜计数

  • 实时写入不会推进底线;
  • 超限仅受"下一次完整 scanner 发布前已接受的写入"约束;
  • 若连有效持久化底线都没有,配额保持不可用并失败关闭

可用性决策契约:四条款

决策文档给出的可用性契约精确为四条:

  1. 权威快速路径读取完整的内存或持久化 scanner 用量;
  2. 升级或发布中断期间,配额可依据持久化 per-bucket 用量底线准入;
  3. 底线在该中断窗口内是建议性的,必须收敛回完整 scanner 发布;
  4. 既无权威用量、也无有效底线的 bucket失败关闭

把该决策改为软配额(soft quota)模型属于产品变更而非 scanner 重构,需要分阶段移除权威特定层,并为上述持久化对象做兼容性处理。

删除与恢复规则:属主隔离

只有对象的属主才能删除或隔离它:

  • scanner 可在扫描证明替换内容后重建 per-set.usage-cache.bin
  • scanner 周期恢复可隔离无效的.bloomcycle.bin,且仅在有效周期状态持久化后才能清除标记;
  • scanner 发布只能通过上述发布围栏替换.usage.v2.json或遗留伴随对象;
  • 配额消费者可读取用量底线,但不得删除或修复它;
  • 运维只能通过受支持的 scanner 重置面重置用量状态——该面会记录重置路径并强制全量重建。

缺失、不可解码或无身份的数据不会被转换为零,而是按读者所属面分别报告为 uninitialized、recovery-required、observed-only 或 unavailable。源码中 cycle_state.rs 的恢复标记与重试预算 给出了实现细节:MAX_SCANNER_CYCLE_RECOVERY_RETRIES = 5,恢复状态机(healthy / blocked / paused / recovery-required / cleanup-pending / usage_floor_load_failed 等)由 gauge 指标rustfs_scanner_cycle_recovery_required对外暴露;此外还有幂等的恢复意图(recovery intent)机制,.usage.v2.recovery-intents/前缀下的意图记录支持scanner-usage-full-rebuild动作与 full-rebuild 模式,供运维在确认重置意图后强制重建。

源码佐证:从 QuotaChecker 到发布管线的实现细节

配额检查的三级查找与回归测试

QuotaChecker::get_real_time_usage 是决策"可用性故事"的直接实现:

  1. 先查内存权威用量get_bucket_usage_memory,命中即返回(权威快速路径);
  2. 降级窗口(注释明确指向 issue #5716)读取持久化 per-bucket 底线lookup_degraded_bucket_usage_baseline——该窗口最典型的触发场景是从 pre-v2 版本升级:遗留.usage.json缺少完整性标记被降级为非权威,而 scanner 首个完整周期(写出.usage.v2.json)可能还很遥远,若此时失败关闭,配额桶的每次写入都会在整个窗口内变成可重试 503;
  3. 两级都无结果才返回QuotaError::UsageUnavailable失败关闭。

仓库测试 checker.rs 中的回归测试 精确验证了这一契约:quota_admission_falls_back_to_legacy_snapshot_baseline构造遗留快照夹具验证回退基线值 1_234、验证未知 bucket 失败关闭、验证删除后端用量后重新创建的 bucket 不会继承旧 incarnation 的大小;quota_usage_rejects_an_unknown_mutation_baseline则验证无权威基线时必须失败关闭。

CAS 发布管线的证明与结果枚举

usage_store.rs 的发布管线 展示了围栏如何落到代码:发布保存走scanner_data_usage_publication_commit_scope_with_release_flag提交作用域,根 ACK 确认要求提交状态为 Committed 且写入 ETag 非空root_ack_write_is_confirmed);CAS 冲突触发有限重试(PreconditionFailed分支),并据最终结果产出DataUsagePersistOutcome枚举:NoUpdate / Current / AlreadyDurable / PriorCycleDurable / Saved / Deferred(原因) / Failed——其中Deferred(ScannerCycleDeferReason::DataMovement)PublicationLeaseDeadlineExceeded正是"发布围栏把结果打回重试/观察候选"的代码形态。观察快照写入.usage.observed.json目标路径,且必须先确认权威基线身份存在(usage_snapshot_authoritative_baseline)才允许落盘,呼应"观察层永不作配额权威"。

暂停积压账本:不授予准入的独立记账

backlog.rs 的模块注释明确声明:该账本复制在权威发布路径之外,因此在发布被数据迁移围栏期间仍可推进,但从不授予发布准入。它提供Idle / Paused / CatchingUp / RetryExhausted四个阶段与一组阈值常量:24 小时暂停时长告警、3 个被延迟周期告警、10,000 条积压条目告警、5 分钟最小追赶间隔、1 小时追赶窗口、每窗口最多 4 次尝试、5 次连续失败上限。

既有修复即不变式

契约文档明确指出,若干历史 scanner 修复是这份契约的推论而非独立补丁

  • 不完整的 scanner 用量不得成为完整的 admin 或配额基线——因为完整性与底线身份属于发布所有权的一部分;
  • 脏用量与维护确认必须围栏发布——因为携带未确认工作的远程节点会使候选失效;
  • 遗留或备份用量对象仅在携带有效基线身份、且不越过主纪元围栏时,才可帮助恢复可用性。

未来变更的影响与边界

决策文档为后续工作划定了两条硬边界:

  • #2214是未来用量发布变更的硬性设计输入。未来的提案仍可选择软配额与 MinIO 式 best-effort 用量,但那将是产品变更,必须为持久化工件自带分阶段兼容计划;
  • #2219可在当前权威协议的基础上设计 scanner 存储边界(storage boundary)。其接口必须包含契约所需的 CAS 键值、周期锁、用量底线、观察快照与恢复标记能力,不得把这些证明义务隐藏在一个通用对象存储 trait 之后。

对正在演进 RustFS 或设计同类分布式后台扫描 + 配额系统的工程师,这份决策记录的实际价值在于一个反直觉的结论:当后台扫描的结果要支撑写路径准入决策时,保留"多余"的证明层恰恰是最短路径——删除它们并不能让系统更简单,只会把扫描滞后直接转化为配额欠强制。

【免费下载链接】rustfs🚀2.3x faster than MinIO for 4KB object payloads. RustFS is an open-source, S3-compatible high-performance object storage system supporting migration and coexistence with other S3-compatible platforms such as MinIO and Ceph.项目地址: https://gitcode.com/GitHub_Trending/rus/rustfs

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/10 1:54:14

cann/ge Pyatc接口文档

Pyatc接口 【免费下载链接】ge GE(Graph Engine)是面向昇腾的图编译器和执行器,提供了计算图优化、多流并行、内存复用和模型下沉等技术手段,加速模型执行效率,减少模型内存占用。 GE 提供对 PyTorch、TensorFlow 前端…

作者头像 李华
网站建设 2026/9/10 1:53:35

Python接入QQ群机器人:从零搭建到部署的完整实践指南

从“想给群友整个活”开始,我花了两天时间把QQ群聊机器人搭了起来,用的就是官方开放的QQ开放平台和Python。坦率讲,这个方案比很多人想的要简单,但网上能查到的资料确实不集中,尤其是从零开始到“能跑起来”这一段&…

作者头像 李华
网站建设 2026/9/10 1:53:13

Spring 循环依赖与三级缓存深度解析:从源码到 AOP 的延迟设计

面试 Java 岗,Spring 循环依赖几乎是一道必问题。我见过不少候选人能把“一级缓存存成品、二级缓存存半成品、三级缓存存 ObjectFactory”背得很顺,但只要我追问一句:那第三级能不能去掉?场面就会安静好几秒。这种安静很正常&…

作者头像 李华
网站建设 2026/9/10 1:52:21

AI基础设施开源指南:从GPU调度到Agent运行时的全栈落地

1. 一场论坛,为什么把目光锁在“AI基础设施”这个底座 AI大模型卷了一年多,我观察到一个很有意思的变化:各团队比拼的重点,正在从“能不能训出模型”悄悄转向“能不能把模型稳定地跑起来”。前者拼的是算法和算力,后者…

作者头像 李华
网站建设 2026/9/10 1:52:14

PaddleOCR 3.x 快速上手指南:安装、命令行与 Python 推理实战

PaddleOCR 3.x 快速上手指南:安装、命令行与 Python 推理实战 【免费下载链接】PaddleOCR Turn any PDF or image document into structured data for your AI. A powerful, lightweight OCR toolkit that bridges the gap between images/PDFs and LLMs. Supports …

作者头像 李华
网站建设 2026/9/10 1:51:52

需求侧响应下配电网供电能力综合评估的Matlab复现与工程实践

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华