The naive save from lesson 39 overwrites the live file in place, so a crash mid-write leaves a torn, unreadable database. Today you make a save all-or-nothing - write to a temp file, flush it, then rename it into place.
Replace the in-place overwrite with a write-to-temp, fsync, then atomic-rename save so a crash never corrupts the live database file.
Lesson 39 wrote the database straight over db.txt. If the process dies partway
through that write, the file is left half-old and half-new - torn - and often
will not even load. The fix is the same trick every durable system uses: never
mutate the live file in place. Write the full new snapshot to a temporary
file, force its bytes to disk, and then rename the temp over the real path.
A rename within one directory is atomic: at every instant the live path
names either the complete old file or the complete new file, so a crash can only
leave one whole snapshot or the other, never a mix. The flush before the rename
matters too - it guarantees the new file’s bytes are actually on disk before it
becomes the official copy. This is the foundation the rest of the chapter builds
on: a snapshot you can always trust to be whole.
// write the whole snapshot to db.txt.tmp, flush it to disk,// then rename db.txt.tmp -> db.txt in one step:// os.WriteFile(tmp, bytes) ; f.Sync() ; os.Rename(tmp, path)// rename is atomic, so a reader sees only the old file or the new one