Skip to content

TSI-4320: test(acceptance): 补齐恢复类验收的可执行载体 - #162

Merged
tsic404 merged 1 commit into
mainfrom
multica/TSI-4320
Oct 26, 2026
Merged

tsic404 merged 1 commit into
mainfrom
multica/TSI-4320

Conversation

@multica-tsic404

@multica-tsic404 multica-tsic404 Bot commented Oct 25, 2026 •

Copy link
Copy Markdown
Contributor

Closes TSI-4320

Issue: https://multica.tsic.top/issues/01a172d1-afe8-7857-befe-0f843da537ec

设计依据

tsic/torrentfs-design@5b622b6

本批引用的 AC-81..84、C-9.2、C-9.3、C-1.2、C-2.3.7、C-1.3.4、C-10.7/10.8、C-5.2.2、C-11.6 在 5b622b6..HEAD 区间只有拆行与版式变化,语义未变,故仍以 5b622b6 判定。本 PR 无 D1–D4 设计变更。

设计实现

设计要求 代码落点
03-acceptance §I TC-recovery-02 / AC-81(进入、列出、递归列出、查找秒级返回,输出完整,不挂起) tests/acceptance/test_tc_recovery_02.rs · tc_recovery_02_large_directory_answers_within_seconds:4000 文件多文件种子(根 3000 + 20 目录 × 50);进入/列出/递归/查找各自计时,列表集合全等 3020 项,递归 20 目录 / 4000 文件;预算 2s / 5s / 5s
02-contracts §C-11.6 / §C-2.5.6(复合索引解析、偏移分页不重不漏) test_tc_recovery_02.rs · 列表集合与递归条目数全等断言 + 单次 listing / 整树递归计时
02-contracts §C-1.3.4(目录项与属性缓存 1 秒) test_tc_recovery_02.rs · 计时前等过 1s 有效期(+200ms 余量),使「进入」成为真实 LOOKUP 而非 dcache 命中
02-contracts §C-2.5.3(投递窗口的临时投影,落库后切换真实目录树) test_tc_recovery_02.rs · wait_for_file_table:有界等待落库后的文件表,再在稳定形态上测量
03-acceptance §I TC-recovery-03 / AC-82(SIGTERM → 退出码 0、无残留挂载、缓存元数据落盘) tests/acceptance/test_tc_recovery_03.rs · tc_recovery_03_sigterm_exits_zero_and_persists_cache_metadata
02-contracts §C-9.2.2 / §C-9.2.3 / §C-1.2.1(关停顺序、正常关停退出 0) test_tc_recovery_03.rs · 先经挂载读出一片再 SIGTERM,wait_for_exit 断言 status.code() == Some(0)
02-contracts §C-5.2.2 / §C-5.2.4(停机为权威落盘点;不对存活 daemon 作单次即时断言) test_tc_recovery_03.rs · 进程退出后读 <cache>/cache_metadata.txt,断言 <info_hash>:piece:0 存在且 piece 大小 = 16384
03-acceptance §I TC-recovery-04 / AC-83(存在读取摘要行、无逐 piece 刷屏、异常路径告警) tests/acceptance/test_tc_recovery_04.rs · is_read_summary / mentions_piece_index / is_read_warning 按行形态判定;32 片夹具下逐 piece 行 ≤ 3
02-contracts §C-2.3.7(无做种者/缓存无法服务必须是 ENODATA) test_tc_recovery_04.rs · 空 swarm 读断言 raw_os_error() == Some(libc::ENODATA)(EIO、重试耗尽的 ENOENT 不再算异常路径成立)
03-acceptance §I TC-recovery-05 / AC-84(切断 → 104;入口恢复 ≤3 次、每次退避 1s、日志行带序号;第 4 次 104;外部卸载 102 不重启) tests/acceptance/tc_recovery_05.sh · A 段(daemon 外部卸载 → 102)、B 段(rootful 入口外部卸载 → 入口 102、无重启行)、C 段(C1 切断 → daemon 104;C2 连续四次切断 → recovery 1/3..3/3、退避、第 4 次入口 104)
02-contracts §C-9.3.1 / §C-1.2.7 / §C-1.2.8(挂载仍附着 → 104;已不在挂载表 → 102) tc_recovery_05.sh · A 段 fusermount -u 后断言 102 且 mountinfo 无残留;C1 段 abort 后断言 104
02-contracts §C-10.7(仅 104 且已用次数 < 3 才重启;每次退避 1 秒;日志行指明恢复序号;第 4 次 104) tc_recovery_05.sh · C2 段逐次匹配 recovery N/3;退避 = 恢复行时刻 → 重启进程的 /proc/<pid>/stat starttime 换算墙钟,断言 900ms..6s;第 4 次断言入口 104 且重启行恰 3 条
02-contracts §C-10.8(外部卸载 102 不自动重启,入口透传 102) tc_recovery_05.sh · B 段断言入口退出 102 且日志无 restarting torrentfs
03-acceptance 头部「编号冻结,可执行形态见 tests/acceptance/<用例编号>」 tests/acceptance/main.rs · 三个 mod、LANDED 三条、SCRIPT_FORM 一条(纯追加)
01-scope 禁项:不改产品行为 未改 src/;git diff --stat origin/main...HEAD -- src/ 为空

tests/acceptance/common.rs 的进程级夹具(Daemon、mountinfo_fuse_count、read_when_ready、ingest_torrent、wait_for_projection、wait_for_path、daemon_config)复用既有 TestHarness / MiniTracker / 做种端;tests/acceptance/lib_fuse_harness.sh 上提 fuse_minor_at / wait_for_fuse 两个共享函数,本脚本与 ci/tests/session_recovery_e2e.sh 各取一份,不再各写一套 mountinfo 解析。

判据强度

  • AC-81:「进入」是过期内核缓存(C-1.3.4 的 1s,留 200ms 余量)后的首次 LOOKUP,「查找」是首次按名解析(READDIR 不实例化目录项);测量前有界等待落库后的文件表,跳过 C-2.5.3 的临时态,不跳过任何断言。四个操作各自计时,列表集合全等、递归条目数全等,不以「退出 0」替代。
  • AC-82:退出码 0、mountinfo 无 fuse 条目、停机后读到的缓存元数据记录 piece:0 且 size 正确,三条逐条断言。
  • AC-83:行形态判定(摘要行 ≥1、逐 piece 行 ≤3、失败读必须留 WARN/ERROR);异常路径锁 ENODATA 本身,而不是「任意 errno」。冷读前用 .stats 的 Seeds: 确认有源,把「有源」作为前置条件而不是判据。
  • AC-84:退避只测退避——恢复行时刻与重启进程 starttime 的差值,下限 900ms 只由退避构成(不再混入 force_unmount、挂载与入口轮询),上限 6s 只约束退避加卸载;恢复计数、第 4 次 104、外部卸载 102 不重启逐项断言。
  • 环境判定:MountGate::Environment 只认「挂载阶段才有的退出码(2/3/100)+ 只有挂载尝试才会写出的行」;user_allow_other 是 mount 之前的无条件告警,单独不再构成 SKIP。tc_recovery_05.sh 的 mount_unavailable 采用同一张表。

CI 接线

四条载体此前在 CI 中一条都不执行;现在:

  • entrypoint-test 的 bash -n 清单加入 tests/acceptance/tc_recovery_05.sh;
  • acceptance-fuse-e2e(debian:sid-slim + --device /dev/fuse --cap-add SYS_ADMIN)的 P0 循环加入 tc_recovery_05,并新增 cargo test --locked --release --test acceptance -- tc_recovery_0 步骤执行三个 Rust 载体(cargo test 作业没有 /dev/fuse,它们在那里自行 SKIP);
  • 同一作业按 session_recovery_e2e 的先例创建 uid 1000 的 daemon 用户——rootful 入口会 setpriv 到 TORRENTFS_UID,fusermount3 需要该 uid 能过 NSS。

验证

已 rebase 到 origin/main(670d95d)后重跑。环境:debian:sid-slim + --device /dev/fuse --cap-add SYS_ADMIN --security-opt apparmor=unconfined 容器,root,容器内 cargo build --locked --release(与 acceptance-fuse-e2e 同镜像同参数)。

# rootful: ./tests/acceptance/tc_recovery_05.sh ./target/release/torrentfs
[TC-recovery-05] external unmount → daemon exit 102
[TC-recovery-05] entrypoint external unmount → exit 102, no restart
[TC-recovery-05] severed session → daemon exit 104
[TC-recovery-05] recovery 1/3: daemon restarted 972ms after the recovery line
[TC-recovery-05] recovery 2/3: daemon restarted 1389ms after the recovery line
[TC-recovery-05] recovery 3/3: daemon restarted 1401ms after the recovery line
[TC-recovery-05] 3 recoveries then exit 104 on the fourth severed session
TC-recovery-05: PASS (102 final on external unmount; 104 recovery budget, 1s backoff, exit 104 on budget exhaustion)
exit 0

# 非 root(uid 1000)
[TC-recovery-05] external unmount → daemon exit 102
[TC-recovery-05] entrypoint external unmount → exit 102, no restart
TC-recovery-05: SKIP — severing a FUSE connection needs root (writes /sys/fs/fuse/connections)
exit 2
  • cargo test --locked --release --test acceptance -- registry_tests:4 passed / 0 failed(67 filtered out)。
  • cargo test --locked --release --test acceptance -- tc_recovery_0:3 passed / 0 failed(55.32s)。
  • bash -n:tc_recovery_05.sh、lib_fuse_harness.sh、session_recovery_e2e.sh 均通过;git diff --check 干净。
  • git merge-tree --write-tree origin/main HEAD 返回单树、退出码 0;GitHub 侧 mergeable=MERGEABLE。

环境限制

本轮没有在宿主直跑:宿主非 root、fusermount 非 setuid,rootful 两段根本起不来。上表全部在容器内以 root 实跑,rootful 分支(B 段、C2 段)与 C 段都已执行;非 root 用例另行实跑,确认它如实 exit 2 而不是静默通过。

观察(未改产品行为)

同一份多文件种子在 data/<filename>.torrent 下的形态随 C-2.5.3 的窗口变化:临时态直接列出文件表(扁平,含 3000 文件 + 20 目录),落库后切换为「以种子名命名的根目录」。载体因此有界等待落库后的稳定形态再测量,未把临时态的形状写进判据;两种形态的取舍属产品语义,不在本批范围。

Summary by Sourcery

Add executable acceptance coverage for recovery behavior and wire it into FUSE-enabled CI.

New Features:

  • Add executable acceptance coverage for recovery cases covering large-directory responsiveness, graceful shutdown persistence, and read logging/error behavior.
  • Add a shell acceptance case covering external unmount handling, FUSE session recovery limits, backoff timing, and exit statuses.

Enhancements:

  • Centralize process-level daemon and FUSE mount test helpers, including bounded waits, environment gating, cleanup, and projection handling.
  • Register the new recovery acceptance carriers and make their assertions validate complete results and contract-specific outcomes.

CI:

  • Run the new Rust recovery tests and shell recovery case in FUSE-enabled CI, including required daemon-user setup and syntax validation.
  • Reuse shared FUSE mount probing helpers in the session recovery end-to-end test.

Tests:

  • Expand acceptance coverage for TC-recovery-02 through TC-recovery-05 without changing product source behavior.

@sourcery-ai

sourcery-ai Bot commented Oct 25, 2026

Copy link
Copy Markdown

Sorry @multica-tsic404[bot], you've used your own review budget of 250,000 diff characters for the last 7 days.

You can request another review in 23 hours and 9 minutes by commenting @sourcery-ai review. Upgrade to get a review now.

@sourcery-ai

sourcery-ai Bot commented Oct 25, 2026

Copy link
Copy Markdown

Reviewer's Guide

本 PR 仅修改 tests/acceptance,通过新增统一的真实 daemon/FUSE 测试夹具和四个恢复类验收载体,覆盖大目录索引性能、SIGTERM 关停与缓存落盘、读取日志质量,以及会话断开后的 102/104 状态和入口恢复预算;同时将用例接入验收注册表,并在不具备 FUSE 或 fusectl 能力的环境中明确跳过而不降低判据。

File-Level Changes

Change Details Files
新增真实 daemon/FUSE 级验收测试基础设施,统一管理进程、挂载、临时状态和环境跳过逻辑。
  • 增加 Daemon 进程生命周期、信号、退出状态、日志和清理封装。
  • 增加 FUSE 挂载检测、mountinfo 校验、投影等待、带超时读取及投递窗口重试辅助函数。
  • 复用既有 TestHarness、MiniTracker 和做种端,配置临时端口、缓存及发现参数。
tests/acceptance/common.rs
补齐大目录恢复性能验收,直接通过 FUSE 命名空间验证索引访问和完整输出。
  • 构造包含 4000 个文件的多文件种子并验证进入、3000 项列表、递归遍历和深层按名解析。
  • 为单次操作、列表和整树遍历设置秒级预算,同时断言结果集合和条目数量完整。
  • 验证 SIGTERM 后退出码为 0 且无残留 FUSE 挂载。
tests/acceptance/test_tc_recovery_02.rs
补齐 SIGTERM 优雅关停验收,验证挂载清理和停机权威缓存落盘。
  • 先经挂载读取一个完整文件,再发送 SIGTERM 并断言退出码为 0。
  • 进程退出后检查 mountinfo 无 FUSE 残留,并解析 cache_metadata.txt 验证 piece:0 及 16384 字节记录。
tests/acceptance/test_tc_recovery_03.rs
补齐读取日志质量验收,覆盖正常冷读、命中读和无种子异常读。
  • 按日志行形态识别读取摘要、逐 piece 日志和异常告警,而不锁定具体文案。
  • 要求存在摘要和 WARN/ERROR,同时将 32 片读取中的逐 piece 行限制为最多 3 条。
tests/acceptance/test_tc_recovery_04.rs
以 shell 载体补齐会话切断和入口恢复预算验收。
  • 验证外部卸载使 daemon 和入口分别以 102 退出且不触发重启。
  • 通过 fusectl abort 模拟挂载仍附着的会话断开,验证 daemon 以 104 退出。
  • 连续触发四次断开,断言最多三次恢复、每次带恢复序号并满足约 1 秒退避,第四次以 104 结束。
  • 对 FUSE、fusectl、权限等执行环境限制返回明确 SKIP,而不放宽验收判据。
tests/acceptance/tc_recovery_05.sh
将新增恢复验收用例接入编号冻结和载体注册表。
  • 注册 TC-recovery-02、03、04 的 Rust 模块和 LANDED 条目。
  • 注册 TC-recovery-05 的脚本载体。
tests/acceptance/main.rs

Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

@tsic404 tsic404 left a comment

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

overview

规模:6 文件 +1352/-0,全部落在 tests/acceptance/,src/ 零改动。

通过的审查维度(≥3):判据强度(AC-81..84 与断言逐条对照)、脚本与进程语义(退出码、SKIP/FAIL 边界、恢复预算)、并发与资源(wave_slot、Daemon Drop 的 lazy detach、读超时线程)、安全(root 写入面、固定路径副作用、无 eval/无未引号展开、无 CI 环境变量特判)、可维护性(注册表纯追加、夹具复用)。

其他审查者反馈:本 PR 此前无 review(reviews 为空),无需折叠他人意见。

点赞:

  • 注册表是纯追加(mod 3 条、LANDED 3 条、SCRIPT_FORM 1 条),既有 21 条载体与四道门禁未动。
  • read_when_ready 只对 ENOENT 重试、其它错误立即返回——把「投递窗口」与「真实失败」分开了;这正是重试窗口最容易掩盖失败的地方,处理是对的。
  • tc_recovery_05.sh 在 root/abort 前置不可得时 exit 2 并打印原因(C 段开头与 abort 写入处都如此),没有静默通过;C2 逐次匹配 recovery N/3、重启计数恰为 3、第 4 次入口 104,恢复预算与终态都锁住了。
  • Daemon::Drop 在断言 panic 后 lazy-detach 挂载,一个用例失败不会污染后续用例。
  • 日志判据走 strip_ansi + 行形态分类(级别 + 是否点名单个 piece),不锁死措辞。

无法锚定的发现(不在本 diff 的可锚定行上,按规则写在这里):

🟡 CI 接线缺口:ci.yml 的 acceptance-fuse-e2e 循环(P0 脚本清单)不含 tc_recovery_05.sh,entrypoint-test 的 bash -n 清单也没有它;Cargo Test 跑在 debian:sid-slim(无 --device /dev/fuse),因此三个 Rust 模块在 CI 里必然 SKIP。净结果:本批 4 条载体在 CI 中一条都不执行,唯一证据是本地 run;而 PR 自述本机非 root,所以只走了 entrypoint 的 rootless 分支——rootful 两段发布分支与整个 C 段从未执行。建议在本 PR 内把 tc_recovery_05.sh 接进 FUSE E2E job(root + /dev/fuse,需对 exit 2 放行),或单开 root job。

安全维度(显式检查):脚本无 eval、无未引号变量展开、无外部输入拼接进命令;root 写入面只有 /sys/fs/fuse/connections/<minor>/abort 与固定 /mnt-inner,前者是 C 段明示前提,后者见 cleanup 那条 inline。未发现注入或凭据类问题;也没有以环境变量特判跳过断言/放宽判据的代码(跳过全部由能力探测决定)。

结论:REQUEST CHANGES——1 项 blocking(脚本在 root 下无法通过,AC-84 至今没有能变绿的证据)、6 项 🟡、1 项 💭。

Comment thread tests/acceptance/tc_recovery_05.sh
Comment thread tests/acceptance/common.rs Outdated
Comment thread tests/acceptance/test_tc_recovery_02.rs
Comment thread tests/acceptance/test_tc_recovery_04.rs Outdated
Comment thread tests/acceptance/tc_recovery_05.sh Outdated
Comment thread tests/acceptance/tc_recovery_05.sh Outdated
Comment thread tests/acceptance/tc_recovery_05.sh Outdated
@multica-tsic404
multica-tsic404 Bot force-pushed the multica/TSI-4320 branch 3 times, most recently from fb994ac to 9aecb1f Compare October 25, 2026 20:27

@tsic404 tsic404 left a comment

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

overview

规模:9 文件 +1562/-30。行数落在 L4 区间,但改动类型是验收载体 + CI 接线,src/ 零改动(git diff --stat origin/main...HEAD -- src/ 为空),因此按判据文件分组逐条核对,不按「大 diff 直接打回」处理。

通过维度(5):判据强度(AC-81..84 与断言逐条对照)、rebase 完整性(共享追加点与同名函数解析)、CI 可执行性(用 CI 日志而不是整改自述验证)、安全(root 写入面、无 CI 环境特判)、测试判别力(假绿与假红两侧)。

未通过 2 条:1 🟡 + 1 💭,均为小改,见 inline。

上轮 8 项(7 inline + 1 💭)核对:全部收口

  1. $WORK 0700:chmod 0755 "$WORK"(tc_recovery_05.sh:37),$BIN 绝对化(:215-216)。rootful 不再靠自述——CI 日志里 A/B/C1/C2 四段全部执行。
  2. 环境判定:mount_stage_exit ∈ {2,3,100} 且行形态(common.rs:514-522),user_allow_other 已移出清单,exit 1(状态库/引擎)与 101(实例锁)不再落 SKIP。方向正确;仅 100 支的 marker 不可达(inline)。
  3. TC-recovery-02 真实 LOOKUP:CACHE_EXPIRY_WAIT = 1200ms(test_tc_recovery_02.rs:55)晚于 C-1.3.4 的 1s,fs::metadata(&torrent_root)(:261)在过期后发起真实请求;查找走 READDIR 未实例化的 d07/f0042.bin(:321)。2s/5s/5s 预算(:49,57,59)相对 AC-81 的「秒级」是宽松方向。
  4. TC-recovery-04:raw_os_error() == Some(libc::ENODATA)(:252),锁的是 errno 本身。
  5. 退避:区间只剩「恢复行 → 重启进程 /proc/<pid>/stat 第 22 字段」(tc_recovery_05.sh:311-340),挂载与 detach 都在区间外;recovery_at_ms 取在等日志之前,只会把区间估长 ~50ms(单向偏松,可接受)。CI 实测 1179 / 1194 / 1156 ms。
  6. harness 去重:fuse_minor_at / wait_for_fuse 上提(lib_fuse_harness.sh:28-52),TORRENTFS_FUSE_HARNESS_LIB_ONLY=1 在挂载前返回(:50-52);session_recovery_e2e.sh:114 用同一变量 source,两处调用改名后语义等价(30s 上限,轮询 0.2s vs 0.5s),Session Recovery E2E 绿。
  7. CI 接线(上轮唯一 🔴 的根因):bash -n 清单加 tc_recovery_05.sh(ci.yml:109);P0 循环 7→8(:601);新步骤 -- tc_recovery_0 命中 3 个用例(:612-616);uid 1000 用户照 session_recovery_e2e 先例建(:589-596)。CI 日志实证:[TC-recovery-05] external unmount → daemon exit 102 / entrypoint external unmount → exit 102, no restart / severed session → daemon exit 104 / recovery 1..3 / 3 recoveries then exit 104 / PASS,且 test_tc_recovery_02|03|04 ... ok(3 passed,无 SKIP 行)。四条载体在 CI 里都真的执行了。
  8. 💭 /mnt-inner:危险方向(无条件 umount)已消除,但守卫条件恒假(inline 💭)。

rebase 完整性

  • main.rs +7/-0:测试模块 28→31、LANDED 28→31、SCRIPT_FORM 9→10(main.rs:48-50,153-155,180)。与 origin/main 的差集只有本批 4 条,别批条目零删除、零重排。
  • common.rs +358/-0:别批的 quiet_config / service_with_state / build_multi_file_torrent / rig / ingest 与 service 级 ingest_torrent(:235)都在;本批挂载级函数改名 ingest_torrent_through_mount(:618),两个函数不同名且签名不同,别批调用点(test_tc_negative_01.rs:35,46、test_tc_negative_02.rs:149,155)仍解析到 service 级那份——不是「覆盖同名函数」的另一种写法。
  • 36deba0 → 9aecb1f 的本批文件差异只有改名与一处断言措辞,内容无丢失。

安全(显式核对)

无 eval、无未引号展开;root 写入面仅 /sys/fs/fuse/connections/<minor>/abort(C 段明示前提,minor 取自 mountinfo)与 rootful entrypoint 的固定发布路径;chmod 0755 的工作目录只含测试缓存与日志,无凭据;跳过条件只看 /dev/fuse 与 fusermount 是否存在,没有「检测 CI 环境变量即跳过断言 / 放宽判据」的写法。

其他审查者

PR 上只有本审查者上一轮的 CHANGES_REQUESTED(锚在 84e691b,已悬空),无其他审查者的变更请求需要并入。

点赞

  • 环境分类改成「退出码 ∧ 行形态」的合取,而不是为了让环境跑通去放宽断言。
  • 退避改测进程启动时刻(/proc/<pid>/stat + btime + CLK_TCK):AC-84 的 1s 第一次被单独见证,是这轮最扎实的一处。
  • 四个载体的判据都钉在可观察量(退出码、mountinfo、cache_metadata.txt 键值、errno),不是日志措辞。

无法锚定:无。

Comment thread tests/acceptance/common.rs
Comment thread tests/acceptance/tc_recovery_05.sh Outdated
@multica-tsic404
multica-tsic404 Bot force-pushed the multica/TSI-4320 branch 2 times, most recently from 0bf9378 to 65d4d88 Compare October 25, 2026 22:52

@tsic404 tsic404 left a comment

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

overview

规模:9 文件 +1581/-30(tests/acceptance/*、ci/tests/session_recovery_e2e.sh、.github/workflows/ci.yml),src/ 零改动。行数在 L4 区间,但改动类型是验收载体 + CI 接线,不含产品逻辑,按载体判据逐条审。

通过的维度(≥3):量测基准(新旧时钟逐式对照)、判据强度(AC-81..84 与断言逐条)、CI 接线实证(读 run 40242197444 的 P0 作业日志)、共享 harness 等价性、安全(root 写入面与命令构造)。

本轮时钟改动核对通过:

  • uptime_ms(/proc/uptime 第一段,1/100s 精度,+0.5 取整)与 proc_started_uptime_ms(/proc/<pid>/stat 第 22 字段 × 1000 / getconf CLK_TCK)两端同为零点=开机的同一时钟,不再拼 btime(整秒向下取整)与精确墙钟;旧式系统性偏早 0–999ms 已消除。
  • 量化误差:/proc/uptime ±5ms、starttime 10ms 向下取整、CLK_TCK=100(Linux glibc 固定 USER_HZ)⇒ 合计 ≤10ms 单向偏短,对 900ms 下限有 100ms 余量,不产生取整假绿,也不会假红。
  • pid 消失 ⇒ cat 失败 ⇒ return 1 ⇒ 显式 fail … cannot read the start time,不静默;pid 复用只在容器内极窄窗口可能,误判方向为偏晚(假红)而非假绿。
  • CI 实测 1460 / 1470 / 1480 ms 全部 > 1s,0bf9378 上那条 577ms 的红已消失,BACKOFF_MIN_MS / BACKOFF_MAX_MS 未动。

上轮 2 条 inline 按 65d4d88 复核,均已收口:

  • exit 100 分支可达:两个 marker 表都加了 Mount failed and /dev/fuse cannot be opened(common.rs:77、tc_recovery_05.sh:126),与源码 src/main.rs:960(100)、949(2)、801(3)三条独占 marker 对齐;exit 1/101 仍落 FAIL,没有退回「任何启动期失败都 SKIP」。
  • INTERNAL_MNT_USED 已移到主 shell(tc_recovery_05.sh:265-269,328-330),cleanup 的回收段真实生效(实跑后 /mnt-inner 不在 mountinfo、.torrentfs.lock 所在目录被 rmdir),且不再是「永不回收」。

本轮另两处改动核对:

  • -- tc_recovery_0 精确命中 3 个用例:全仓 Rust 载体里只有 tc_recovery_02_large_directory_answers_within_seconds、tc_recovery_03_sigterm_exits_zero_and_persists_cache_metadata、tc_recovery_04_debug_logging_stays_lean_during_reads 含该串(TC-recovery-01 是脚本形态,无同名 Rust 载体);CI 日志 3 passed; 81 filtered out + 三条 … ok 实证执行,无 SKIP 行。uid 1000 用户的建法与 session_recovery_e2e 作业同款(ci.yml:541-547 vs :600-607)。
  • fuse_minor / wait_for_fuse_mount 上提:fuse_minor_at 的 awk 与旧实现逐字节等价,wait_for_fuse 保留 30s 上限(轮询 0.5s→0.2s),TORRENTFS_FUSE_HARNESS_LIB_ONLY=1 在挂载前 return(lib_fuse_harness.sh:58-60),两处调用点改名后语义不变,Session Recovery E2E 绿。

其他审查者反馈:无外部 CHANGES_REQUESTED(两条 review 都出自本账号前两轮)。

无法锚定的发现:无。

发现

  • 🟡 tests/acceptance/tc_recovery_05.sh:341 —— 退避窗口的起点仍在恢复行之前约 460ms,900ms 下限实际只覆盖 ~440ms 的真实退避(详见该行 inline)。
  • 💭 tests/acceptance/tc_recovery_05.sh:250 —— /mnt-inner 占用守卫只在根分支执行,非 root 跑时仍可能选中别人的挂载(详见该行 inline)。

做对的,勿因整改丢失:两侧时钟统一到开机时钟并把偏早写成注释事实;recovery_at_ms 的取帧方向选「只偏长」;INTERNAL_MNT_USED 移到主 shell 后 cleanup 的守卫语义与注释一致;注册表纯追加(main.rs mod/LANDED/SCRIPT_FORM 各 3/3/1 条,既有 21 条载体未动);四道门禁未改;marker 表与源码退出码一一可达;-- tc_recovery_0 在无 /dev/fuse 的 Cargo Test 作业里只静默跳过(由 FUSE 作业负责真跑,日志实证)。

安全维度(显式核对):无 eval;$(cat …/children) 仅做 pid 分词;路径全部引号化;root 写入面仍只有 /sys/fs/fuse/connections/<minor>/abort(C 段前置写明)与 /mnt-inner(清理受 INTERNAL_MNT_USED 收口);新增 CI 步骤只建本地用户、不引入 secret;tests/acceptance/ 与 ci/tests/ 无任何按 CI 环境变量跳过或放宽判据的分支(grep 无命中)。

Comment thread tests/acceptance/tc_recovery_05.sh Outdated
Comment thread tests/acceptance/tc_recovery_05.sh

@tsic404 tsic404 left a comment

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

overview

规模:9 文件 +1612/-30(本轮 delta 仅 tests/acceptance/tc_recovery_05.sh +54/-23),src/ 0 文件。改动类型:验收载体判据修正 + CI 接线,不含产品逻辑。

通过的维度(≥3):量测路径(取戳点与窗口构成的代码级核对)、边界与失败面(children 空/不可读、超时分支)、环境分类(ROOTFUL 定值时机与两段入口复用)、注释↔断言一致性、安全。

上轮 2 条 inline 按 5c9709d 复核,均已收口:

  • 区间起点:wait_for_daemon_exit(tc_recovery_05.sh:172-182)在「第一个没有 torrentfs 子进程」的那一轮就地取 uptime_ms,调用点是 exit_at_ms="$(wait_for_daemon_exit 30)"(:371)——取值发生在同一轮迭代、同一命令替换内,wait 返回之后没有任何补读。窗口 = 前一 daemon 退出 → 替代进程 exec,daemon 关停(src/main.rs:503-530)与恢复行打印都在窗口开之前,重启后的 mount/republish 在 starttime 之后。CI 三次 1000/1000/1010ms 与该口径自洽(若 stamp 晚 40ms 就会读到 960ms)。
  • 负对照按代码路径成立:窗口内只有 detach + sleep + exec,0.5s 的 SESSION_RECOVERY_BACKOFF_SECS 必然测到 ~500ms < 900ms 下限。该实验是 Dev 自建副本,我没有复跑(本角色不跑测试),此处只确认判据本身能分辨。
  • /mnt-inner 守卫:占用检查提到根判定之外(:282-284),且 daemon_mountpoint 只在 ROOTFUL=1 且固定路径确有 FUSE 挂载时才选它(:234-240)——非 root 路径回落到 $ENTRY_MNT,INTERNAL_MNT_USED 保持 0,别人的挂载既不被 fusermount -u 也不会被 cleanup 回收。

你点名的四处边界,逐条核过(均无问题):

  1. 取戳点确实在「daemon 子进程消失」那一轮,不是窗口外的补读(见上)。
  2. children 在入口已 wait 回收时:文件存在但为空 → for 空转 → return 1 → 就地取戳,这正是「daemon 已消失、入口尚未打印恢复行」的正确时刻。入口自身退出导致 children 不可读时同样立即取戳,随后 wait_for_log 30s 超时 FAIL,不静默。超时分支 return 1 经 exit_at_ms="$(…)" || fail "…still running 30s after the abort" 落到 FAIL(set -e 下赋值 + || 合法,不会落空)。
  3. ROOTFUL 在 B 段开头定值(:272-276),C2 在同一 shell、同一 uid 起第二个入口,值不变;daemon_mountpoint 另加「固定路径上真有 FUSE 挂载」这一条件,所以即使 ROOTFUL=1 而入口实际走了 rootless 分支(容器 root + rootless podman),也会回落到 $ENTRY_MNT,不会误认别人的挂载。
  4. :348-353 的注释与 :366-386 的断言一致:窗口含 detach + 退避 + exec,不含被 abort 的 daemon 的关停、不含重启后的挂载;上轮被点的两句(「detach 在区间外」「最多迟一个轮询」)已删除,~90ms 余量与 10ms 轮询/10ms tick 的说法对得上。

其他审查者反馈:无外部 CHANGES_REQUESTED(历史两条 review 均出自本账号)。

无法锚定的发现:无。

发现

  • 💭 tests/acceptance/tc_recovery_05.sh:173 —— local timeout="${1:-30}" 声明后从未读取,deadline 用的是 ${1:-30}(详见该行 inline)。这是本轮唯一发现,属一行改动的清理;判据本身不需要再动。

做对的,勿因整改丢失:取戳点从「abort 之后」挪到「daemon 消失」,把关停排除在窗口外并同步改写注释;wait_for_daemon_exit 超时即 FAIL;ROOTFUL 单一来源同时约束选点与守卫,rootless 与 rootful 的负向路径都保留了实跑证据;:298 的诊断行让「选了哪个点」可从日志直接判定;BACKOFF_MIN_MS/BACKOFF_MAX_MS 未动;CI 由 FUSE 作业真跑四条载体(3 passed + 三行 recovery N/3)。

安全维度(显式核对):无 eval;$(cat …/children) 仅做 pid 分词;路径全引号化;root 写入面仍只有 abort(C 段前置写明)与受 ROOTFUL/INTERNAL_MNT_USED 双重收口的 /mnt-inner;非 root 路径不再触碰别人的挂载;无按 CI 环境变量跳过或放宽判据的分支(tests/acceptance/、ci/tests/ grep 无命中)。

Comment thread tests/acceptance/tc_recovery_05.sh Outdated
恢复批次的四个用例此前只有设计条目、没有可执行形态,大目录列出、
SIGTERM 优雅停止、debug 日志精简与会话切断恢复因此无法自动判定。

新增三个 Rust 模块与一个脚本:分别驱动真实守护进程的挂载面命名空间
操作、退出码与缓存元数据落盘、日志行形态,以及容器入口的恢复预算与
外部卸载终态。判据复用既有 harness 与注册表,不新增也不放宽。

计时只测被测对象:进入目录取过期内核缓存后的真实往返,恢复退避以
重启进程的启动时刻为准。四条载体接入带 FUSE 设备的 CI 作业,无法
挂载或缺少 root 的环境自行跳过并写明原因。

@tsic404 tsic404 left a comment

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

overview

规模:9 文件 +1613/-30(相对 origin/main@46d6360),src/ 0 文件。改动类型:验收载体 + CI 接线 + 一次 rebase 并集解。相对上一轮 head 5c9709d,本批内容只有 wait_for_daemon_exit 的两行清理(:172-174)。

通过维度:rebase 并集正确性(证明级核对)、CI 接线(main 新 rc=$? 循环)、判据四处复核、注释↔断言一致性、安全。零发现。

rebase 并集:按 diff 判定,不按符号名

git diff origin/main..5bc20b6 -- tests/acceptance/common.rs = +359 / -0。零删除即证明 main 上那份 Rig::with_database / reopen / IngestHarness 重构的正文一字未动,本批只在其尾部追加 daemon 夹具,并补三行 import(fs、path、process;BTreeSet 是 main 原有且被 :638 使用)。逐条核过你点名的面:

  • Rig 两条构造路径仍在:rig() → Rig::with_database(:102-106)、IngestHarness::with_file_db → Rig::with_database(:400-403)、reopen 供 remount 用(:122-139、:409-425)。
  • import 全为并集且都被使用:main 的 BTreeSet(:638)、DirEntry(:514);本批 Child/Command/ExitStatus/Stdio(:759,788,834,898,922)。无重复顶层定义(^(pub )?(fn|struct|enum|const|mod) 去重为空)。
  • marker 表两处一致:common.rs:861-867 与 tc_recovery_05.sh:126 为同五条(含 Mount failed and /dev/fuse cannot be opened),退出码集合仍为 {2,3,100}。
  • 本批 359 行窗口夹具(Daemon/MountGate/fuse_mount_blocker/mountinfo_fuse_count/ingest_torrent_through_mount/read_when_ready/wait_for_projection/wait_for_path/daemon_config)全部在位,ingest_torrent_through_mount 与 main 的 service 级 ingest_torrent(:265)仍是两个函数,没有互相覆盖。

CI 接线

tc_recovery_05 落在 main 的新循环里(ci.yml:618,列表尾部追加),没有回退成旧的 set -euo pipefail 直跑式:rc=$? + rc=2 → ::warning:: + rc≠0 → ::error:: 并 exit(:619-626)原样保留,tc_ingest_03/06 与其余 7 条都在;bash -n 清单加的是我们自己那行(:111);Create the daemon user 只出现在 FUSE 作业内(:604-610),与 session-recovery 作业那份(:544-549)分属不同容器,无重复建用户。

判据四处(上轮结论在本 head 上未被 rebase 改动)

5c9709d..5bc20b6 里 tc_recovery_05.sh 的 delta 只有 :173-174 两行,其余逐字节不变,四处按新行号复核:

  1. 取戳点 wait_for_daemon_exit(:172-183):uptime_ms 在 entry_daemon_pid 首次失败的那一轮就地取,:372 的 exit_at_ms="$(wait_for_daemon_exit 30)" 是同一命令替换内的值,wait 返回后无补读。
  2. children 边界:空文件 → for 空转 → return 1 → 就地取戳;不可读同理;超时 return 1 经 || fail(:373)落 FAIL。
  3. ROOTFUL 在 B 段开头定值,C2 同 shell/同 uid 复用;daemon_mountpoint 另要求固定路径上确有 FUSE 挂载。
  4. 注释 :349-354 与断言 :367-389 一致(窗口 = daemon 退出 → 替代进程 exec)。

CI 实证(head 5bc20b6,run 40250891265)

15 项 check 全 SUCCESS(Sourcery review SKIPPED),headSha = 5bc20b6。FUSE 作业日志:recovery 1/3: 990ms / 2/3: 1000ms / 3/3: 1010ms,TC-recovery-05: PASS,循环里没有 ::warning::tc_recovery_05(即真跑而非 SKIP);三个 Rust 载体 ok. 3 passed; 0 failed(91 filtered out,含别批新增用例)。990ms 这条比 1s 低 10ms,正是 10ms tick 取整的量级,与 ~90ms 余量的说法一致。

其他审查者反馈:无外部 CHANGES_REQUESTED(历史四条 review 均出自本账号)。

无法锚定的发现:无。

做对的:并集按「main 的正文不动、本批只追加」解,因此 sizeof 级核对就能否证「语义被动过」;CI 循环换成 main 的新跑法而不是把旧式加回来;一行清理按同文件三处参照式样对齐(:92-93、:103-104、:213-214)。

安全维度(显式核对):无 eval;$(cat …/children) 仅做 pid 分词;路径全引号化;root 写入面仍只有 abort(C 段前置写明)与受 ROOTFUL/INTERNAL_MNT_USED 双重收口的 /mnt-inner;CI 新增步骤只建本地用户、无 secret;tests/acceptance/、ci/tests/ 无按 CI 环境变量跳过或放宽判据的分支。

@tsic404
tsic404 merged commit d2f756e into main Oct 26, 2026
15 checks passed
@tsic404
tsic404 deleted the multica/TSI-4320 branch October 26, 2026 03:28
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant