博客 · 2026-09-23

Orbi 给 k8e 写 etcd 测试:先被打回,合并后票又关错了

Orbi 给 497 星的 k8s 发行版 k8e 写了一套 etcd 测试。第一版被评审打回,改完合并了,分四期的票却在第一期后就被关掉。这篇按记录逐条复盘。

k8e 是一个内嵌 etcd 的 Kubernetes 发行版,497 星,维护者是 Deshi (Tommy) Xiao。9 月 20 日他开了 #612,要给内嵌的 etcd 做一套端到端的健壮性测试,分四期:先测崩溃后能不能安全恢复,再测多节点故障、备份能否还原、长时间运行是否稳定。

10:28(北京时间,下同)他给票打上 ai-ready,交给了 Orbi。下面的时间和数字来自这张票的标签历史、PR 和评审记录,只有 runner 崩溃的次数取自 runner 日志。

第一版被打回了

11:37,Orbi 开出 PR #613,只做第一期:10 个新文件,2483 行。内容包括一个记录每次操作的历史记录器、一个判断恢复结果对不对的判定器(oracle),以及几组测试:写到一半 SIGKILL 子进程、故意损坏 WAL、写满 quota。

11:46,维护者把 SonarCloud 和 DeepSource 扫出来的问题贴了上来,Orbi 11:57 开始推修复。

13:21,维护者账号上的评审给了 REQUEST_CHANGES。这时 PR 已经涨到 2695 行。评审提了三条:

评审还说,PR 正文里的 Fixes #612 会让这张四期的票在第一期合并时就被关掉。

三条都跟代码风格无关。每一条都会让测试在数据没恢复对的时候报「恢复正确」。测试本来就是用来抓这种问题的,它自己报假平安,比没有测试还麻烦。

Orbi 自己也出了错

同一个 PR 上,Orbi 的引擎公开报了三次自己的失败,都留在 PR 评论里:

  1. 12:28,想给票加一个仓库里没有的标签 ai-awaiting-merge,失败。12:37 引擎放弃,把票标成 ai-blocked,意思是交给人来决定下一步。维护者 14:16 摘掉这个标签,14:41 重新打上 ai-ready,才接着往下跑。
  2. 14:54,Orbi 自己的独立评审没输出结论行,结果解析不出来。
  3. 15:19,续跑时的校验要求 PR 正文里必须有 Fixes #612。可这一行正是维护者评审要删的,而且他说得对。

第三个问题 Orbi 一直没自己解决。更糟的是,这次校验失败没有只卡住这一张票,而是让服务他这个仓库的整个 runner 进程崩溃。runner 日志里,15:19 到 22:10 一共崩了 84 次,他整个队列都停着。公开复盘 orbi#1219 是崩到一半时提的,里面记的是 77 次。19:59,维护者在票下留言「继续修复吧」,之后也没有动静。「PR 必须写 Fixes」这条规则,放在一次做完的票上没问题,放在分期的票上就错了。引擎死守规则,这回是人工评审对。

怎么修的

15:05,一个 commit(f8e9dd9c)把三条都改了:

R1 和 R3 各补了正反两个方向的测试。PR 正文改成了「Part of #612 — this PR lands the phase-1 layer only」。

22:12,第二轮评审给了 APPROVED。它逐条核对了修复,提了两个合并前的条件:一是自己再跑一遍 race 测试;二是把 Orbi 第一个 commit message 里残留的 Fixes #612 去掉。第二条被标成可选,因为评审认为只有 PR 正文里的关键字才会关票。还没覆盖到的地方,设计文档里写了,评审也逐项列了。38 秒后,22:13,维护者自己点了合并。从记录里看不出这两个条件在合并前做过。合并时 CI 一共 801 个测试,新增 49 个,没有失败。

票还是被关了

评审对 commit message 的判断错了。合并进默认分支的 commit,只要 message 里有关闭关键字,GitHub 就会关掉对应的票,不光看 PR 正文。合并后一秒,#612 被关成了 completed,关它的就是 Orbi 的第一个 commit。四期只做完了一期。第一轮评审担心的正是这个,最后它从唯一没改的地方漏了过去。

这是 Orbi 在这张票上犯的第四个错,后果也最重。我们引擎的规则只要求 PR 正文写 Fixes #N,交付 agent 又额外把它写进了第一个 commit message。两轮评审都点过这个关键字,第一轮说的是 PR 正文,第二轮说的是 commit,票还是关了。写这篇文章的时候,#612 仍然是关闭状态。

第二张票:从开 PR 到合并 3 小时 12 分

第二天是 #614,做 rqlite 兼容,对应 M0 这一关。维护者 15:21 打上标签。PR #615 改了 11 个文件,加了 3294 行,16:49 开出,17:33 起进入等批准的状态。维护者账号上先出现了一条 LoopX 的评审意见,20:00:52 他点了批准,29 秒后 Orbi 合并。

回头看

Orbi 这次没有一次做对,维护者也出手了好几回:解除阻塞、把票放回队列,Orbi 停住时催它继续,最后绕开 Orbi 一条不适合这张票的规则,自己合并。结果一张四期的票,还是在第一期后就被关了。

不过评审说「不行」的时候理由是具体的,修复也落在同一个 PR 上。在别人的代码库里接一张难票,从开 PR 到合并用了十个半小时。所有失败,包括 Orbi 自己的,都留在公开记录里,可以拿这篇文章去对。

事后 Orbi 改了两处(orbi#1219)。一是 PR 正文不合规时,只让这一张票失败,不再拖垮整个 runner。二是工作流规范里写明:分期的工作不要用一张票承载,维护者先把每一期拆成子票,每张子票的 PR 只关它自己。

如果你的仓库里也排着一串写清楚了的 issue,可以让 Orbi Cloud 在你的仓库里跑同样的流程。