deepseek-harness/.agents/notes/implemented/bug-fix/2026-08-29-windows-atomic-replace-retry.zh.md

2 KiB
Raw Blame History

Agent Note: 重试 Windows 上的瞬时原子替换失败

Status: implemented

English | 中文

问题

当另一个系统组件持有目标文件时,Windows 可能以 EACCES、EBUSY 或 EPERM 暂时拒绝替换已有文件的 rename。跨进程写锁能够排序应用内互相协作的写入方,却无法释放该外部句柄,因此把第一次错误当作永久失败会让本来有效的设置或凭据更新随机失败。

决策

writeFileAtomic 负责替换重试,因为每个文件型存储都需要相同保证。它仅在 Windows 上重试 EACCES、EBUSY 与 EPERM,最多八次,延迟从 20 毫秒指数增长至 200 毫秒。整个过程中,同一份已经完整写入的临时兄弟文件始终作为 rename 来源;调用方持有的写锁也会保持到 writeFileAtomic 结束。

其他错误码和其他操作系统会立即失败。重试预算耗尽后,函数移除临时兄弟文件并重新抛出最后一个文件系统错误;由于任何尝试都不会删除或截断现有目标,目标内容保持不变。

考虑过的替代方案

重试凭据变更。 消费方级重试仍会让设置和未来存储暴露于同一问题,而且重放一次读-修改-写操作可能重复原子替换之外的工作。共享原语是只负责替换重试的最窄所有者。

在 rename 前删除目标。 删除目标会让读取方观察到文件缺失,并放弃原子替换,因此不能作为恢复步骤。

无限重试。 永久权限错误会由此挂住写入方与所有锁竞争者。有界延迟可以吸收瞬时文件占用,同时保留可预测的失败结果。

后果

一个瞬时 Windows 句柄最多会让单次替换多等待 1.1 秒,随后最终尝试失败。在此期间,读取方继续看到完整的旧目标;成功仍由一次原子 rename 完成。回归测试注入每种可重试错误、永久错误、非 Windows 错误与重试耗尽,并观察 rename 尝试和推进伪时钟,而不依赖真实时间 sleep。