diff --git a/matter_server/server/storage.py b/matter_server/server/storage.py index eeb896aa..64a0e0bd 100644 --- a/matter_server/server/storage.py +++ b/matter_server/server/storage.py @@ -5,8 +5,11 @@ import asyncio import logging from pathlib import Path +import shutil from typing import TYPE_CHECKING, Any, cast +from atomicwrites import atomic_write + from ..common.helpers.json import JSON_DECODE_EXCEPTIONS, json_dumps, json_loads if TYPE_CHECKING: @@ -152,11 +155,14 @@ async def async_save(self) -> None: def do_save() -> None: # make backup before we write a new file - if self.filename.is_file(): - self.filename.replace(self.filename_backup) + self.filename_backup.unlink(True) + shutil.copy(self.filename, self.filename_backup) - with open(self.filename, "w", encoding="utf-8") as _file: + # use atomomic write to avoid corrupting the file + # if power is cut during write, we don't write a corrupted file + with atomic_write(self.filename, encoding="utf-8", overwrite=True) as _file: _file.write(json_dumps(self._data)) + LOGGER.debug("Saved data to persistent storage") async with self._save_lock: diff --git a/pyproject.toml b/pyproject.toml index c5786a04..b4069fef 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -35,6 +35,7 @@ server = [ "aiohttp==3.11.10", "aiorun==2024.8.1", "async-timeout==5.0.1", + "atomicwrites==1.4.1", "coloredlogs==15.0.1", "cryptography==44.0.0", "orjson==3.10.12",