Reading — step 1 of 3
fsync, Group Commit & Durability Modes
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
| Mode | Survives | Cost |
|---|---|---|
| In-memory only | Nothing | Free |
write() (no fsync) | App crash; NOT kernel/power/disk failure | Cheap |
fsync() per write | Power loss after ack | ~0.1–1 ms per call |
fsync() + O_DIRECT, disabled drive write cache | Catastrophic failures, with battery-backed devices | Even 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:
- Application buffer (e.g. Python's
io.BufferedWriter): cleared byflush(). - OS page cache: cleared by
fsync/fdatasync. - Device write cache (DRAM on the SSD/HDD): may need
hdparm -W 0or 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, BadgerDBsync=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…