Skip to content

fix(push): read procfs entries whole to avoid read buffer cross-talk - #61

Merged
LucaLin233 merged 3 commits into
mainfrom
luno/fix-flakes
Oct 24, 2026
Merged

LucaLin233 merged 3 commits into
mainfrom
luno/fix-flakes

Conversation

@LucaLin233

Copy link
Copy Markdown
Owner

问题

tests/test-push.sh 在 Debian 13 上约 1/10 偶发失败:

❌ active worker 身份不可验证,停止调度并交由完整 grace 清理: pid=... expected_start=... actual_start=... state=R uid=0
❌ batch lifecycle failure,停止汇总与交互重试
FAIL: partial failure must be ordinary transfer failure, not lifecycle/accounting failure

存活 worker 被父进程 prune_active_workers 误判为身份不可信,BATCH_WORKER_FAILED=true 后整批提前终止。

定位

  • 失败样本中 /proc/<pid>/stat 可读、fd 目标正确、位置为 0,但 read_process_stat 连续拿到的是 status 文件的行(Gid: / FDSize: / Groups: / …),格式校验失败,PROC_START 保留上一次读取的值。
  • 旧实现用 exec {fd}</proc/...; read -r ... <&fd 逐行读取;read_process_uid 读取 status 时提前 break,未消费的缓冲会在后续 stat 读取中被取出。
  • 对照(同 churn 负载):旧读取模式 3000 轮 2 次 UID-MISS;整文件读取 3000 轮 0 次。
  • 触发条件未完全闭合到 Bash 内部状态;本仓库验证环境为 GNU bash 5.2.37。

修改

  • read_process_stat / read_process_uid 改为整文件读取,避免逐行 read 的跨读取缓冲路径。
  • $(<file) 在 Bash 5.2 下打开失败可能绕过 if/|| 终止 shell,故读取期间按 $- 快照临时关闭 errexit,读完恢复。
  • 身份校验(UID、starttime、state、SID/PGID、PID 复用判定)一律未放宽;失败仍然返回非零。

验证

  • 原实现基线:tests/test-push.sh 10 次串行 1 次失败;失败样本与插桩日志见 hkhkt /root/workdir/minis/flake-20261024/logs/target-2249/(诊断环境,未入库)。
  • 修复后:tests/test-push.sh 连续 20 次 rc=0,每次 342 PASS,77.7–78.9 s(基线 76–77 s)。
  • 负控:旧读取模式 3000 轮 2 次 UID-MISS;整文件读取 3000 轮 0 次。
  • 新增回归用例:两种 errexit 模式、if ! 与 || 四种失败调用位、前后 $- 状态一致;套件 348 PASS。变异(移除 set +e)会让该用例失败。

已知限制

  • 20 次绿灯对 10% 失败率的检出力约 88%,不能单独证明低频故障归零;本次修复针对已观察到的读取路径,未证明 Bash 内部触发条件。
  • set +e 是函数级 shell 状态,信号 trap 可能在该窗口看到 errexit 关闭;窗口仅一次读取。
  • 最终提交与本地绿跑版本仅差测试侧 PID 绑定修正,最终 HEAD 以本 PR 的 Ubuntu 24.04 / Debian 13 CI 为准。

FORGE and others added 3 commits October 25, 2026 00:21
Read a live pid and an absent pid through read_process_stat and
read_process_uid under `set -e` and `set +e`, in `if !` and `||` call
positions, and assert that control flow and the caller's errexit state
survive. Narrow the helper comments: the substitution only avoids the
observed cross-read path, and errexit is toggled because a failed
$(<file) open can bypass if/|| on Bash 5.2.
@LucaLin233
LucaLin233 merged commit d76e978 into main Oct 24, 2026
33 of 34 checks passed
@LucaLin233
LucaLin233 deleted the luno/fix-flakes branch October 24, 2026 17:21
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