Skip to content
Lesson 7 of 23

Step 1 of 3 · Reading · ~3 min

fsync, Group Commit & Durability Modes

Write-Ahead Log & Durability

Why the WAL needs fsync at all

Your write-ahead log exists so a crash can't lose acknowledged writes: append the record to the log before touching the memtable, and after a crash you replay the log to rebuild state. But write() alone doesn't guarantee durability — the OS page cache happily buffers writes in RAM and can lose them on a power cut or kernel panic. fsync() (or fdatasync) is the call that forces those buffered pages to physical storage, and only after it returns can you honestly tell a client "your write survived."

The catch: fsync is slow — single-digit milliseconds on spinning disks, still a real cost (tens of microseconds to low milliseconds) even on SSDs, because it's a synchronous round trip to the storage controller. If every single write waited on its own fsync, your write throughput is capped at 1000 / fsync_latency_ms writes per second, full stop — no amount of application-level parallelism gets around it.

Group commit: amortize the fsync, not the durability

Group commit is the standard trick every serious WAL-based system uses (PostgreSQL, MySQL/InnoDB, Kafka's log segments): instead of one fsync per writer, batch together every write that arrives while a previous fsync is in flight, and flush them all with a single fsync call once that call returns. The durability guarantee is unchanged — nobody gets an ack until the bytes are actually synced — but the cost of syncing is now shared across however many writers happened to show up during that window.

This is exactly why it matters that fsync cost is fixed regardless of batch size: one fsync covering 1 record costs the same as one fsync covering 100. Under load, group commit turns "N fsyncs for N writes" into "far fewer fsyncs for N writes," and throughput approaches writers_per_fsync_window / fsync_latency, not 1 / fsync_latency.

The state machine you're simulating

At any moment there's at most one fsync "in flight," represented by fsync_end — the timestamp it will complete at (0 meaning idle):

  • A record arriving while no fsync is in flight (its arrival time is at or after fsync_end) kicks off a brand-new fsync, starting now.
  • A record arriving while one is in flight doesn't wait for the next fsync to start — it silently joins the batch currently being assembled, and gets acknowledged the moment that in-flight fsync (or the very next one, depending on your exact batching boundary) completes.

The subtlety worth sitting with: a record that arrives at the exact instant a running fsync completes has to be classified as "joins the batch that just finished" vs. "starts a fresh one" — get this boundary condition right and the rest of the simulation falls out cleanly. Walk through the worked example in the exercise slowly; every one of the four fsyncs is triggered by a different boundary case (idle-start, immediately-after-completion, and so on).

Durability modes, in the broader system

Real databases expose this as a tunable, because "batch better under load" trades against "batch worse under low load, so fsync less often, so acknowledge slower/less durably":

  • fsync every write (always) — safest, slowest; no data loss on crash, but throughput bounded by fsync latency per writer.
  • group commit (what you're building) — safe, much higher throughput under concurrent load; a single fsync failure or crash mid-batch is still all-or-nothing per fsync call.
  • periodic / OS-buffered (fsync every N ms, or none at all) — fastest, but a crash within the buffering window loses however many writes accumulated — this is what Redis's appendfsync everysec and many "eventually durable" systems choose by default, trading a bounded window of data loss for much higher write throughput.

Group commit is the sweet spot most production WALs land on: no data loss, and the fsync tax shrinks automatically as concurrency rises — exactly the behavior your simulator should reproduce.

Up nextSSTable Anatomy: Sorted Strings on DiskSSTable On-Disk Format

Discussion

Ask a question, share an insight, or help someone who’s stuck.

Sign in to post a comment or reply.

Loading…