An old leather-bound logbook open on a desk with a fountain pen resting across the page Tech
AI-generated, Working Theory
Tech · ◉ Evergreen

Write it down before you do it

by · ·5 min·Working Theory

The 'D' in ACID isn't free, and it isn't the write you think it is. Write-ahead logging is the trick that lets a database promise your data survived a crash without paying full price on every change, and the fsync you quietly skipped is exactly where that promise breaks.

Your app calls save(). It returns. The user sees “Saved.” Somewhere in that half-second, a promise got made: if the power dies right now, this survives. The interesting question is when, exactly, that promise became true, because it is almost never the moment you think.

Here’s the naive mental model: to save a change, the database walks to the right spot in the big data file on disk and writes it there. Simple, and slow, and, worse, dangerous. That “right spot” might be a random location in a multi-gigabyte file, and updating a real record often means touching several places at once: the row, an index, maybe a free-space map. If the machine loses power halfway through touching those several places, you don’t get “the old data” or “the new data.” You get a structure that’s half-updated and internally contradictory, a torn write, a corrupt index. The database can’t even trust itself when it comes back up.

Write-ahead logging is the fix, and the name says the whole thing: write the intention to a log before you touch the real data. Before the database modifies a single data page, it appends a compact record to the end of a sequential log file — “here’s the change I’m about to make” — and forces that to disk. Only after the log record is safely down does the database consider the change durable and tell you “Saved.” The actual data pages can be updated lazily, in the background, minutes later, in whatever order is efficient.

change 1 · append to log fsync ← the promise 2 · "Saved" acknowledge 3 · apply to data pages background · lazy · unordered crash here… recovery …replay the log to rebuild the pages disk / log (append-only)
The durability point is step 1's flush, not step 3's write. Everything after the flush can be rebuilt from the log; anything that never reached the log is simply lost, cleanly. Original diagram · Working Theory

Two things make this work, and they’re worth holding onto. First, appending to the end of a sequential file is dramatically cheaper than scattering writes across a big file. The disk head (or the SSD’s controller) isn’t jumping around; it’s writing one growing stream. So you pay the “make it durable” cost once, on a cheap sequential write, instead of many times on expensive random ones. Second, a crash is now survivable by construction. When the database restarts, it reads the log from the last known-good checkpoint forward and replays it, re-applying any changes that made it into the log but hadn’t yet been written to the data pages. Changes that never reached the log are gone, but gone cleanly, as if they never happened, which is exactly the promise you wanted. Half-applied corruption isn’t on the menu.

Now the part that bites people. The whole guarantee rests on one system call actually reaching the physical medium: fsync. Writing to the log isn’t enough — a plain write usually just lands in the operating system’s page cache, in volatile memory, and returns “success” while the bytes are still in RAM. If the power dies there, your durable log wasn’t durable. fsync is the call that says don’t come back until it’s really on the disk. It is slow precisely because it’s honest, it’s waiting on physics.

And so the most common durability bug isn’t exotic. It’s someone, somewhere, deciding that fsync on every commit is too slow (it is slow!) and quietly relaxing it, batching flushes, or turning them off, to make the benchmark look better. That’s not a free speed-up. It’s a change to the meaning of “Saved.” You’ve moved the durability point from “the moment we acknowledged” to “sometime in the next few hundred milliseconds, probably,” and bought yourself a small window where the database will happily tell a user their data is safe when it’s still only in RAM. This can be a completely legitimate trade — plenty of systems accept losing the last fraction of a second of writes on a hard crash in exchange for throughput. The sin isn’t making the trade. The sin is making it without saying so, so that “Saved” means one thing to the user and another to the disk.

There’s a legitimate way to have both, and it’s worth knowing the name: group commit. Instead of flushing once per transaction, the database gathers all the transactions that piled up in the last tiny slice of time and flushes them together in a single fsync. Each individual writer waits a hair longer, but the expensive honest call gets amortized across many of them. You keep the real guarantee and reclaim most of the speed, which is usually the trade you actually wanted, made on purpose.

The takeaway that outlives the specifics: durability is a promise about a specific instant, and that instant is the flush, not the write. Any system that returns “Saved” faster than it can honestly flush has moved the promise without telling you where. Write-ahead logging is how you keep the promise cheaply, write the intention down, force that, and let the real work happen whenever. But it’s only as true as the fsync you didn’t skip.

Concepts, to look up: write-ahead logging and the ARIES recovery algorithm (Mohan et al.); the “D” (durability) in ACID; the OS page cache and what fsync/fdatasync actually guarantee; group commit as the amortization of flush cost; checkpointing and log replay on recovery.

Sources

  • Write-ahead logging and the ARIES recovery algorithm (Mohan et al.)
  • the 'D' (durability) in ACID
  • the OS page cache and what fsync/fdatasync actually guarantee
  • group commit as the amortization of flush cost
  • checkpointing and log replay on recovery.

Liked this? Get the next one in Working Theory.

Going weekly in August (it's in beta now). One genuinely interesting read on building, the brain, and the science most people missed.

Subscribe →
Got a reaction, a counter-example, or something I missed? Reply by email — I read everything.
◉ join in

Where have you hit this — in a product you use, or one you're building?

Threads open here soon. For now, the conversation lives two clicks away — discuss on GitHub, or just reply by email. I read and answer everything.