SAVE LAB · 故障注入实验

保存到一半断掉,旧文件还能活着吗?

“用 w 打开再写入”看起来没问题,危险却藏在打开和写完之间。选择保存策略,再把故障插进不同步骤,比较主文件最终留下的是完整旧版、残缺半版,还是完整新版。

保存策略
故障发生位置
第 2 步后
故障 0:全程成功。
故障 1–4:对应步骤执行完后突然中断。尝试在同一个位置切换两种策略,结果最直观。
settings.json · 主文件

              
完整旧版本,可正常读取
settings.json.tmp · 临时文件
(尚未创建)
不参与当前读取
等待实验:当前主文件保存着一个完整旧版本。

直接覆盖:先破坏旧版,再构造新版

with open(target, "w", encoding="utf-8") as f: json.dump(data, f, ensure_ascii=False)
  • w 在打开成功时就截断旧文件,不会等新内容全部准备好。
  • 若写到一半遇到磁盘错误、进程退出或设备断电,主路径可能只剩半截内容。
  • 代码短,适合“丢了也能重建”的临时输出;重要配置和用户数据要更谨慎。

临时写入:新版本完整后再换岗

tmp = target.with_suffix(".tmp") with open(tmp, "w", encoding="utf-8") as f: json.dump(data, f, ensure_ascii=False) f.flush() os.fsync(f.fileno()) os.replace(tmp, target)
  • 写临时文件期间,主路径上的旧版不动;中途失败还能继续读取旧版。
  • os.replace 成功时把临时文件替换到主路径;在 POSIX 同文件系统条件下,此替换是原子的。
  • 临时文件放在目标文件同一目录,更容易保证处在同一文件系统,也便于权限和清理管理。
边界别忽略:“原子替换”解决的是读者看到半个新文件的问题,不等于所有平台、所有断电场景都自动获得绝对持久性。生产代码还要处理权限不足、磁盘空间耗尽、临时文件清理,以及不同操作系统的文件占用规则;特别重要的数据应配合备份、校验和恢复策略。