数据存储后端【免费下载链接】opendalApache OpenDAL: One Layer, All Storage.项目地址https://gitcode.com/gh_mirrors/op/opendal点击查看免费下载Apache OpenDAL 的规范Specifications定义了它在所有支持服务上承诺的可移植行为这些跨服务成立的可观察契约以活文档形式存在要求实现、API 文档与行为测试behavior tests始终与规范保持一致。本篇以仓库中 core/core/src/docs/specs/README.md 为主体完整展开规范的定义、治理流程与两份现行规范条件操作、Metadata的内容并结合 Capability 结构体、错误类型 与 behavior 测试 等源码实现说明规范—能力—测试—文档这条契约链在 OpenDAL 中如何闭环落地。什么是 OpenDAL Specifications活文档与可移植契约规范文档开篇给出了 specifications 的核心定位Specifications define the portable behavior OpenDAL promises: the observable contracts that hold across every supporting service. They are living documents; implementations, API documentation, and behavior tests must remain consistent with them as OpenDAL evolves.从中可以提炼出四个关键属性可移植契约portable contract规范只承诺在每一个支持该能力的服务上都成立的可观察行为。调用方依赖的必须是契约内行为契约之外的行为不属于可移植保证调用方不应假设它跨服务一致。活文档living documents规范描述的是当前契约。随着实现演进规范会随之更新而不是冻结在某次设计的状态。能力门控capability-gated部分承诺仅在服务声明advertise了对应 capability 时生效对不支持的请求服务返回显式错误而不是静默降级。三方一致性实现、API 文档、行为测试必须与规范保持一致。README 明确指出规范声明契约state the contractcore/tests/behavior 下的行为测试为契约提供可执行的符合性覆盖executable conformance coverage公共 API 文档在 API 使用现场解释可观察行为、能力要求、参数含义和相关错误然后链接到规范以获得完整的跨操作契约。规范与 RFC 的分工当前契约 vs 不可变历史README 中一段容易被忽略但极其重要的区分是规范与已接受 RFC 的关系Accepted RFCs are immutable historical documents. They record the design that reached consensus, including its original motivation and trade-offs. Specifications describe the current contract, even when later implementation work or a newer RFC changes the accepted design.也就是说RFC位于 core/core/src/docs/rfcs/如 RFC 8147 Write If Not Changed是不可变的历史文档记录达成共识时的原始动机与取舍即使后续实现改变了该设计RFC 原文也不改Specification描述当前契约是活文档。这对读者的实用意义在于查当初为什么这么设计去读 RFC查现在行为是什么、能依赖什么读 specification以规范为准。规范的治理流程PR 与 RFC 两条路径README 还定义了规范变更的两条路径编辑性修正Editorial corrections与固化已实现行为走普通 pull request 流程并附带该行为的证据实现、测试实质性契约变更Substantial contract change必须发起新的 RFC。且变更后实现、行为测试、API 文档、规范四者必须一起落地land together——不允许规范先改、实现后补或实现先上、文档欠账的状态。现行规范一览条件操作与 MetadataREADME 列出了两份 active specifications原文为指向站点页面的外链仓库内的权威来源是以下两份 Markdown 文件由 core/core/src/docs/specs/mod.rs 通过include_str!直接编入 rustdoc规范仓库内源文件核心内容Conditional operationsconditional-operations.md定义可移植的谓词、错误与原子性语义包括文件不存在时的行为Metadatametadata.md定义 owned metadata、紧凑构造上限与结果完成result completion语义mod.rs中的组织方式值得注意pub mod conditional_operations与pub mod metadata两个空模块各自挂载了对应 Markdown 的 rustdoc使规范成为 Rust API 文档的一等公民——开发者在rustdoc中查任何条件操作选项时都能被引到这份跨操作契约上。规范一条件操作Conditional Operations条件操作允许调用方只有当文件处于预期状态时才观察或变更存储。规范全文conditional-operations.md可拆为六个问题条件检查的是哪个文件、能力契约、文件存在/不存在时的行为、if_not_changed的降级lowering、错误契约、原子性与可见性。条件检查的是哪个文件规范首先澄清每个条件只检查一个文件并规定了不同操作下该文件是谁stat与read检查被观察的文件write与delete检查操作路径上的文件copy与rename检查目标文件destinationrestore检查当前存储在 restore 路径的文件而所选版本仍然是 restore 的来源。规范特别区分了两个易混淆概念version selector选择操作作用于哪个已存储版本version condition比较的是条件所检查的那个文件的版本。二者是不同概念。另一个明确声明的未定义区copy/rename 可能因为源不存在而失败restore 可能因为所选版本不存在而失败当这类错误与条件错误同时发生时规范不定义操作返回哪个错误——调用方不应依赖这种组合下的具体返回值。能力契约OpenDAL 永不静默忽略条件规范中Capability contract一节与源码中的 Capability 直接对应OpenDAL 在分发操作之前校验条件选项若服务未声明所需 capability操作返回Unsupported。OpenDAL 永远不会静默忽略一个条件。声明了某个条件能力的服务必须精确实现该条件的契约。独立的能力字段不意味着支持这些条件的组合。若实现接受组合文件必须满足所有条件若无法保持完整的合取conjunction必须返回Unsupported而不是丢弃某个条件。这一点在Capability结构体中体现得非常具体它按操作_条件的命名模式为每个组合单独设布尔字段例如stat_with_if_match、write_with_if_not_exists、copy_with_if_not_exists、rename_with_if_not_exists、delete_with_if_version_not_match等见 capability.rs。字段注释也明确写道Conditional capability fields follow the conditional operation specification——能力字段本身就是规范的镜像。文件存在时的条件语义表规范给出了一张完整的条件真值表stat/read/write/delete/copy/rename/restore通用以文件存在为前提。注意规范强调表中 ETag 条件使用具体 ETag 值通配符 ETagwildcard ETag除非某操作的具体文档另行定义否则没有可移植含义。Condition成功条件if_match(etag)文件的 ETag 等于etagif_none_match(etag)文件的 ETag 不等于etagif_version_match(version)文件的版本等于versionif_version_not_match(version)文件的版本不等于versionif_not_exists()永不成功文件存在即条件不成立if_modified_since(time)文件的修改时间在time之后if_unmodified_since(time)文件的修改时间不在time之后if_not_changed(metadata)文件具有metadata所表示的身份文件不存在时的条件语义表文件不存在时的行为是各云服务实现差异最大的地方规范为此给出了明确的可移植表对stat和read无论带什么条件都返回NotFound。对变更类操作mutationsCondition结果if_match(etag)ConditionNotMatchif_version_match(version)ConditionNotMatchif_not_changed(metadata)ConditionNotMatchif_none_match(etag)具体 ETag条件成功if_not_exists()条件成功if_version_not_match(version)未定义可移植行为条件成功后操作按正常语义执行删除一个不存在的文件仍然是成功的 no-opwrite/copy 可以创建文件rename 和 restore 仍可能因为源或所选版本不存在而失败。if_not_changed的降级lowering规则if_not_changed(metadata)是一个正身份条件来源于此前 OpenDAL 返回过的Metadata。规范定义了确定性的五步降级过程若操作声明支持 version matching且 metadata 中包含 version使用 version match否则若 metadata 包含 ETag使用 ETag match否则若 metadata 包含 version使用 version match若 metadata 两者都不含返回ConfigInvalid按正常流程校验推导出的原始条件若操作不支持则返回Unsupported。组合语义与推导条件完全相同的显式相等条件是冗余的与之冲突的显式相等条件返回ConditionNotMatch其他被接受的条件与推导条件按AND组合。降级发生的时机也被精确约束发生在可变公共 options 仍持有源 metadata、且 OpenDAL 已知组合后服务 capability的阶段最终固化的 raw 操作参数Op*中只含被选中的原始相等条件不再保留源Metadata。这部分在源码中有直接印证core/core/src/types/options.rs 中WriteOptions、DeleteOptions、CopyOptions等的if_not_changed: OptionMetadata字段携带公共 options 阶段的可变 metadatacore/core/src/raw/ops.rs 的测试options_lower_if_not_changed_before_freeze验证了降级行为当 capability 同时支持write_with_if_match和write_with_if_version_match、metadata 同时含 version 与 ETag 时OpWrite优先降级为if_version_match当 capability 只有delete_with_if_match时OpDelete降级为if_match公共 API 上if_not_changed以 builder 方法暴露例如 operator_futures.rs 中的WriteOptionsFuture::if_not_changed(metadata: Metadata)文档注释统一指向规范Refer tooptions::WriteOptions::if_not_changedfor the full contract。错误契约与协议状态码的边界规范定义了四类错误及其边界情形错误显式条件求值为假ConditionNotMatchstat/read观察到不存在的文件NotFound与条件无关服务自报的、与 OpenDAL 显式条件无关的冲突Conflict条件或条件组合不可用Unsupported关键细节是协议状态码本身不决定错误类别。例如 HTTP304 Not Modified或412 Precondition Failed只有当它是服务对OpenDAL 显式提供的条件的响应时才映射为ConditionNotMatch。这与 core/core/src/types/error.rs 中ErrorKind的定义一一对应ConditionNotMatch的文档注释明确写道仅对调用方通过 OpenDAL options 提供的条件返回服务侧自报冲突返回Conflict用stat/read观察不存在的文件无条件返回NotFound并链接回条件操作规范。此外ConfigInvalid的注释也点明了if_not_changed场景metadata 中没有可用 version 或 ETag正是ConfigInvalid的典型来源之一。一个实践示例来自 core/tests/behavior/async_write.rstest_write_with_if_not_exists先检查op.info().capability().write_with_if_not_exists不支持则跳过随后对已存在的路径执行.if_not_exists(true)写入并断言得到ConditionNotMatch。这就是能力门控 条件错误契约在测试中的落地形态。原子性与可见性禁止 stat-then-act规范对原子性提出了最硬性的要求服务只有在能够把条件判定与变更的可见提交visible commit原子化时才允许声明变更条件能力。实现不得用先stat再无条件变更来模拟条件变更stat-then-act 反模式被明确禁止。对分阶段或多段写入staged/multipart条件在最终提交时判定条件失败时先前可见的文件保持不变。条件错误在哪个环节暴露是未指定的可能在构造 I/O 句柄时、I/O 过程中、或关闭句柄时但最终结果与原子性契约不变。条件批量删除逐条独立判定每个条目只有当服务能保持每个条目的条件与错误时才允许批量处理。部分成功的详细报告遵循 delete 操作自己的契约。规范二Metadata 契约metadata.md 定义Metadata的可移植语义要点如下Owned、不可变、可任意生命周期持有Metadata是对一个存储条目的自有owned、不可变描述。它不借用自 response、lister、reader 或 operation因此调用方可以以任何生命周期持有它克隆之间可以在内部共享不可变存储而对某一个 clone 执行Metadata::into_builder并构建修改值不会影响其他 clone。构造与访问构造入口MetadataBuilder::file(content_length)、MetadataBuilder::dir()、MetadataBuilder::unknown()最后以MetadataBuilder::build完成已有 metadata 可通过Metadata::into_builder消费再用set_file(content_length)、set_dir()、set_unknown()修改 mode 后重建用户元数据user metadata通过借用视图UserMetadata暴露支持查找、长度检查与迭代且不需要HashMap分配未提供的 user metadata 映射与显式提供的空映射保持可区分。对应实现位于 core/core/src/types/metadata.rsinto_builderL203、fileL356、dirL364、unknownL370、buildL496。紧凑构造上限u16::MAXRust 侧 metadata 与固化后的操作参数使用紧凑块compact blocks表示含索引在内的编码尺寸不得超过u16::MAX字节超出即 panic。规范同时提醒各服务与调用方应在此表示边界之前先执行自己更小的协议限制。源码中MetadataBuilder::build的文档注释与断言total_len u16::MAX as usize见 metadata.rs与此完全一致。Content length 的完成语义规范对每个文件 Metadata 必然含完整对象长度给出了构造保证与提升promote规则MetadataBuilder::file(0)表示空文件目录与 unknown metadata 报告零Raw 服务与中间层在条目 mode 或完整长度任一不可用时使用Unknown并只在拥有权威长度的边界上提升为FILEstat 与 read 响应解析器只有在从服务响应拿到完整对象长度后才构造FILElist 完成时统计 unknown 条目、解析其 mode并用 stat 结果的长度提升文件write 完成时替换写入用已写字节数append 适配器使用最终对象偏移而非追加字节数copy 完成时使用权威的服务结果或若 copy 协议提供成功拷贝进度否则信任调用方断言的源长度不读取源 metadata无断言时才在拷贝前读源 metadata。它不会对目标发stat且当上述来源都无法提供权威长度时失败不携带对象长度的 delete marker 使用 content length 零。操作边界Operation boundary公共操作 options 是可变的逻辑构造值OpenDAL 在把它们冻结为 rawOp*参数之前会针对组合后的服务 capability 解析if_not_changed等逻辑条件。raw 参数中只保留服务必须执行的原始条件不保留源Metadata——这正是前文if_not_changedlowering 一节所述的机制两份规范在此交汇。符合性验证Capability 门控的行为测试规范Conformance一节承诺core/tests/behavior 下按 capability 门控的行为测试会针对真实服务验证可移植条件且只覆盖本规范定义的行为不施加规范之外的跨服务期望服务在声明条件能力之前必须先通过相关的可移植行为测试。这套机制的运行方式来自 core/tests/behavior/README.md用OPENDAL_TEST环境变量选择被测服务例如OPENDAL_TESTfs cargo test behavior --features tests运行某一类测试或单个测试OPENDAL_TESTfs cargo test behavior::test_write --features tests OPENDAL_TESTfs cargo test behavior::test_stat_dir --features tests测试按 operator 的完整 capability选择可用OPENDAL_TEST_CAPABILITY_OVERRIDES覆盖 capability 做针对性验证OPENDAL_TESTmemory OPENDAL_TEST_CAPABILITY_OVERRIDESstat_with_versionfalse,read_with_versionfalse,delete_max_size700 \ cargo test behavior --features tests环境变量未设置时所有行为测试默认跳过适合本地按需启用。条件操作相关的测试分布在 async_write.rs、async_read.rs、async_copy.rs、async_rename.rs 等文件中模式统一先检查op.info().capability().*_with_*能力字段不支持即跳过支持则执行条件操作并断言ConditionNotMatch等规范约定的错误——这正是规范 → capability 字段 → 行为测试三方一致的例证。文档链路规范如何被分发rustdoccore/core/src/docs/specs/mod.rs 用include_str!把README.md作为docs::specs模块文档把两份规范挂载为conditional_operations与metadata子模块文档开发者在 API 文档内即可直达完整契约官网文档website/docs/04-specifications/01-overview.mdx 等页面通过 frontmatter 中的specification_source指向本 README 与两份规范源文件站点内容由仓库内的权威 Markdown 生成API 现场每个条件选项if_match、if_not_exists、if_not_changed等的 rustdoc 在解释可观察行为与能力要求后链接回规范形成API 文档讲现场行为、规范讲跨操作契约的分工。历史决策Historical decisions规范末尾以Historical decisions一节保留了到达当前契约的设计轨迹体现了RFC 不可变、规范随当前契约演进的治理原则。条件操作规范引用的 RFC 包括RFC 5485: Conditional Reader条件读取的起源RFC 7818: Rename If Not ExistsRFC 8145: Conflict Error KindConflict错误类别的引入与ConditionNotMatch的边界划分RFC 8147: Write If Not Changedif_not_changed写入条件Metadata 规范则引用了 RFC 8194: Compact Metadata and Operation Arguments紧凑 metadata 与操作参数的设计及其跟踪 issue。这些 RFC 文档保存在仓库的core/core/src/docs/rfcs/目录中如 8147_write_if_not_changed.md、8194_compact_metadata_and_operation_args.md需要追溯设计动机时可按图索骥。小结OpenDAL 的规范体系可以概括为一句话规范声明契约capability 声明边界行为测试验证符合性API 文档在使用现场指回规范。对使用者的实际价值是只要服务声明了相应 capability你从规范表格中读到的每一行条件语义、每一条错误映射、每一项原子性承诺都是可依赖的跨服务保证而ConditionNotMatch、Conflict、NotFound、Unsupported、ConfigInvalid这几个错误类别的边界在规范与 ErrorKind 源码中是逐字一致的。要深入某一项契约建议的阅读路径是本 README 建立整体框架 → 对应规范文件读具体语义表 →capability.rs查服务实际声明 → behavior 测试看可执行验证。赞分享数据存储后端【免费下载链接】opendalApache OpenDAL: One Layer, All Storage.项目地址https://gitcode.com/gh_mirrors/op/opendal点击查看免费下载相关推荐Agent Zero 测试体系与 tests/AGENTS.md 契约pytest 回归、安全与契约测试的工程规范Agent Zero 测试体系与 tests/AGENTS.md 契约pytest 回归、安全与契约测试的工程规范 本文基于 Agent ZeroPytho人工智能大模型AI AgentAgent 框架自主智能体多智能体工具调用MCP 服务浏览器控制DeepSeek Harness 可移植标识符接缝代码运行时共享绑定排除契约的设计与落地DeepSeek Harness 可移植标识符接缝代码运行时共享绑定排除契约的设计与落地 DeepSeek HarnessEverything is a P人工智能AI AgentAgent 框架DeepSeekApache OpenDAL RFC-3017 解析为何移除 oio::Write::copy_from 及其设计演进Apache OpenDAL RFC 3017 解析为何移除 oio::Write::copy_from 及其设计演进 导读 本文以 Apache OpenD数据存储后端创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考