Jerry's Blog

Back

参考写作风格:Jerry’s Blog《深度复盘:Dicfuse 测试超时问题调试全记录》(目录 + 现象驱动 + 假设验证 + Trade-off + 坑点 + 行动项)
信息来源:两份 agent transcript(2d3c933a-...1fb9cee3-...)的全过程记录
范围:聚焦 Error getting build targets -> buck2 cells -> Error creating cell resolver 这条故障链路的定位、修复演进、语义一致性改造,以及验证闭环受环境影响的复盘


目录#


1. TL;DR(给忙的人)#

1.1 发生了什么?#

当用户从 CL 页面触发 “build targets” 解析时,服务端报:

Error getting build targets: Fail to get cells: Buck2 stderr: Command failed: Error creating cell resolver
Couldn't find a buck project root for directory `/tmp/.../antares/mnt/{UUID}`. Expected to find a .buckconfig file.
text

1.2 根因是什么?#

一句话:挂载参数的语义与 Buck2 project root 的硬约束不一致

  • Buck2 只能从”当前目录向上”找 .buckconfig;找不到就直接失败(不会向下找)。
  • Antares 的 mount 支持”按路径裁剪为子目录视图”;如果请求里传的是子目录(如 /mega),挂载点根目录只暴露该子目录内容,仓库根的 .buckconfig 被裁掉。
  • 由于 mountpoint 的父目录在宿主机而不在 mount 视图内,Buck2 再向上也不可能找到 .buckconfig,因此该报错对这类入参是必现

1.3 我们怎么修?(分阶段)#

  • 阶段 A(止血,快速恢复可用):把 orion 的挂载逻辑改为”始终挂 /”,保证 .buckconfig 可见,Buck2 不再报错(commit dec83222a)。

    • 代价:语义变化(忽略请求中的 repo/path),在多仓/多 root 场景可能不通用。
  • 阶段 B(语义一致,面向长期鲁棒):不再”偷偷换根”,而是把契约显式化并前置校验:

    • API/文档默认字段名统一为 mount_path(旧字段 repo/path 仍兼容解析)。
    • orion 在 mount 后预检 mount_point/.buckconfig
      • 缺失则通过 WS 输出清晰错误:mount_path 必须是 Buck2 project root(如 '/'),并直接失败退出。
    • 同时补齐两类”次生风险”修复:
      • 并发 targets 导出 base.jsonl/diff.jsonl 的覆盖风险:改为 per-task 临时目录
      • 增加 Buck2 cells + audit_configreadiness 检查(更早失败、更好定位)。

1.4 验证情况一句话#

  • Rust 单测通过orion 43/43、orion-server 6/6(同时修了一个 BuildDTO::from() 的 move/clone 编译问题,commit 92da7e8b0)。
  • 完整 Docker E2E 未完全闭环:受 Docker Hub/镜像源/DNS/代理/镜像 manifest 等环境因素影响,采用了”代理 + 部分 compose + 本地启动服务”的折中路线;建议在稳定网络/一致平台上补齐最终 E2E 回放。

1.5 调试时间线(压缩版)#

┌────────────────────────────────────────────────────────────────────────┐
│  T0: 用户报障                                                           │
│      "Error getting build targets: ... Couldn't find a buck project    │
│       root ... Expected to find a .buckconfig file."                   │
├────────────────────────────────────────────────────────────────────────┤
│  T1: 定位失败点                                                         │
│      grep "buck2" / "cells" / ".buckconfig" → 锁定 orion/buck_controller│
│      确认失败发生在 buck2 cells() 初始化阶段                             │
├────────────────────────────────────────────────────────────────────────┤
│  T2: 追溯 mount 语义                                                    │
│      读取 scorpio/daemon/antares.rs normalize_mount_path()              │
│      确认 Antares 支持"子目录视图裁剪"                                   │
├────────────────────────────────────────────────────────────────────────┤
│  T3: 假设闭环                                                           │
│      H1: .buckconfig 在根,mount 看不到 → ✓ (stderr 证实)               │
│      H2: mount 传参是子目录视图 → ✓ (Antares 语义证实)                   │
├────────────────────────────────────────────────────────────────────────┤
│  T4: 止血修复 (Phase A)                                                 │
│      强制挂载 "/" → commit dec83222a → push fix/buck2-mount-root        │
├────────────────────────────────────────────────────────────────────────┤
│  T5: 用户反馈"语义变化不可接受"                                          │
│      重新审视:fallback 会掩盖错误、破坏多 root 场景                      │
├────────────────────────────────────────────────────────────────────────┤
│  T6: 语义一致化改造 (Phase B)                                            │
│      移除 fallback、统一 mount_path 字段、前置 .buckconfig 预检          │
│      增加 buck2 cells readiness 检查、WS 明确错误输出                    │
├────────────────────────────────────────────────────────────────────────┤
│  T7: 并发安全修复 (Phase C)                                              │
│      base.jsonl/diff.jsonl → TempDir per-task 隔离                      │
├────────────────────────────────────────────────────────────────────────┤
│  T8: 单元测试验证                                                        │
│      orion 43/43 ✓ | orion-server 6/6 ✓                                │
│      (中途发现 BuildDTO::from() move/clone 编译问题,commit 92da7e8b0)   │
├────────────────────────────────────────────────────────────────────────┤
│  T9: E2E 验证受阻                                                        │
│      Docker Hub 超时 → 配置镜像加速器 → DNS 失败 → 配置代理 →            │
│      端口冲突 (6379) → 清理容器/网络 → 镜像 manifest 不匹配              │
│      最终采用"Compose 依赖 + 本地服务"折中方案                           │
└────────────────────────────────────────────────────────────────────────┘
plaintext

2. 问题概述#

2.1 问题现象#

  • 访问 CL 页面触发 build targets 解析时报错(核心是 Buck2 无法创建 cell resolver)。
  • 错误信息指向 .buckconfig 缺失。

2.2 影响评估#

  • 功能影响:targets discovery 阶段直接失败,构建链路”起步即失败”(阻断型故障)。
  • 排障成本:错误发生在 buck2 stderr,若缺少前置校验与结构化上下文,定位会依赖对 Antares mount 语义的隐含知识。

3. 系统链路与约束(为什么会”必现”)#

3.1 系统调用链(从请求到报错点)#

┌─────────────────────────────────────────────────────────────────────────────┐
│                              整体架构示意                                    │
├─────────────────────────────────────────────────────────────────────────────┤
│                                                                             │
│   ┌──────────────┐                                                          │
│   │  Web 前端    │  CL 页面触发 "build targets" 请求                         │
│   │  (gitmega)   │                                                          │
│   └──────┬───────┘                                                          │
│          │ HTTP POST /api/task                                              │
│          │ { "mount_path": "/mega", "cl": "SVOBYAV2", ... }                 │
│          ▼                                                                  │
│   ┌──────────────┐                                                          │
│   │ orion-server │  API 网关 / 任务调度                                      │
│   │  (Rust)      │  - 解析 TaskRequest                                      │
│   │              │  - 通过 WS 下发到 worker                                  │
│   └──────┬───────┘                                                          │
│          │ WebSocket                                                        │
│          ▼                                                                  │
│   ┌──────────────┐                                                          │
│   │    orion     │  Build Agent (Worker)                                    │
│   │  (Rust)      │  - mount_antares_fs(job_id, path, cl)                    │
│   │              │  - get_build_targets() → buck2 cells/targets             │
│   └──────┬───────┘                                                          │
│          │ RPC / FUSE mount                                                 │
│          ▼                                                                  │
│   ┌──────────────┐                                                          │
│   │   scorpio    │  FUSE 文件系统 (Antares + Dicfuse)                        │
│   │  (Rust)      │  - normalize_mount_path(path)                            │
│   │              │  - 按 path 裁剪视图(可能是子目录)                        │
│   └──────┬───────┘                                                          │
│          │                                                                  │
│          ▼                                                                  │
│   ┌──────────────────────────────────────────────────────────────────┐      │
│   │  /tmp/scorpio-megadir/antares/mnt/{UUID}                         │      │
│   │  ├── src/           ◄─── 如果 path="/mega",这里只有 /mega 内容   │      │
│   │  ├── Cargo.toml                                                  │      │
│   │  └── (NO .buckconfig!)  ◄─── .buckconfig 被裁掉了!               │      │
│   └──────────────────────────────────────────────────────────────────┘      │
│          │                                                                  │
│          ▼                                                                  │
│   ┌──────────────┐                                                          │
│   │    buck2     │  构建系统                                                 │
│   │              │  - buck2 cells → 向上找 .buckconfig                       │
│   │              │  - 找不到 → Error creating cell resolver ❌               │
│   └──────────────┘                                                          │
│                                                                             │
└─────────────────────────────────────────────────────────────────────────────┘

关键约束冲突:
┌────────────────────────────┐     ┌────────────────────────────┐
│     Buck2 的硬约束         │     │    Antares 的语义          │
├────────────────────────────┤     ├────────────────────────────┤
│ 只向上找 .buckconfig       │  ≠  │ path 可以是子目录视图      │
│ 不会向下遍历               │     │ 裁剪后根文件不可见         │
└────────────────────────────┘     └────────────────────────────┘
plaintext

3.2 Buck2 的硬约束(关键)#

  • Buck2 通过”当前目录及其父目录链”寻找 .buckconfig 来判定 project root。
  • 它不会向下遍历寻找 .buckconfig

3.3 Antares mount 的语义(关键)#

  • path/repo(后统一为 mount_path)并不总等价于”仓库根”,可能是”monorepo 子目录视图”。
  • 一旦裁剪为子目录视图,挂载点根目录就是”子目录根”,仓库根文件不可见。

结论:当 mount_path 不是 Buck2 root 时,buck2 cells 对该 mountpoint 是确定性失败


4. 定位过程:从现象到根因#

4.1 现象驱动的第一步:锁定”失败点”#

  • Error getting build targets 追踪到 orion 在 mountpoint 内执行 buck2 cells(...)
  • 失败发生在 cells/resolver 初始化阶段,而不是 targets 计算阶段。

4.2 假设-验证循环(最关键的闭环)#

  • 假设 H1.buckconfig 在仓库根,但 mountpoint 看不到它。
    • 证据:Buck2 stderr 明确提示 mountpoint 找不到 .buckconfig
  • 假设 H2:mountpoint 之所以看不到 .buckconfig,是因为 mount 传参是子目录视图(如 /mega)。
    • 证据:Antares mount 支持按 path 裁剪;当挂载子目录视图时,根文件被裁掉。

这两个假设成立后,根因即闭环:“请求参数语义(子目录)“与”Buck2 root 必须包含 .buckconfig”之间发生错配


5. 修复方案演进(止血 → 语义一致 → 鲁棒化)#

这一节的重点不是”改了什么”,而是”为什么要从 A 走到 B”——即对系统语义的尊重与可运维性提升。

5.1 阶段 A:止血型修复(强制挂 /#

  • 做法orion 忽略请求里的 repo/path,mount 时一律挂载 /
  • 收益:立刻恢复 Buck2 cells 能力,消除 .buckconfig 报错(快速恢复可用)。
  • 问题(隐患)
    • 语义变化:调用方认为构建的是”某 repo/path”,实际 worker 强行用 /
    • 多仓/多 Buck root 形态下风险大:如果 Buck root 不在 /,该止血方案会反向失效。

5.2 阶段 B:把契约”显式化”(mount_path + 预检 + 明确失败)#

核心原则:不通过隐式 fallback 改变请求语义,而是让系统在错误入参时”尽早、清晰、可行动地失败”。

落地包括:

  • 字段语义统一:请求/响应/文档默认字段名统一为 mount_path(兼容旧字段 repo/path 作为 alias)。
  • 挂载后预检:在 orion mount 完成后,检查 mount_point/.buckconfig
    • 缺失时:WS 输出明确提示,并返回失败(用户/调用方可据此修正入参)。
  • readiness 检查:把 buck2 cells + audit_config 前置执行,并把失败信息结构化回传。

5.3 阶段 C:修复”并发稳定性”隐患(与根因不同,但同批次必须修)#

get_build_targets() 曾固定写 base.jsonl/diff.jsonl,并发任务会互相覆盖导致”偶发错乱”。

  • 做法:改为 per-task 临时目录(例如 TempDir)承载 targets 输出文件。
  • 意义:这属于”未来事故种子”——不修会在高并发 CI 下炸成更难定位的间歇性故障。

6. 关键改动点(按模块拆解)#

6.1 orion(worker / build agent)#

重点:把 Buck2 失败从”stderr 偶现”变成”早期校验 + 结构化输出”,并修复并发覆盖。

  • 路径规范化normalize_mount_path()(空/无前导 //尾随 / 统一)。
  • 挂载后预检has_buckconfig(mount_point)(异步 metadata 检查)。
  • readinessload_buck2_cells(mount_point)(cells + audit_config)。
  • targets 并发安全base.jsonl/diff.jsonl 写入 per-task TempDir
  • 主函数语义build(..., mount_path: String, ...)(日志与参数名对齐)。
📜 代码示例:路径规范化与 .buckconfig 预检(点击展开)
// orion/src/buck_controller.rs

/// 规范化挂载路径:处理空值、缺少前导/、尾随/等边界情况
fn normalize_mount_path(path: &str) -> String {
    let trimmed = path.trim();
    if trimmed.is_empty() {
        return "/".to_string();
    }
    let with_leading = if trimmed.starts_with('/') {
        trimmed.to_string()
    } else {
        format!("/{}", trimmed)
    };
    // 移除尾随 /(除非是根 "/")
    if with_leading.len() > 1 && with_leading.ends_with('/') {
        with_leading.trim_end_matches('/').to_string()
    } else {
        with_leading
    }
}

/// 异步检查 mount_point 下是否存在 .buckconfig
async fn has_buckconfig(mount_point: &str) -> Result<bool, Box<dyn Error + Send + Sync>> {
    let buckconfig_path = Path::new(mount_point).join(".buckconfig");
    match fs::metadata(&buckconfig_path).await {
        Ok(metadata) => Ok(metadata.is_file()),
        Err(err) if err.kind() == std::io::ErrorKind::NotFound => Ok(false),
        Err(err) => Err(Box::new(err)),
    }
}

/// Buck2 readiness 检查:加载 cells 配置
fn load_buck2_cells(mount_point: &str) -> anyhow::Result<CellInfo> {
    tracing::info!("Get cells at {:?}", mount_point);
    let mount_path = PathBuf::from(mount_point);
    let mut buck2 = Buck2::with_root("buck2".to_string(), mount_path);
    buck2.cells()
}
rust
📜 代码示例:严格语义校验 + 明确错误输出(点击展开)
// orion/src/buck_controller.rs - build() 函数核心逻辑

// 关键改动:挂载后立即校验 .buckconfig
if !has_buckconfig(&mount_point).await? {
    // 不再 fallback,直接明确报错
    let error_msg = format!(
        "[Task {id}] mount_path '{}' 缺少 .buckconfig,\
         请确保传入的是 Buck2 project root(通常是 '/')",
        normalized_mount_path
    );
    tracing::error!("{error_msg}");
    
    // 通过 WebSocket 将错误信息推送到前端
    if sender
        .send(WSMessage::BuildOutput {
            id: id.clone(),
            output: error_msg.clone(),
        })
        .is_err()
    {
        tracing::warn!("Failed to send buckconfig missing error to WS");
    }
    return Err(error_msg.into());
}
rust
📜 代码示例:并发安全的 targets 输出(点击展开)
// orion/src/buck_controller.rs - get_build_targets()
use tempfile::TempDir;

// 使用临时目录隔离并发任务的输出文件
let temp_dir = TempDir::new()
    .map_err(|err| anyhow!("Failed to create temp dir: {}", err))?;

let base_path = temp_dir.path().join("base.jsonl");
let diff_path = temp_dir.path().join("diff.jsonl");

// 现在每个任务都有独立的输出路径,不会互相覆盖
let base = get_repo_targets(&base_path, &mount_path)?;
let changes = Changes::new(cells, mega_changes)?;
let diff = get_repo_targets(&diff_path, &mount_path)?;

// temp_dir 在函数结束时自动清理(RAII)
rust

6.2 orion-server(API / schema / docs)#

重点:把”repo/path”的歧义收敛为 mount_path,同时保持兼容。

  • 请求字段
    • 默认字段名:mount_path
    • 兼容 alias:repopath
  • 查询参数文档(utoipa params 等)同步改为 mount_path
  • README 示例同步更新。

6.3 orion-server/bellatrix(client SDK)#

  • 同步字段名与序列化行为(mount_path 为默认)。

6.4 ceres(上游触发方)#

  • 构建请求字段从 repo 更新为 mount_path,避免继续传播旧语义。

7. 验证与证据:做到了什么、缺了什么#

7.1 已完成的验证(有硬证据)#

  • orioncargo test --lib 43/43 通过
  • orion-servercargo test --lib 6/6 通过
  • 编译链路:在网络不稳定时出现 crates 下载超时;后通过代理环境变量(示例:HTTP_PROXY/HTTPS_PROXY/ALL_PROXY + CARGO_HTTP_TIMEOUT=600)完成 cargo fetchcargo check

说明:这类验证证明”逻辑与编译正确”,但不等价于”端到端链路在真实环境 100% 正常”。

7.2 E2E 验证的阻碍与应对(这次复盘必须直面)#

阻碍 A:Docker Hub / 镜像源 / DNS

  • 多次 docker compose up -d 受限于镜像拉取超时与镜像源 DNS 失败。
  • 后发现系统存在本地 HTTP 代理(127.0.0.1:7897),但 Docker daemon 未使用;通过为 Docker systemd service 配置代理后,镜像拉取成功。

阻碍 B:端口冲突

  • 镜像拉取成功后遇到本地端口冲突(例如 6379)。
  • 通过排查占用进程、清理/重启容器与网络等方式逐步推进。

阻碍 C:镜像 manifest / 平台不匹配

  • 在”完整 E2E”阶段,出现镜像 manifest 不匹配(典型是镜像不包含当前平台的 manifest)。
  • 因此采取折中:用 Compose 拉起依赖(DB/Cache/存储),而 orion-server / orion-worker 走本地启动,以完成核心链路验证。

7.3 缺失的最终闭环(建议补齐)#

  • 在稳定网络与一致平台上,跑一次”从 CL 页面触发 → targets 返回”的完整回放。
  • 增加一条自动化健康检查:mount 后执行 buck2 cells(或更轻量的 .buckconfig 校验)并将结果纳入监控/告警。

8. Trade-off 分析:为什么最终选择”严格语义 + 明确失败”#

8.1 “自动 fallback 到 /”为什么看似好、但长期风险大?#

优点:

  • 体验上”更不容易失败”,短期减少工单。

隐患:

  • 语义漂移:用户以为构建的是 mount_path=/mega,系统却悄悄换成 /
  • 错误被掩盖:上游调用方不会修正入参,系统长期维持不一致状态。
  • 多 root 场景反噬:当 Buck root 本就不在 / 时,fallback 可能把正确请求变成错误行为。

8.2 “严格要求 mount_path 是 Buck2 root”会不会太硬?#

会更”严格”,但它的收益是工程化的:

  • 失败可行动:错误信息明确告诉你”传参错了”,而不是让你猜 mount 发生了什么。
  • 契约可维护:未来新增多 repo / 多 Buck root,只需要扩展契约,而不是依赖隐式兜底。
  • 可观测性更强:可以统计 .buckconfig missing 的比例、来源,推动上游修正。

结论:在构建系统这类”正确性优先”的场景里,宁可明确失败,也不要静默成功但语义错乱


9. 坑与教训(可迁移方法论)#

9.1 坑 1:把”repo/path”当万能字段,迟早会炸#

这次事故的本质不是 Buck2,也不是 Antares,而是契约设计含混

  • 一个字段同时承载”仓库标识 / 子目录路径 / Buck root”三种语义,必然在某个环节被误用。

9.2 坑 2:错误从 stderr 泄漏出来,定位成本指数级上升#

没有 mount 后预检时:

  • Buck2 失败表现为 Error creating cell resolver
  • 你很难从 API 层第一时间判断是”挂载视图裁剪”还是”仓库缺文件”。

预检与结构化输出本质是在做:

  • 把不可控失败前移为可控失败(可解释、可行动)。

9.3 坑 3:验证链路本身也需要工程化(网络/代理/平台)#

这次验证的最大阻力来自”环境不确定性”:

  • Docker daemon 与 shell 的代理配置不同步;
  • 镜像源 DNS 不稳定;
  • 镜像 manifest 与平台不一致。

经验:对 infra-heavy 的系统,“可重复验证”本身就是功能的一部分


10. 后续行动项(P0/P1/P2)#

P0(必须做/尽快)#

  • 把契约写死在文档与错误信息里mount_path 必须是 Buck2 project root(包含 .buckconfig)
  • 上线后证据闭环:日志/指标记录:
    • mount_pathmount_point
    • .buckconfig 是否存在
    • buck2 cells readiness 成功/失败原因

P1(强烈建议)#

  • 补最小 E2E 回归:staging 每日定时跑一次 “mount + buck2 cells + targets”。
  • 字段拆分提案(从根上消灭歧义):
    • buck_root(必须包含 .buckconfig
    • work_subdir(可选:业务要操作的子目录)

P2(优化方向)#

  • Cargo/Docker 的网络与缓存治理
    • cargo:代理/镜像源的标准化配置与 CI 缓存
    • docker:daemon proxy/mirror 的标准化与平台镜像策略(manifest)

11. 附录#

11.1 关键信息速查#

  • 基础 commitff49ba8365e2a4529f539f1327ad796c4e20090f
  • 修复分支fix/buck2-mount-root
  • 关键提交(节选)
    • dec83222a:止血修复(强制挂 /
    • 93676c509:语义一致化改造落地后的主提交(见 transcript 记录)
    • 92da7e8b0:修复 BuildDTO::from() 的 move/clone 编译问题(测试过程中发现)

注:提交号以 transcript 记载为准;若需要在最终 PR 描述中对齐”精确 commit 列表”,建议用 git log --oneline --decorate 再做一次确认与补全。

11.2 相关参考#


12. 补充:更深层的坑与方法论#

本节是对第 9 节”坑与教训”的进一步展开,补充更多调试过程中的细节和可迁移经验。

12.1 意外发现的编译 Bug:BuildDTO::from() move/clone#

在单元测试阶段,发现了一个与本次修复无直接关系、但被重构触发的编译问题:

// 原代码(编译失败)
impl From<BuildModel> for BuildDTO {
    fn from(model: BuildModel) -> Self {
        Self {
            // ... 其他字段 ...
            mount_path: model.repo,  // model.repo 被 move
            // ... 后面又用了 model.xxx ...  ← 编译错误:value borrowed after move
        }
    }
}

// 修复后(commit 92da7e8b0)
impl From<BuildModel> for BuildDTO {
    fn from(model: BuildModel) -> Self {
        Self {
            // ...
            mount_path: model.repo.clone(),  // 显式 clone,避免 move
            // ...
        }
    }
}
rust

教训:重构字段名时,要关注所有使用该字段的 impl FromIntoClone 等 trait 实现,Rust 的 move 语义会在意想不到的地方咬你。

12.2 Docker 环境踩坑全记录#

这次 E2E 验证的环境问题值得单独记录,因为每一步都是”以为能跑却跑不动”:

阶段期望实际解决方案
镜像拉取docker compose up -d 直接跑Docker Hub 超时(30s 无数据)-
加速器 1配置中科大镜像 mirrors.ustc.edu.cnDNS 解析失败-
加速器 2换网易镜像 hub-mirror.c.163.com同样 DNS 失败-
直连诊断curl registry-1.docker.io发现系统有代理 127.0.0.1:7897,但 Docker 没用它配置 Docker daemon proxy
代理配置为 Docker systemd 添加 HTTP_PROXY配置生效,镜像拉取成功
端口冲突容器启动6379 端口被占用清理旧容器/重启 Docker
镜像 manifest运行服务某些镜像不包含当前平台 manifest采用”Compose 依赖 + 本地服务”折中
🔧 Docker 代理配置命令备忘(点击展开)
# 1. 创建 Docker systemd 代理配置
sudo mkdir -p /etc/systemd/system/docker.service.d
sudo tee /etc/systemd/system/docker.service.d/http-proxy.conf << 'EOF'
[Service]
Environment="HTTP_PROXY=http://127.0.0.1:7897"
Environment="HTTPS_PROXY=http://127.0.0.1:7897"
Environment="NO_PROXY=localhost,127.0.0.1,172.16.0.0/12,192.168.0.0/16,10.0.0.0/8"
EOF

# 2. 重载配置并重启 Docker
sudo systemctl daemon-reload
sudo systemctl restart docker

# 3. 验证代理配置
docker info 2>&1 | grep -A 2 -i proxy
bash
🔧 Cargo 代理配置命令备忘(点击展开)
# 方法 1:环境变量(临时)
export HTTP_PROXY=http://127.0.0.1:7897
export HTTPS_PROXY=http://127.0.0.1:7897
export ALL_PROXY=http://127.0.0.1:7897
export CARGO_HTTP_TIMEOUT=600  # 增加超时时间

cargo fetch  # 先拉依赖
cargo check -p orion -p orion-server

# 方法 2:Cargo 配置(永久)
# ~/.cargo/config.toml
[http]
proxy = "http://127.0.0.1:7897"
timeout = 600

[net]
git-fetch-with-cli = true
bash

教训:WSL 环境下,宿主机代理和 Docker daemon 代理是两套配置,需要分别处理。Shell 能用代理不代表 Docker 能用。

12.3 假设-验证循环的详细展开#

本次调试中,假设-验证循环是定位根因的关键方法:

┌─────────────────────────────────────────────────────────────────────────┐
│                        假设-验证循环                                     │
├─────────────────────────────────────────────────────────────────────────┤
│                                                                         │
│  假设 H1: .buckconfig 在仓库根,但 mountpoint 看不到它                   │
│    │                                                                    │
│    └─ 验证方法: 检查 Buck2 stderr                                        │
│       └─ 证据: "Expected to find a .buckconfig file" ✓                  │
│                                                                         │
│  假设 H2: mountpoint 看不到是因为 mount 传参是子目录视图                  │
│    │                                                                    │
│    └─ 验证方法: 读取 scorpio/antares.rs normalize_mount_path()           │
│       └─ 证据: Antares 确实支持"子目录视图裁剪" ✓                        │
│                                                                         │
│  假设 H3: 强制挂载 "/" 能解决问题                                        │
│    │                                                                    │
│    └─ 验证方法: commit dec83222a 后重新测试                              │
│       └─ 证据: Buck2 不再报错 ✓                                          │
│       └─ 但引入新问题: 语义变化,需要继续迭代                             │
│                                                                         │
│  假设 H4: 移除 fallback、增加预检、明确报错能兼顾鲁棒性和语义一致性       │
│    │                                                                    │
│    └─ 验证方法: 单元测试                                                 │
│       └─ 证据: orion 43/43 + orion-server 6/6 全过 ✓                    │
│                                                                         │
└─────────────────────────────────────────────────────────────────────────┘
plaintext

教训:不要跳过假设直接改代码。每一步修改都应该有明确的假设和验证证据。“改对了”和”知道为什么改对了”是两回事。

12.4 原始错误日志(供复现参考)#

Error getting build targets: Fail to get cells: Buck2 stderr: Command failed: Error creating cell resolver 

Caused by: 

    Couldn't find a buck project root for directory 
    `/tmp/scorpio-megadir/antares/mnt/10916897-2fd6-4f5b-9218-3fc2618b349f`. 
    Expected to find a .buckconfig file.
text

关键信息解读:

  • UUID 路径10916897-2fd6-4f5b-9218-3fc2618b349f 是 Antares 为每次挂载生成的唯一标识
  • 路径层级/tmp/scorpio-megadir/antares/mnt/ 是 Scorpio FUSE 的标准挂载点前缀
  • 错误触发点Error creating cell resolver 表明失败发生在 Buck2 初始化阶段,而非构建阶段

12.5 单元测试结果详情#

┌─────────────────────────────────────────────────────┐
│              测试验证结果                            │
├─────────────────────────────────────────────────────┤
│                                                     │
│  orion (build agent)                                │
│  ─────────────────────                              │
│  $ cargo test --lib                                 │
│  running 43 tests                                   │
│  test result: ok. 43 passed; 0 failed               │
│                                                     │
│  覆盖范围:                                          │
│  - MountGuard 生命周期                               │
│  - normalize_mount_path() 边界情况                   │
│  - Antares FS mount/unmount                         │
│  - Buck2 targets 解析                               │
│                                                     │
├─────────────────────────────────────────────────────┤
│                                                     │
│  orion-server (API 层)                              │
│  ─────────────────────                              │
│  $ cargo test --lib                                 │
│  running 6 tests                                    │
│  test result: ok. 6 passed; 0 failed                │
│                                                     │
│  覆盖范围:                                          │
│  - TaskRequest 序列化/反序列化                       │
│  - BuildDTO 字段映射                                 │
│  - API 重试逻辑                                      │
│                                                     │
└─────────────────────────────────────────────────────┘
plaintext

13. 思考:从这个案例能学到什么?#

13.1 关于”止血”与”根治”#

  • 止血:快速恢复系统可用性(强制挂载 /
  • 根治:消除隐患、统一语义、增加预检

两者不矛盾,但止血不能替代根治。止血是为了争取时间,根治才是真正解决问题。

13.2 关于”隐式兜底”#

“隐式 fallback”看似友好,实则是技术债的温床

  • 掩盖了上游的错误
  • 让系统行为难以预测
  • 在边界场景会反噬

构建系统这类”正确性优先”的场景,宁可明确失败,也不要静默成功但语义错乱

13.3 关于”可重复验证”#

这次 E2E 验证受阻的主要原因是环境不一致

  • 代理配置不同步
  • 镜像源不可用
  • 平台/manifest 不匹配

对 infra-heavy 的系统,“可重复验证”本身就是功能的一部分。没有可靠的验证环境,修复就无法闭环。


文档更新日期:2026-01-23 作者:基于 agent transcript 自动生成(待优化)

Orion × Buck2:mount_path 语义统一与验证闭环的深度复盘
https://jerry609.github.io/blog/orion-buck2-mount-path-postmortem
Author Jerry
Published at January 23, 2026
Comment seems to stuck. Try to refresh?✨