When a save is really saved

Engineering

You save a document, close the application, and expect to find your work when you come back. Getting that right takes more than writing the new contents to a file.

Consider one document on a local Linux filesystem, with one process making changes. A common approach writes a temporary file beside the original, synchronizes it, replaces the original, then synchronizes the directory.

Replacing a fileThe original remains at document.txt while a temporary file is written and synchronized. Rename switches the path to the new file. Directory synchronization follows.Write temporary fileSync fileReplaceSync directoryOriginalReplacementReplacing a fileThe original stays intact while a temporary file is written and synchronized. Replacement switches the destination; directory synchronization follows.OriginalTemporaryWriteSync fileReplaceSync directory

Keep the original until the replacement is ready

Opening the original file with truncation discards its contents before the replacement has finished writing. If the process stops halfway through, the destination can contain only part of the new document.

Writing to a separate file leaves the original alone. Once the new file is complete, rename can replace the destination atomically on the same filesystem. A process opening that path gets the old file or the new one. A reader that already has the old file open can keep reading it.

Direct overwriteIncomplete
Separate temporary fileOriginal intact
Destination after interruption during the write, before replacement.

Visible does not mean durable

Replacement changes which file the name points to. Surviving a machine crash is a separate concern.

On Linux, fsync waits for the device to report that the file’s data and metadata have been synchronized. The directory entry needs its own synchronization. Sync the temporary file before replacement, then the directory afterwards. Check every result.

If replacement succeeds but the directory sync fails, the new document may already be visible. The application cannot honestly report that nothing changed, or that durability was confirmed. It should keep the edits available and report the failure.

Test the interrupted save

Stop the writer at each boundary and reopen the destination in a fresh process. Inject write, sync, and replacement errors. Check the recovered document as well as what the application tells its user.

Killing a process leaves the operating system running; it does not test power loss. SQLite’s crash tests go further by simulating incomplete and reordered writes before checking recovery.

This is a design outline, not a report on an Aro product. Permissions, concurrent writers, and the actual filesystem still need attention. The rule we would build around is straightforward: keep the previous work until its replacement is ready, and only claim the guarantee we have checked.