Skip to content
fsync, Group Commit & Durability Modes
step 1/3

Reading — step 1 of 3

fsync, Group Commit & Durability Modes

~2 min readWrite-Ahead Log & Durability

fsync, Group Commit & Durability Modes

write() doesn't actually put bytes on disk. It puts them in the OS page cache. fsync() (or fdatasync()) is what tells the kernel to push those pages to the storage device — and even then the device may buffer further unless you've disabled its write cache.

The Durability Pyramid

ModeSurvivesCost
In-memory onlyNothingFree
write() (no fsync)App crash; NOT kernel/power/disk failureCheap
fsync() per writePower loss after ack~0.1–1 ms per call
fsync() + O_DIRECT, disabled drive write cacheCatastrophic failures, with battery-backed devicesEven more expensive

RocksDB calls these kNoSync, kSyncWAL, kManualSync. Cassandra has commitlog_sync = periodic | batch. The semantics are the same.

fsync vs fdatasync

fsync syncs both data blocks AND inode metadata (mtime, size, permissions). fdatasync skips metadata if it doesn't affect file content — typically saves one extra IO. For a pre-allocated WAL file at a known size, fdatasync is strictly faster with no downside.

LevelDB uses fdatasync on Linux, F_FULLFSYNC on macOS (where fsync is famously dishonest — Apple makes you ask explicitly for true durability).

Group Commit: The Throughput Multiplier

A single fsync covers all bytes appended so far. So:

T1: write rec1
T2: write rec2     <- both buffered
T3: write rec3
T4: fsync()        <- durable for all three
T5: ack T1, T2, T3

With per-write fsync at ~1 ms, throughput ceiling = 1000 ops/sec. With group commit batching 100 writes per fsync, the same fsync now covers 100 acks — throughput is 100,000 ops/sec.

Implementation: writers queue their records, one designated "leader" calls fsync, all queued writers are released. RocksDB's WAL implements exactly this with a writer queue.

The Three-Layer Reality

Even after fsync, your bytes may not be on platters / flash cells:

  1. Application buffer (e.g. Python's io.BufferedWriter): cleared by flush().
  2. OS page cache: cleared by fsync/fdatasync.
  3. Device write cache (DRAM on the SSD/HDD): may need hdparm -W 0 or a battery-backed write cache to be safe. Most consumer SSDs lie about fsync to win benchmarks.

This is why enterprise databases insist on power-loss-protected SSDs (Intel "DC" series, Samsung PM/PE series, etc.) — the controller flushes the DRAM cache to flash using onboard capacitors.

Choosing a Mode

  • Strict (banks, payments): fsync per write, group commit for throughput, PLP SSDs.
  • Strong (default RocksDB): fsync the WAL per batch, ack after fsync.
  • Eventually durable (Cassandra periodic, BadgerDB sync=false): fsync every N ms; lose a few seconds on crash.
  • None (caches): no fsync; total memtable loss on crash.

The right choice depends on your latency budget and your client's tolerance for losing recent writes.

Discussion

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

Sign in to post a comment or reply.

Loading…