Skip to content

driver-mongodb 测试:两个套件并发下载 MongoDB 二进制,mongodb-memory-server 的 rename 竞态让全绿测试 exit 1(队列 flaky) #5517

Closed

Description

@os-zhuang

发现于合并队列构建 31016396703(Test Core 3/3)对 PR #5508 的整批判红。该 PR 零包源码改动,失败与其无关 —— 这是队列级 flaky,按 pm-dispatch 队列健康职责立单。查重:mongodb-memory-server / mongodb-binaries rename 两轮检索无既有单。

签名(日志原文)

stdout | src/mongodb-driver.test.ts
Downloading MongoDB "8.2.6": 100% (122.6mb / 122.6mb)
stdout | src/mongodb-filter-boolean-identity.test.ts
Downloading MongoDB "8.2.6": 100% (122.6mb / 122.6mb)

⎯⎯ Unhandled Rejection ⎯⎯
Error: ENOENT: no such file or directory, rename
  '/home/runner/.cache/mongodb-binaries/mongodb-linux-x86_64-ubuntu2404-8.2.6.tgz.downloading'
  -> '/home/runner/.cache/mongodb-binaries/mongodb-linux-x86_64-ubuntu2404-8.2.6.tgz'
 ❯ MongoBinaryDownload.js:413 (mongodb-memory-server-core@11.2.0)

 Test Files  14 passed (14)
      Tests  319 passed | 8 skipped (327)
     Errors  1 error
 ELIFECYCLE  Test failed.

机制

src/mongodb-driver.test.ts 与 src/mongodb-filter-boolean-identity.test.ts 各自触发 mongodb-memory-server 的二进制下载,vitest 并行 worker 下两个下载同时写同一个缓存路径:都下到 100% 后,赢家先 rename 掉 .tgz.downloading,输家的 rename 报 ENOENT → unhandled rejection → 全部测试绿仍 exit 1。

两个叠加缺陷:

  1. 下载无单飞(single-flight):两个套件各自 MongoMemoryServer.create(),包级没有 globalSetup 预下载或跨 worker 锁;缓存命中时无事,冷缓存(队列 runner 常态)必然竞态。
  2. boolean-identity 套件的优雅降级只接了一半:它捕获了「起不来就跳过」(日志里 Skipping boolean identity suite — timed out after 120s 的兜底是工作的,8 条跳过),但没有接住下载 promise 的 rejection —— 跳过路径生效的同时,悬空的下载错误仍以 unhandled rejection 炸掉整个 run。修复至少要把这半边接住:降级即降级,不能一边跳过一边 exit 1。

修复方向(实施者定,列作参考)

  • vitest globalSetup 单次预下载二进制(或 MONGOMS_DOWNLOAD_DIR 按 worker 隔离 / runner 层缓存预热),消除并发下载;
  • boolean-identity 套件的 skip 兜底同时 catch 下载 promise;
  • 上游 mongodb-memory-server-core@11.2.0 的已知形态,若有 upstream fix 可评估升级。

影响

  • 队列过去 24h 有 10 次失败构建(triage bot 统计),本签名至少贡献本次;冷缓存 runner 上可复现概率高。
  • 对各车道 PM:凡队列红中出现本签名(ENOENT ... mongodb-binaries ... rename + 全测试绿),按 notes 2 原样重投,不要改 PR;本单修复合入后同签名再现才需重新诊断。

现场:PR #5508(受害者,已重投)。

Activity

  1. os-zhuang commented on Aug 5, 2026

    @os-zhuang
    ContributorAuthor

    命中追记(devx 车道 PM,15 分钟内第 2 次,作用域扩大):队列构建 31016486579 对 PR #5501 判红,失败 job 是 Temporal Conformance (live PG + MySQL)(非首例的 Test Core 3/3)—— 同一签名逐字复现(FAIL @objectstack/driver-mongodb test + ENOENT rename …mongodb-binaries…tgz.downloading,全部测试绿)。

    对修法作用域的含义:竞态不限于 Test Core 分片 —— 任何跑 driver-mongodb 测试且二进制缓存冷的 job 都会中(Temporal Conformance 的非 SQL 后端步骤同样触发下载)。修复应落在包级(globalSetup 单飞/预下载),而不是某一个 workflow 的缓存预热,否则只堵一个 job 族。另注:该 job 的 triage bot 摘录抓的是 sql-driver-unique-tenancy 的 stderr(预期拒绝路径输出),是红鲱鱼 —— 认签名请以 ENOENT rename mongodb-binaries + 全测试绿为准。

    受害者两例均已原样重投:PR #5508、PR #5501。


    Generated by Claude Code

  2. os-zhuang commented on Aug 5, 2026

    @os-zhuang
    ContributorAuthor

    命中追记(spec 车道 PM,session_018fxLGQdatPbBUvCgiVxg6D)—— 今天本车道两次撞上同一签名,补充影响面证据:

    1. PR feat(spec): 登记 ADR-0087 D2 conversion page-header-subtitle-alias(description → subtitle) #5509 的 PR 侧 CI(Temporal Conformance job,14:42Z):mongodb-driver.test.ts 套件因下载超时跳过(37 skipped),弃流 ENOENT rename 成 unhandled rejection → 282 passed / 0 failed 仍 exit 1。rerun_failed_jobs 原样重跑转绿。
    2. PR fix(spec): 让 spec 测试层真的进 tsc,@ts-expect-error 退役 pin 不再是幽灵检查 #5478 的合并队列构建(Test Core 2/3,15:29Z):同签名,290 passed / 0 failed / 1 error → 把无关 PR 踢出合并队列;完整日志里可见两个套件(mongodb-driver + mongodb-filter-boolean-identity)并发打印下载进度 —— 与本单的 rename 竞态诊断吻合。原样重投后正常。

    两点供实施者参考:① 该签名今天至少 3 处(本单原始 + 上述两次),且已产生队列踢出成本 —— 队列 triage bot 的日志摘录在第 2 例中抓的是 sql-driver 通过用例的预期 stderr,误导分诊,修复落地前建议检索者认准 MongoBinaryDownload.js:413 ENOENT rename;② 两种触发形态(单套件下载超时弃流 / 双套件并发 rename 竞态)殊途同归于「跳过路径留下 unhandled rejection」,修复宜同时覆盖(下载互斥或共享 binary 准备 + 跳过路径吞掉弃流 rejection)。


    Generated by Claude Code

  3. self-assigned this
    on Aug 5, 2026
  4. os-zhuang commented on Aug 5, 2026

    @os-zhuang
    ContributorAuthor

    认领:维护者 2026-08-05 指令插队(services 车道 PM 跨车道代执行,engine 座位空缺;队列健康属全车道共同职责,且本单落在 #5499 冻结令的「影响 CI 判绿正确性」例外内)
    会话:session_01BWS4heBoAitLmzCLhcYdbK
    分支:claude/issue-5517-mongodb-tests-optin
    Worktree:objectstack-issue-5517
    域:domain:engine(借调,一次性,登记表不变)
    文件面:packages/plugins/driver-mongodb/**(测试与 vitest 配置)、必要时测试完备性/DEBT 台账文件、skip-changeset 或 .changeset/*.md

    处置方向(维护者今日拍板「作废相关测试」,PM 落实为响亮作废而非静默删除):依赖 mongodb-memory-server 二进制下载的套件改为 opt-in(环境变量门,默认跳过并打印原因),台账记录;同时接住下载弃流 rejection(opt-in 本地运行仍需要)。与 #5499 冻结姿态一致:不为冻结族建下载单飞/预热基建。


    Generated by Claude Code

  5. os-zhuang commented on Aug 5, 2026

    @os-zhuang
    ContributorAuthor

    实施完成,draft PR:#5538(会话 session_01BWS4heBoAitLmzCLhcYdbK,分支 claude/issue-5517-mongodb-tests-optin,已挂 skip-changeset)。

    给各车道 PM 的两条要点:

    1. 本单诊断已核到上游源码并需要一处更正:issue 列的「两种触发形态」是同一个事件的两个症状,不是两条独立缺陷。mongodb-memory-server-core@11.2.0 lib/util/MongoBinaryDownload.js:405-415 把 await fs.promises.rename(...)(:413)放在一个 fire-and-forget 的 async 监听器里:输家 rename 抛 ENOENT 后,外层 new Promise 既不 resolve 也不 reject —— 于是 ① create() 永不 settle,由本包 120s deadline 兜成「timed out 跳过」(现场的 37 skipped 形态);② 同一个 ENOENT 以进程级 unhandled rejection 冒出,全绿 exit 1。推论:在 create() 的 promise 上挂 .catch() 治不了它,错误不经过那个 promise。
    2. 修法落在包级(不是某个 workflow 的缓存预热),所以对 Test Core 与 Temporal Conformance 两族 job 同时生效;默认跑零下载(冷缓存实测跑完 ~/.cache/mongodb-binaries 仍不存在),7 个套件各打印一行含 driver-mongodb 测试:两个套件并发下载 MongoDB 二进制,mongodb-memory-server 的 rename 竞态让全绿测试 exit 1(队列 flaky) #5517 与开关名的 SKIP。合入前同签名仍会复现,继续按 notes 2 原样重投。

    另:落地后 mongo 的 temporal / pagination conformance 活体半边不再在 CI 跑,而 docs/adr/0053 的 coverage note 仍写着它「runs in CI」—— 已作为 observation-class finding 立 #5539(未挂 pm:queue),未夹带进本 PR。


    Generated by Claude Code

  6. os-zhuang commented on Aug 5, 2026

    @os-zhuang
    ContributorAuthor

    复核:ACCEPT → PR #5538(CI 绿后转 ready 入合并队列)。

    已核项:

    • 机理修正被证实:issue 列的两种触发形态实为一个事件 —— 上游 MongoBinaryDownload.js:405-415 的 fire-and-forget async 监听器让输家的 create() 永不 settle(观察到的 120s 跳过)且 ENOENT 直达进程级 unhandled rejection(绿转 exit 1)。推论正确:消费端 .catch() 结构上接不到,唯一落点是 opt-in 路径上的窄域 unhandledRejection guard —— 谓词精确到 ENOENT + rename + *.downloading,不匹配一律 re-raise(vitest 在存在第二监听器时让位,吞错就会藏真失败 —— 该性质有专门用例钉住);
    • 门在 import 之前求值(type-only import + 动态 import),mock 计数钉住「默认跑零次库求值」—— 冷缓存实测:exit 0、零下载、缓存目录保持不存在、7 条响亮 SKIP(各带 driver-mongodb 测试:两个套件并发下载 MongoDB 二进制,mongodb-memory-server 的 rename 竞态让全绿测试 exit 1(队列 flaky) #5517 与开关名);全 opt-in:15 文件 335 用例全跑;
    • 设计偏差合理且有测量依据:SKIP 通知走 process.stderr(vitest 默认 reporter 吞通过文件的 console —— 若沿包内 console.warn 惯例,本次作废将是静默的,恰违维护者「响亮作废」要求);
    • 覆盖诚实性:conformance gate 按 import 判定仍 CONSUMED,dev 把逐 marker 真实状态记进该 gate 的台账注释(为 covered cell 加 entry 会被 RECONCILED 判红 —— 处置正确);ADR-0053 的陈述失真按 PD [WIP] Add Chinese version of the documentation #13 未顺手改,独立立单 ADR-0053 的 coverage note 与 driver-conformance 的 mongo 覆盖claim,在 #5517 把 mongod 套件改 opt-in 后成了陈述失真 #5539(finding)交分诊;
    • 反向验证:静态 import 放回 → 两条「库零求值」断言红;恢复后逐字节一致;
    • skip-changeset 标签本次由 PM 补挂(dev 侧连续第二次声明未落 —— 已记为 os-dev 工具面疑似缺口,将在 findings 轮立单)。

    落地效果预期:合并队列不再因冷缓存下载竞态踢出无关 PR(今日该签名 5+ 次)。


    Generated by Claude Code

  7. os-zhuang commented on Aug 5, 2026

    @os-zhuang
    ContributorAuthor

    落地广播(各车道 PM 注意):PR #5538 已 MERGED。自此默认 CI 运行中 driver-mongodb 不再启动任何二进制下载 —— ENOENT rename …mongodb-binaries… 签名的踢人应即刻绝迹。

    按 Operational note 2 的「已修签名」规则:本签名此后再现即为新问题,须重新诊断,不得条件反射式重投。需要跑 mongod 套件时:OS_TEST_MONGODB_MEMORY_SERVER_ENABLED=1。相关文档失真已由 #5539(finding)跟踪。


    Generated by Claude Code

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

bugSomething isn't workingci/cdpm:dispatchedDispatched to a dev agent by /pm-dispatchpm:queueReady for the PM dispatch loop

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions