KernelRanch
返回文章列表

配置文件写到一半怎么办:临时文件与原子替换

2026-11-12 #python#filesystem#reliability

直接打开配置文件写入会先截断旧内容。进程在中途失败,下一个读者可能只看到半份 JSON。把新内容写到目标目录里的临时文件,刷新后再替换目标,可以把发布动作集中到一次替换操作。

1. 在发布前故意抛出异常

保存为 atomic_file_lab.py,运行 python atomic_file_lab.py。输出目录由脚本临时创建。

import json
import os
from pathlib import Path
from tempfile import NamedTemporaryFile, TemporaryDirectory

def publish_json(target, value, fail_before_publish=False):
    payload = json.dumps(value, ensure_ascii=False).encode("utf-8")
    staged = None
    try:
        with NamedTemporaryFile(dir=target.parent, prefix=".pending-", delete=False) as stream:
            staged = Path(stream.name)
            stream.write(payload)
            stream.flush()
            os.fsync(stream.fileno())
        if fail_before_publish:
            raise RuntimeError("injected failure")
        os.replace(staged, target)
    finally:
        if staged is not None:
            staged.unlink(missing_ok=True)

with TemporaryDirectory() as tmp:
    target = Path(tmp) / "config.json"
    publish_json(target, {"version": 1})
    try:
        publish_json(target, {"version": 2}, fail_before_publish=True)
    except RuntimeError:
        pass
    assert json.loads(target.read_text()) == {"version": 1}
    print("failed publish: version=1")
    publish_json(target, {"version": 2})
    assert json.loads(target.read_text()) == {"version": 2}
    assert sorted(p.name for p in target.parent.iterdir()) == ["config.json"]
    print("successful publish: version=2; temporary files=0")

2. 根据故障位置判断结果

故障位置应观察的状态
序列化失败目标文件仍是旧版本
临时文件写入或刷新失败目标文件仍是旧版本,异常传给调用方
替换失败本次发布失败,调用方检查权限、打开句柄和文件系统条件
替换成功后续打开目标路径的读者可以读取新版本

示例实测发布前异常后版本仍为 1,正常发布后版本变成 2,临时文件清理完成。临时文件放在目标目录可以避免跨文件系统重命名的问题。Windows 上先关闭临时文件句柄,再调用替换。

3. 区分完整发布和断电持久性

os.fsync 用于刷新文件内容。Linux 等 POSIX 环境若要求目录项在断电后也持久,需要在替换后打开父目录并同步目录,具体保证取决于文件系统。示例验证的是正常运行期间的失败路径,断电恢复需要专门演练。

替换会发布临时文件的元数据。目标文件需要固定权限、ACL 或所有者时,应在发布前设置并验证。多个写入者同时发布时,最后一次成功替换会覆盖前一次结果。对配置版本有条件更新要求时,需要锁或数据库事务来协调。

读者每次读取应在一次打开期间读完文件。一次逻辑操作反复打开路径,可能跨越两个版本。应用层完成结构校验后再应用新配置,校验失败保留当前生效版本。

小结

先生成完整内容,再写入同目录临时文件,最后替换目标路径。把发布失败、权限变化和并发写入作为独立验收项。

数据来源与检索时间

检索时间 2026-11-12。

来源URL对应内容
Python 3.11 官方文档https://docs.python.org/3.11/library/os.html#os.replacereplace、fsync 与文件系统约束
Python tempfile 文档https://docs.python.org/3.11/library/tempfile.html临时文件位置、句柄与清理

核验说明

  • 主示例在本地 Python 3.11.9 下执行,检查正文中的断言与输出。
  • 实验范围是文中构造的输入和本地运行条件,部署时需按实际数据规模与运行环境核验。