Skip to content

fix(outcome): 并发保存观点与评估结果会静默丢数据 #316

Open

Description

@huyengiang101086-spec

问题

OutcomeRepository.saveOpinion() 和 saveOutcome() 分别读取整个 outcomes.json,修改一个集合后覆盖写回。JsonFileStore 的原子替换只保证文件完整,没有保护仓库的读改写过程。两个保存操作重叠时,会基于同一旧快照写入,静默丢失其中一次更新。

这不只影响同时保存两个观点:kernelHost.ts 的研究完成回调调用 createOpinionFromReport(),启动及手工评估路径调用 evaluateDue(),两条路径共用同一个仓库文件,因此新观点和到期评估结果也会相互覆盖。

复现与预期

基线:main a6da0059ecafe4426c9a841ec59215655ebcabd1。

对真实临时目录中的 JsonFileStore.read() 增加 20ms 延迟以放大重叠窗口,执行:

await Promise.all([
  repository.saveOpinion(newOpinion),
  repository.saveOutcome(completedOutcome),
])

实际:一份旧快照覆盖另一份更新,新观点或评估结果消失;操作均成功返回。

预期:同一文件的两类读改写应串行化,两份更新均可从新建仓库实例读回。

验证

环境:Bun 1.4.2 / Windows 11 (10.0.26200)。

测试先于生产修复执行:

bun test packages/shared/src/outcome/repository.test.ts packages/shared/src/outcome/service.test.ts
→ 19 pass / 4 fail

四个失败覆盖并发观点保存、并发评估结果保存、两集合交错保存,以及真实 OutcomeService.createOpinionFromReport() 与 evaluateDue() 的重叠持久化。

修复范围

按解析后的目标文件路径串行化两个保存方法的完整读改写。沿用 ConnectionStore 的进程内队列模式,兼容同一文件上的多个 store/repository 实例;失败对原调用方保持可见,且不阻塞后续更新。保持 JSON schema、读取行为和 upsert 语义。

范围限定于单个应用进程内的并发写入,不承诺多进程事务,也不恢复已经丢失的数据。

本报告与配套改动由 Codex 在用户授权下准备,未调用真实金融提供方。

Activity

  1. huyengiang101086-spec commented on Oct 12, 2026

    @huyengiang101086-spec
    Author

    /claim

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions