先检查文件不存在还不够:让报告写入真正拒绝覆盖

举报
yd_294604443 发表于 2026/10/26 09:14:06 2026/10/26
【摘要】 从真实的并发和故障注入记录出发,解释存在性检查的竞态,以及同目录临时文件、fsync与硬链接如何让报告写入拒绝覆盖,并交代文件系统和断电持久性的边界。

摘要:报告导出需要同时处理两个问题:已有文件不能被覆盖,失败时不能留下半份成品。本文结合一次本地修复及并发、故障注入记录,说明如何用同目录临时文件和硬链接创建完成发布,并划清异常清理、原子可见性与断电持久性的边界。

报告路径不存在,于是打开文件写入,看上去已经满足“只能新建”。但如果两个进程同时导出到同一路径,检查时不存在,写入时可能已经属于另一个进程。一次离线核对工具的修复,就从这个时间差开始。

提前检查挡不住后来的写入者

常见写法是先调用 exists(),不存在时再用 open(path, "w") 写报告。问题在于,这两步不是一个不可分割的操作。进程甲检查后暂停,进程乙创建并写好文件;甲恢复时仍按旧判断打开目标,而 "w" 会截断已有内容。

2026 年 10 月 25 日保存的失败记录中,竞争写入者留下的文本确实被报告内容替换;另外两个用例还暴露了写入失败后目标仍存在,以及报告失败却先输出成功结果的问题。这些是实际失败断言,不能靠多加一次存在性检查解决。

直接用排他创建模式 "x" 能拒绝已有目标,但写入过程仍发生在最终路径上:中途异常可能留下不完整文件。这里还需要第二个契约:最终文件名出现时,内容已经写完。

先准备内容,再争取最终文件名

修复采用两个阶段。先在目标目录创建随机临时文件,将完整报告写入,执行 flush()、os.fsync(),随后关闭文件。准备成功后,才调用 os.link(temporary, destination) 创建最终路径。

硬链接为已经写好的文件增加一个名字,不需要把内容再复制一遍。在支持相应硬链接语义的文件系统上,创建目标名这个操作会拒绝已存在的路径,检查与创建之间不再留给应用程序一个可被插入的间隙。多个发布者竞争时,失败者不能因此覆盖胜者。

教学流程可以压缩为:

在目标目录创建临时文件
try:
    写完内容,flush,fsync,关闭
    创建临时文件到最终路径的硬链接
finally:
    关闭尚未移交的文件描述符
    删除临时文件名

临时文件放在同一目录,是为了让准备与发布落在同一文件系统范围内,避免跨文件系统硬链接失败。成功创建链接后,删除临时名字不会删除最终名字对应的内容。这里也不能随手改成允许替换目标的操作,否则“拒绝覆盖”的契约就变了。

失败也有需要保护的状态

写入、编码或同步失败时,还没有创建最终路径,清理只处理临时文件。若发布时目标已存在,就保留对方内容,返回拒绝覆盖错误;若硬链接操作因权限或文件系统能力等原因失败,则报告写入失败,不能退回普通覆盖写入。

实现还把完整 JSON 序列化放在报告发布之前,把成功结果输出放在发布之后。这样,结果无法序列化时不会先留下报告,报告发布失败时也不会先向调用者发出成功信号。调用者读到的应当是单个结构化错误,而非成功与错误混杂的输出。

finally 是异常路径的清理安排,不代表任何情况下都没有残留。进程被强制终止,或者清理本身失败,仍需另外考虑恢复策略;也不应为了清理方便而删除竞争者已经创建的最终文件。

试验支持什么,就陈述什么

在最终整数边界修复前,独立复核曾于 Windows、Python 3.12 环境启动 12 个真实子进程,使用同一份有效输入,争抢一个新报告路径。记录显示,恰有 1 个返回成功,其余 11 个返回拒绝覆盖的 JSON 错误;最终报告完整,未留下临时文件。

另一组试验保留真实临时文件创建,只分别在 os.fsync、os.link 边界注入 OSError。两种情况均返回写入错误,最终目标未出现,临时文件被清理。这证明了被测错误路径的行为,不能据此声称已经遍历所有不支持硬链接的存储设备。

最终日志记录了整个离线套件 53 项通过,涵盖的内容也超出报告写入。本文依据这些既有记录分析,没有重新运行测试;测试数量不能替代生产环境证明。

还要分清三个问题:拒绝覆盖,发布时让读者看到已写完的内容,以及断电后目录项能否持久保存。当前方案围绕前两项设计,文件上的 fsync 不等于已经证明目录项在断电后持久。本地试验也不能推广为所有网络盘、挂载方式和文件系统的统一保证。实际部署应确认目标存储的契约;硬链接能力不足时明确失败,才能守住“只能新建”的承诺。

【版权声明】本文为华为云社区用户原创内容,未经允许不得转载,如需转载请自行联系原作者进行授权。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0)

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。