Skip to content

Flow Diagram:明确外部写入一致性保证并消除 compare→rename 覆盖窗口 #239

Description

@suntianc

Parent

来源

2026-07-28 对 #200 落地代码的 Spec review。

问题

replaceFileAtomicallyIfUnchanged() 当前执行:

  1. 读取目标文件;
  2. expectedBytes 比较;
  3. await fs.promises.rename(temp, target) 发布新内容。

Project mutation 队列只能串行化 CDF 进程内的写入。任意外部程序仍可在步骤 2 与 3 之间写入目标文件,该写入随后会被 CDF 的 rename 静默覆盖。因此当前实现提供的是“原子发布 + 进程内串行化”,不是 #200 所写的、面向任意外部写入者的原子 compare-and-swap。

现有测试仅覆盖:调用前文件已变化,以及两次 store.saveDocument() 的进程内并发;没有覆盖“比较完成后、发布前”发生外部写入。

必须先完成的决策

标准 Node 文件 API 没有跨平台的“比较现有内容并条件替换”原语,且不合作的外部写入者不会遵守 CDF 的进程锁。因此实现前必须明确产品保证,二选一并记录理由:

A. 严格外部写保护

保证任意外部程序在任何时刻写入都不会被 CDF 静默覆盖。采用此选项时,必须给出可在 macOS / Windows / Linux 上成立的发布协议或明确受支持的平台边界,并用可控竞态测试证明,而不是再次缩短 check→rename 窗口。

B. CDF 内部强保证 + 外部写 best-effort

严格保证所有 CDF 写入者之间不丢数据;对不合作的外部程序只做发布前检测和冲突提示,不宣称无窗口。采用此选项时,必须同步修订 #200 中“原子 CAS / 外部程序不丢修改”的表述、测试口径和用户可见冲突语义,避免实现与承诺继续不一致。

若该保证会成为长期架构约束,按 docs/agents/domain.md 判断是否需要 ADR;不要在代码注释里单独做产品决策。

交付内容

  • 写明选定的保证等级、适用写入者和平台边界。
  • 使 API/type/注释准确表达该保证;禁止继续把 read + rename 称为无条件原子 CAS。
  • 按选定保证实现冲突检测与错误结果。
  • 保留原子发布:读者只能看到旧文件或新文件,不能看到半写文件。
  • 失败和冲突必须保留用户未保存内容,并返回当前磁盘内容/版本。

测试

必须新增一个可控竞态 seam,在“完成 expected 版本校验”与“发布”之间插入外部写入,并断言选定保证下的预期结果。

同时保留:

  • 两个 CDF writer 的交错写入;
  • 创建目标时的并发 EEXIST
  • 写入失败无临时文件残留;
  • 冲突返回当前内容;
  • 发布中断不产生半写文档。

验收标准

非目标

Metadata

Metadata

Assignees

No one assigned

    Labels

    P1高优先级 bugarchitecture架构 deepening 候选(来自架构评审)bugSomething isn't workingneeds-infoWaiting on reporter for more information

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions