当前拓扑(plan-20260824,2026-08-24 落地): orbit 契约与实现已迁入本 package:
src/orbit_api/(traits、config、errors)与src/orbit/(object_store后端)。 对象存储由src/jupiter/storage/object_storage.rs::build_object_storage直接调用crate::orbit::factory::ObjectStorageFactory::build;无ObjectStorageProvider进程级注册表,无 独立crates/orbit*workspace 成员。下列历史正文记录自 2026-06 起的 provider 注入 + workspace crate 演进,保留供审计。
本文档记录 mega2 对 orbit 对象存储库的依赖治理:把当前对 orbit 实现 crate 的项目(path)引用重构为只依赖 orbit-api(纯 API/契约 crate),并通过依赖注入把唯一的具体实现构造点下沉到组合根 / 独立二进制边界。
治理规范:本文档遵循
general.md中定义的统一结构、共同约束和执行标准。在审阅或执行本计划前,请先查阅 general.md。
关键依赖:
- 与
config.md协同:ObjectStorageConfig/LocalConfig/S3Config/GcsConfig/ObjectStorageBackend均来自orbit_api::factory,经src/config/mod.rs:11re-export 进crate::config,并被config validate的对象存储校验消费。本重构不改变这些 API 类型的来源(仍是orbit-api)。- 与
vault.md协同:必须保持启动顺序硬约束Config(synchronous) → Storage(DB + object storage) → VaultCore。对象存储在Storage::new阶段(vault 之前)构造,本重构只移动其构造位置,不改变构造时机。
集成测试指引:对象存储 4 种后端(
S3/S3Compatible/Gcs/Local)的构造与读写行为必须保持不变。应通过integration.md中的对象存储 / 服务启动场景(本地后端 put/get、服务service http启动)端到端验证重构前后行为一致。
实现状态(2026-06-19):阶段 0–3 已全部落地并通过门禁。 mega2 已拆分为 Cargo workspace:根 crate
mega2-core(lib,只依赖orbit-api)+bin/cratemega2(瘦二进制,依赖mega2-core+orbit实现)。核心改造:在src/jupiter/storage/object_storage.rs定义ObjectStorageProvidertrait + 进程级注册表(set_object_storage_provider/build_object_storage);Storage::new/AppContext::new改为接收注入的MegaObjectStorageWrapper值;service/chat-migrate两个 exec 通过注册的 provider 构造对象存储;bin/src/main.rs注册OrbitObjectStorageProvider(唯一调用orbit::factory::ObjectStorageFactory::build处)。验收:cargo tree -p mega2-core的object_store计数 = 0、orbit-impl 计数 = 0;bin的object_store计数 = 1。门禁:workspacefmt/clippy -D warnings通过,mega2-core单测 530 passed,mega2集成测试 4 passed,二进制config init/validate烟雾通过。详见各阶段下的"✅ 已落地"标注。
实现状态(2026-08-22):orbit 两个 crate 已并入本 workspace("方案 B")。
crates/orbit+crates/orbit-api(原 sibling../orbit/crates/*)整体拷入本仓crates/,Cargo.tomlworkspacemembers = ["bin", "crates/orbit", "crates/orbit-api"];Cargo.toml依赖改为orbit-api = { path = "crates/orbit-api" },bin/Cargo.toml改为orbit = { path = "../crates/orbit" }/orbit-api = { path = "../crates/orbit-api" }。crate 边界与依赖方向零变化:core 仍只依赖orbit-api,仍不出现orbit::(实现 crate)源码引用;实测cargo tree -e features确认 core 的object_store仅启用default/fs/tokio/walkdir(无cloud/aws/gcp,aws-lc-rs为 rustls TLS 后端,与 AWS 无关),cloud/aws/gcp仍仅存在于bin的编译图(注:cargo tree中 core 的object_store计数为 1 系orbit-api自带依赖,合并前即如此,2026-06-19 的"计数=0"验收口径应理解为"带 cloud 特性的 object_store = 0")。同步改动:Dockerfile去掉COPY orbit(path 依赖全在mega2/内);两个 CI workflow(config-validation.yml/git-protocol-smoke.yml)删除 orbit sibling checkout 步骤(ORBIT_CHECKOUT_TOKEN作为 monoui token 的 fallback secret 名保留未动);README / docs/development.md 更新目录布局描述。门禁:cargo +nightly fmt --all --check通过(对crates/orbit既有 2 处 rustfmt 差异已按本仓 rustfmt.toml 修正);cargo clippy --all-targets --all-features -- -D warnings通过;cargo test中对象存储 / authz / vault / git 协议相关用例全绿(环境受限未跑的用例见下)。验收命令不变。
本文档的代码引用已对照当前
src/(mega2)与/run/media/eli/data/GitMono/orbit(orbit + orbit-api)逐条核对。需特别注意以下决定方案形态的事实:
- mega2 是单一二进制 crate。
src/下没有src/lib.rs,Cargo.toml只有[package] name = "mega2"(无[lib]、无[[bin]],隐式 bin =src/main.rs)。这意味着"核心代码"与"二进制"是同一个编译单元:仅把工厂调用"上移到 main"并不能把orbit从依赖图里去掉——要真正移除重量级依赖,必须引入 crate 边界(见阶段 3)。 - 全仓只有 1 处对 orbit 实现 crate 的引用。
src/jupiter/storage/object_storage.rs:8的pub use orbit::factory::ObjectStorageFactory;是整个 crate 中唯一的orbit::(实现 crate)源码引用;它经src/jupiter/storage/mod.rs:86引入,并在src/jupiter/storage/mod.rs:213运行期调用ObjectStorageFactory::build(&config.object_storage).await?。其余对 orbit 生态的引用(18 处,跨 13 个文件)全部是orbit_api::(API crate)——这正是目标依赖,无需改动。 Cargo.toml同时声明两个 path 依赖:orbit = { path = "../orbit" }(Cargo.toml:57,重量级实现)与orbit-api = { path = "../orbit/api" }(Cargo.toml:58,轻量 API)。两者都在[dependencies](非 dev-dependencies)。orbit-api已经是纯 API/契约 crate。 4 个模块(error/factory/log_storage/object_storage)只含 trait 定义、config 结构体、类型别名与Arc包装器,零存储逻辑;依赖轻量(async-trait、bytes、futures、reqwest、serde、serde_json、thiserror、toml),不含object_store、不含云 SDK、不含tokio(/run/media/eli/data/GitMono/orbit/api/Cargo.toml:6-14)。使用侧的抽象MegaObjectStorage/LogStorage/MegaObjectStorageWithLog与包装器MegaObjectStorageWrapper { inner: Arc<dyn MegaObjectStorageWithLog> }均在此(orbit/api/src/factory.rs:53-70、object_storage.rs)。orbit实现 crate 是重量级来源。 它额外引入object_store 0.13.2且启用["cloud", "aws", "gcp"]features(/run/media/eli/data/GitMono/orbit/Cargo.toml:10),带来 AWS S3 / GCP 云 SDK 及其凭据/HTTP 传输栈。orbit::factory::ObjectStorageFactory::build(cfg) -> OrbitResult<MegaObjectStorageWrapper>(orbit/src/factory.rs:21)按后端分派到build_s3_like/build_gcs/build_local,构造具体ObjectStoreAdapter(orbit/src/adapter.rs)并包进MegaObjectStorageWrapper(一个orbit-api类型)。- 消费端已"注入就绪"。
LfsService.obj_storage(src/jupiter/service/lfs_service.rs:10)、GitService.obj_storage(git_service.rs:18)、ArtifactService.obj_storage(artifact_service.rs:49)都只持MegaObjectStorageWrapper,且只通过.inner调 trait 方法(put_stream/get_stream/get_many等),不接触任何具体实现类型。Storage::new(mod.rs:196)在:213构造后于:216/:248/:305把它克隆进 3 个服务。 - mock / 测试路径已纯
orbit-api。src/jupiter/storage/object_storage.rs:36的mock_object_storage()用MegaObjectStorageWrapper::new(Arc::new(InMemoryObjectStorage::default()))在 crate 内构造,InMemoryObjectStorage仅实现orbit-api的MegaObjectStorage/LogStorage——这本身就证明"注入一个已构造好的包装器"模式可行且不需要实现 crate。 - 组合根与调用方已定位。
AppContext::new(config)(src/context/mod.rs:40)在:43调Storage::new;AppContext::new的两个调用方是src/commands/service/mod.rs:40与src/commands/chat_migrate.rs:114。
下表中"原始状态"为重构前;"✅ 已落地"为 2026-06-19 实现后的状态。
| 能力 / 组件 | 实现状态 | 关键事实与风险 |
|---|---|---|
| mega2 crate 形态 | ✅ 已拆为 workspace | 根 crate mega2-core(lib,src/lib.rs + [lib] doctest=false)+ bin/ crate mega2(瘦二进制)。原为单一二进制 crate。 |
| 对 orbit 实现 crate 的引用 | ✅ core 内为 0,集中在 bin | core 不再依赖 orbit;唯一 orbit::factory::ObjectStorageFactory::build 调用在 bin/src/main.rs 的 OrbitObjectStorageProvider。cargo tree -p mega2-core 的 orbit-impl 计数 = 0。 |
对 orbit_api 的引用 |
保持(即目标依赖) | 18 处 / 13 文件,全是 trait/config/error 类型;未改动。core 与 bin 都依赖同一 orbit-api。 |
orbit-api crate |
API-only,未改动 | 仅 trait/config/wrapper;无 object_store/云 SDK/tokio。本次重构未触碰 orbit-api(采用"注入值"而非新增 factory trait)。 |
| 构造抽象(factory) | ✅ 已落地:ObjectStorageProvider trait(在 core) |
src/jupiter/storage/object_storage.rs 定义 ObjectStorageProvider + 进程级注册表 set_object_storage_provider / build_object_storage;bin 提供 OrbitObjectStorageProvider 实现。 |
| 消费端(Lfs/Git/Artifact 服务) | 未改动(trait-only) | 仍只持 MegaObjectStorageWrapper、只调 .inner trait 方法。 |
| 组合根 | ✅ 已倒置 | Storage::new(config, object_store) / Storage::new_with_connection(config, connection, object_store) 接收注入值;AppContext::new(config) 在内部经 ObjectStorageProvider 注册表构造对象存储(DB-only vault bootstrap 后解析 vault:// SecretRef),再注入 Storage::new_with_connection;最终 orbit 调用在 bin main。 |
| mock / 测试路径 | 未改动(纯 orbit-api) | mock_object_storage() 仍在 core 内用 orbit-api 构造;Storage::new/AppContext::new 无测试调用方,注入值改造零测试破坏。 |
| 重量级传递依赖 | ✅ 已从 core 编译移除 | object_store {cloud,aws,gcp} + AWS/GCP SDK 现仅在 bin 的编译图(cargo tree -p mega2-core 的 object_store 计数 = 0;bin = 1)。 |
关键危险点(各阶段必须收敛):
- 仅完成阶段 1/2(隔离 seam)会让人误以为重量级依赖已移除——其实单 crate 仍链接
orbit。务必把"是否拆 crate"作为达成"只依赖 orbit-api"目标的承重决策(阶段 3)。 - 给
Storage::new/AppContext::new增加参数会触碰启动链路,可能扰乱Config → Storage → Vault顺序。 - 若选择给
orbit-api增加 factory trait,必须警惕"顺手往 orbit-api 加实现/重依赖",那会破坏 API-only 本性。
- 运行期对象存储行为零变更。 同一
orbit::factory::ObjectStorageFactory::build(&config.object_storage)必须仍对 4 种后端(S3/S3Compatible/Gcs/Local)运行,只移动调用位置,不改变构造逻辑与时机。理由:对象存储是 git/LFS/artifact 数据面,任何行为漂移都是数据风险。 orbit-api必须保持 API-only。 不得向orbit-api引入object_store、云 SDK 或tokio。理由:一旦 API crate 携带重实现,"依赖 API 即轻量"的前提失效,重构失去意义。- 注入类型必须是
orbit-api的MegaObjectStorageWrapper(Arc<dyn MegaObjectStorageWithLog>)。 不得把具体ObjectStoreAdapter/BackendStore(orbit/src/adapter.rs)泄露进 core。理由:core 一旦命名具体实现类型,就重新耦合实现 crate。 - 启动顺序约束(与 vault.md 一致):
Config(synchronous) → database_connection() → DB-only VaultCore bootstrap → resolve object storage SecretRef → build_object_storage → Storage::new_with_connection → redis → mail/notification。对象存储凭据在 vault 就绪后解析,随后构造对象存储并注入Storage::new_with_connection。 - 测试 / mock 路径不得依赖 orbit 实现 crate。
mock_object_storage()/Storage::mock/ArtifactService::mock必须只用orbit-api构造(现状已满足,重构后须保持)。 - 消费端代码不改。 18 处
orbit_api::引用(跨 13 文件)保持不动;本重构只动"构造与注入"的少数位置,不动"使用"。
| 维度 | 当前状态 | 目标状态 | 实现难度 |
|---|---|---|---|
| core 对实现 crate 的依赖 | Cargo.toml:57 直接 orbit = { path } |
core 不依赖 orbit 实现 crate,只依赖 orbit-api |
复杂(需拆 crate) |
| 工厂调用位置 | core 内 Storage::new 直接调 orbit::…::build(mod.rs:213) |
工厂调用下沉到单一 bootstrap 模块 / 瘦二进制边界 | 中等 |
| 对象存储注入 | Storage::new 内部自建 |
Storage::new(config, object_store: MegaObjectStorageWrapper) 由调用方注入 |
中等 |
orbit:: 源码引用数 |
1(object_storage.rs:8) |
阶段 2 后仍 1,但集中在 bootstrap;阶段 3 后 core 内为 0 | 简单→复杂 |
| core 编译的重量级依赖 | 含 object_store {cloud,aws,gcp} + 云 SDK |
core 编译不含;仅瘦二进制 / adapter crate 含 | 复杂(阶段 3) |
orbit-api 来源 |
path 依赖 | path 或发布版本(待决策,见"待决策项") | 简单 |
总体策略:两段式。 "阶段 1–2 = seam 清理(仍保留
orbit依赖)"是强制前置且使阶段 3 变成机械操作;"阶段 3 = 拆出mega2-corelib"才真正从 core 编译移除重量级依赖。推荐做法:注入"已构造好的值"MegaObjectStorageWrapper,而非注入 factory——因为构造产物本就是orbit-apitrait object,无需给orbit-api增加 factory trait,API 面最小。
✅ 已落地实现说明(2026-06-19)。 实际实现综合了"注入值"与"进程级 provider 注册表":
Storage::new/Storage::new_with_connection接收注入的MegaObjectStorageWrapper值;AppContext::new(config)在内部先解析vault://SecretRef,再经ObjectStorageProvider注册表构造对象存储并注入Storage::new_with_connection。- 因 config 在 core 的
cli::parse内解析、service/chat-migrate两个 exec(在 core 内)才知道 config,故无法在 bin 侧预先构造值;改为在 core 定义ObjectStorageProvidertrait + 进程级注册表(set_object_storage_provider/build_object_storage,src/jupiter/storage/object_storage.rs),bin 在main启动时注册OrbitObjectStorageProvider,AppContext::new调用build_object_storage(&config.object_storage)构造后注入Storage。- 选择该注册表而非"穿
CommandContext"是为零破坏cli::parse的大量测试调用方;选择"注入值 + 注册表"而非"给 orbit-api 加 factory trait"是为保持 orbit-api 零改动、API 面最小(硬约束 #2)。orbit::factory::ObjectStorageFactory::build的唯一调用点位于bin/src/main.rs(composition root)。
阶段 0 — 确认 seam 与锁定行为基线
- 确认
grep -rn 'orbit::' src/恰好只返回object_storage.rs:8一行。 - 确认唯一构造调用点是
mod.rs:213,消费端在:216/:248/:305。 - 确认 3 个服务只持
MegaObjectStorageWrapper且只走.innertrait 方法(git_service.rs:98/116/156等)。 - 跑现有测试 + 本地后端(
ObjectStorageBackend::Local)put/get 烟雾,记录基线行为。
验收标准:
- ✅
grep -rn 'orbit::' src/恰好 1 行- ✅ 基线测试通过;本地(及如有凭据则 S3/GCS)对象 put/get 正常
- ✅ 4 个实现 crate 触点(
Cargo.toml:57、object_storage.rs:8、mod.rs:86、mod.rs:213)已记录
阶段 1 — 把 object_store 作为注入参数穿过 Storage::new / AppContext::new
Storage::new(config: Arc<Config>)(mod.rs:196)改为Storage::new(config: Arc<Config>, object_store: MegaObjectStorageWrapper);删除:213的let object_store = ObjectStorageFactory::build(...).await?;,在:216/:248/:305直接用参数。AppContext::new(config)(context/mod.rs:51)在内部先执行 DB-onlyVaultCorebootstrap,再调用resolve_object_storage_secrets与build_object_storage构造对象存储,最后通过Storage::new_with_connection注入;保持Config → database_connection() → VaultCore → object storage → Storage → redis → mail/notification顺序。commands/service/mod.rs:40与commands/chat_migrate.rs的调用方仍直接传入config,对象存储构造留在AppContext::new内部(由进程级ObjectStorageProvider注册表实现)。Storage::mock/测试构造点改为传入mock_object_storage()(已是 orbit-api 构造)。
验收标准:
- ✅ crate 编译通过;
Storage::new/AppContext::new签名接收MegaObjectStorageWrapper,其内部不再调ObjectStorageFactory::build- ✅ 启动顺序
Config → Storage → Vault不变(vault 仍在Storage::new之后从其结果构造)- ✅ 全量测试通过;mock 路径不依赖 orbit 实现 crate
阶段 2 — 把唯一的实现 crate 调用隔离进单一 bootstrap 模块
- 新增
src/bootstrap/object_storage.rs(或类似),提供pub async fn build_object_store(cfg: &orbit_api::factory::ObjectStorageConfig) -> orbit_api::error::OrbitResult<orbit_api::factory::MegaObjectStorageWrapper> { orbit::factory::ObjectStorageFactory::build(cfg).await }——使其成为唯一的orbit::使用者。 - 删除
object_storage.rs:8的pub use orbit::factory::ObjectStorageFactory;与mod.rs:86的相应引入。 - 2 个
AppContext::new调用方改为先bootstrap::object_storage::build_object_store(&config.object_storage).await?再AppContext::new(config, object_store)。 - crate 内
mock_object_storage()与测试 mock 不变(已只用 orbit-api 类型)。
验收标准:
- ✅
grep -rn 'orbit::' src/恰好 1 行,且位于新 bootstrap 模块内- ✅ 全量测试通过;mock 路径不变
- ✅ 本地 + S3/GCS 运行期行为与阶段 0 基线一致
阶段 3 —(决策门)拆出 mega2-core lib crate,真正移除重量级依赖
- 引入 crate 边界:将除 bootstrap glue +
main()外的所有模块移入mega2-corelib crate(或当前 crate 转 lib + 新增瘦 bin)。 mega2-core的Cargo.toml:只依赖orbit-api,不依赖orbit。- 瘦二进制 crate(或独立 adapter crate,见"待决策项"):依赖
mega2-core+orbit = { path = "../orbit" };持有src/bootstrap/object_storage.rs与main.rs,先build_object_store再AppContext::new。 - 按需把
AppContext/Config/Storage/MegaError等设为pub,暴露最小入口 API。 - 校验
cargo tree -p mega2-core不含object_store/aws/gcp。
验收标准:
- ✅
cargo tree -p mega2-core | grep -E 'object_store|aws|gcp'为空- ✅ 二进制对 4 种后端运行期行为与基线一致
- ✅ 全量测试通过;core crate 的依赖数 / 编译时间可度量下降
| 本文档的工作 | 对其他文档的依赖 | 类型 | 关键同步点 |
|---|---|---|---|
阶段 1 注入参数穿过 Storage::new |
vault.md(启动顺序 Config→Storage→Vault) |
协同 | 不得改变对象存储相对 vault 的构造时机 |
ObjectStorageConfig 仍来自 orbit_api(不动) |
config.md(config re-export + 对象存储校验) |
协同 | config 的对象存储字段/校验语义不变 |
| 行为不变验证 | integration.md(对象存储 / 服务启动场景) |
后置 | 用本地后端 + service http 烟雾验证重构前后一致 |
| 阶段 3 拆 crate | 无外部前置;属结构性拆分 | — | 需一次独立可评审的 PR |
本重构不被任何其他文档阻塞,也不引入新的跨模块循环依赖;它只收窄 mega2 对外部 orbit 生态的依赖面。
- 风险:单 crate 形态导致阶段 1–2 不减少依赖图。
- 影响:误判"已只依赖 orbit-api",实际
object_store/云 SDK 仍编译。 - 缓解:明确只有阶段 3(lib 拆分)才移除依赖;以
cargo tree -p mega2-core作为目标达成门禁。
- 影响:误判"已只依赖 orbit-api",实际
- 风险:穿参数扰乱启动顺序。
- 影响:init 顺序 bug(如 vault 早于 storage、对象存储构造时机错位)。
- 缓解:在 2 个调用方中紧接
AppContext::new之前构造object_store;保留"Storage::new连库→使用注入的对象存储";用现有启动测试断言顺序。
- 风险:阶段 3 拆 crate 需把大量私有
mod改pub,暴露内部耦合。- 影响:机械改动量大,可能出现可见性 / 循环问题。
- 缓解:阶段 1–2 落地且测试绿后再做阶段 3;增量拆分,仅暴露最小 pub 入口;作为独立 PR。
- 风险:给
orbit-api加 factory trait 时顺手加实现/重依赖。- 影响:
orbit-api不再 API-only,重构目的落空。 - 缓解:优先"注入值"而非"注入 factory";若确需 factory trait,保持 trait-only,并在评审中禁止
orbit-api增加重依赖。
- 影响:
- 约束:消费端 18 处
orbit_api::引用不得改动。 理由:它们是目标依赖;改动会扩大变更面、增加回归风险。
| 维度 | 评估结论 |
|---|---|
| 合理性 | 高(9/10)。准确抓住"唯一实现 crate 触点 = 1 处工厂调用"且"消费端已全是 orbit-api trait object"的事实,重构本质是把一次构造调用下沉、并(可选)拆出 lib 边界。方向与现有抽象高度吻合。 |
| 可行性 | 中高(8/10)。阶段 1–2 改动面极小(参数穿透 + 单文件隔离),低风险;阶段 3 是机械但量大的 crate 拆分,风险集中在可见性与构建配置。 |
| 完整性 | 中高(7.5/10)。"只依赖 orbit-api"目标只有在阶段 3 才真正达成;阶段 1–2 是必要前置但本身不减依赖。待决策项(拆 crate、impl 落点、是否发布 orbit-api)需人决策后方可完全闭环。 |
| 安全性 | 高(8.5/10)。不触碰对象存储构造逻辑与凭据处理,仅移动调用位置;启动顺序硬约束保持;不泄露具体实现类型进 core。 |
| 功能正确性 | 高(9/10)。运行期对同一 build 的调用不变,4 后端行为应逐字节一致;mock 路径已证明注入可行。需以集成测试守住"行为不变"。 |
| 兼容性 | 中高(7.5/10)。orbit-api API 面不变(推荐方案不加 factory trait);消费端不改。阶段 3 的 crate 拆分改变构建拓扑,需更新 CI(config-validation.yml 已 checkout orbit sibling,应同步覆盖 core/bin 两 crate)。 |
| 可扩展性 | 高(8.5/10)。注入边界一旦建立,可在二进制 / adapter crate 中替换不同对象存储实现(含测试用内存实现),并为"多后端 / 可插拔存储"打开空间。 |
- ✅ 是否执行阶段 3 的 crate 拆分? —— 已执行。拆为
mega2-core(lib,仅 orbit-api)+mega2(bin,含 orbit 实现)。cargo tree -p mega2-core已无object_store/orbit-impl。 - ✅ 拆分后 orbit 实现依赖落在哪里? —— 直接放进
mega2瘦二进制 crate(bin/),未单列 adapter crate。OrbitObjectStorageProvider即在bin/src/main.rs。若日后有第二个二进制需复用,可再抽出orbit-bootstrapadapter crate(增量改动)。 - ✅ 注入形态: —— 注入"已构造好的值" + 进程级
ObjectStorageProvider注册表(见上"已落地实现说明")。未给orbit-api增加 factory trait,orbit-api 零改动。 - ⬜
orbit-api来源: —— 仍为 path 依赖(mega2-core→../orbit/api,bin→../../orbit+../../orbit/api)。是否发布orbit-api到 registry / 内部 git 以获得真正的跨仓库构建隔离,仍为开放决策(与本次模块/依赖卫生正交,可后续单独推进)。 - ✅
chat_migrate二进制路径: —— 已一致更新。commands/service/mod.rs:40与commands/chat_migrate.rs:114均直接调用AppContext::new(config);对象存储构造留在AppContext::new内部,通过同一ObjectStorageProvider注册表构造,保证service与chat-migrate使用一致的真实对象存储。
本计划已于 2026-06-19 完整实现(阶段 0–3)。 mega2 现为 workspace:
mega2-core(lib,仅依赖orbit-api)+mega2(bin,注入orbit实现)。cargo tree -p mega2-core不再含object_store/云 SDK/orbit-impl;所有门禁(fmt / clippy-D warnings/ 530 单测 / 4 集成测试 / 二进制烟雾)通过。唯一开放项是是否将orbit-api发布为带版本依赖(待决策项 #4)。
mega2 对 orbit 的"实现 crate 项目引用"实际上只落在一处——Storage::new 中对 orbit::factory::ObjectStorageFactory::build 的调用;其余对象存储交互已经全部走 orbit-api 的 trait/config/wrapper 抽象,消费端零具体类型耦合。因此把依赖重构为"API 方式"的核心动作很小:把对象存储改为由调用方注入已构造好的 MegaObjectStorageWrapper(orbit-api 类型),并把唯一的工厂调用隔离进单一 bootstrap 模块。但由于 mega2 是单一二进制 crate,要真正从核心编译中移除 object_store + 云 SDK,还需把代码拆为 mega2-core(仅依赖 orbit-api)+ 瘦二进制 / adapter crate(持有 orbit 实现依赖)——这是达成目标的承重决策。
- 核心代码与对象存储实现解耦:
mega2-core只依赖稳定的orbit-api契约,可独立编译、测试与演进;切换 / 新增对象存储实现不再触碰核心。 - 核心编译显著瘦身(阶段 3 后):从 core 编译图移除
object_store {cloud,aws,gcp}与 AWS/GCP 云 SDK,缩短编译时间、减少依赖攻击面。 - 可插拔与可测试性提升:注入边界使内存实现 / 替身存储可直接用于测试与本地运行,
mock_object_storage()模式被推广为一等公民。 - 依赖卫生与边界清晰:
grep orbit:: src/收敛到单一 bootstrap 点(阶段 2)乃至 core 内为 0(阶段 3),消除"项目引用"式的隐式强耦合。
ObjectNamespace 变体的 字符串形式是稳定契约(实现:src/orbit_api/object_storage.rs;变更须走兼容性文档)。现行清单:
| Variant | as_str() |
用途 |
|---|---|---|
Git |
git |
Git 对象字节 |
Lfs |
lfs |
Git LFS 对象 |
Log |
log |
日志段 |
Artifact |
artifact |
构建产物 |
Attachment |
attachment |
附件 |
Oci |
oci |
OCI Distribution 字节(blobs/ / manifests/ / uploads/;键布局见 oci.md,plan-20260902 / DR-04+DR-14) |
Media |
media |
FastCDC Media 对象(docs/refactoring/fastcdc-media.md;plan-20260901 FC-03) |
MegaObjectStorage::put_stream_bounded 是 不缓冲整对象 的写入入口:
- 默认实现明确返回 unsupported,不 poll 输入 stream(避免不支持的 backend 把全流读进内存)。
ObjectStoreAdapter覆盖为强制 multipart,忽略 配置的UploadStrategy::SinglePut。- multipart 聚合成固定 8 MiB part,最后一块可更小;stream 错误或
complete失败时abort,不发布半成品。 - 既有
put_stream策略不变(Git/LFS/Artifact/Attachment 仍走原来的 SinglePut/Multipart/idempotent 分支)。