配置文件写到一半怎么办:临时文件与原子替换
直接打开配置文件写入会先截断旧内容。进程在中途失败,下一个读者可能只看到半份 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.replace | replace、fsync 与文件系统约束 |
| Python tempfile 文档 | https://docs.python.org/3.11/library/tempfile.html | 临时文件位置、句柄与清理 |
核验说明
- 主示例在本地 Python 3.11.9 下执行,检查正文中的断言与输出。
- 实验范围是文中构造的输入和本地运行条件,部署时需按实际数据规模与运行环境核验。