Atomic File Writes
Exit

Atomic File Writes

Rewrite save_expenses to use atomic file operations that prevent data corruption

💻

Writing code and entering commands is only available on desktop. Open this page on a larger screen to complete this chapter.

The corruption problem

Your save_expenses() function writes data directly to expenses.json. If the program crashes or loses power while writing, the file can end up partially written — valid JSON at the top, truncated garbage at the bottom. When the API restarts, load_expenses() fails because it cannot parse the corrupted file. All your data is lost.

This is not a theoretical risk. Production servers crash, disks fill up, and processes get killed. Any file write that is not atomic is a data loss risk.

The atomic write pattern

The fix is a two-step process:

  1. Write the data to a temporary file in the same directory
  2. Use os.replace() to swap the temp file into the real file path

os.replace() is atomic on all major operating systems. It either completes fully or does not happen at all. If the program crashes during step 1, the original file is untouched. If it crashes during step 2, the operating system guarantees the swap either completes or rolls back.

Why the same directory matters

The temporary file must be in the same directory as the target file. os.replace() is only guaranteed to be atomic when both files are on the same filesystem. Writing to a different directory might cross filesystem boundaries.

Instructions

Rewrite save_expenses() to use atomic file writes.

  1. Add import os to the imports at the top of the file.
  2. Replace the body of save_expenses() with three parts:
    • Set temp_path = str(DATA_FILE) + ".tmp" — this is the temporary file you will write to first.
    • Open it with with open(temp_path, "w") as f: and write the data with json.dump({"counter": counter, "expenses": expenses}, f, indent=2).
    • After the with block, call os.replace(temp_path, str(DATA_FILE)) — this atomically swaps the temp file into place.