The crash-consistency problem
Updating a file may touch inode, bitmap, and data block separately. Power loss mid-update leaves the file system inconsistent — free block referenced twice, or allocated block orphaned. FSCK repairs offline; journaling prevents the need.
Write ordering without journaling
A single append may require:
- Allocate data block (bitmap)
- Write data
- Update inode size/pointer
Crash after step 1 but before 3 → leaked block. Crash after 3 but before 2 → file points at garbage. Production systems need atomicity for metadata transitions.
Journaling protocol (simplified)
- Write intended changes to journal (log) on disk.
- Commit journal entry (single fsync of log).
- Apply changes to main file system structures (checkpoint).
- Mark journal entry complete.
Recovery after crash: replay committed journal entries. Same pattern as database WAL.
Data journaling vs metadata journaling
| Mode | Journals | Trade-off |
|---|---|---|
| Metadata only (ordered) | Inode/bitmap changes | Fast; data may be stale on crash |
| Full data journaling | Data + metadata | Safer; 2× write traffic |
ext4 defaults to ordered mode — data flushed before metadata commit.
fsync and durability
write() returns when data hits the page cache — not necessarily disk. fsync(fd) forces dirty pages and metadata to stable storage. Databases call fsync on commit; that's the latency you pay for durability.
Senior-level signal
"Postgres is slow on ext4" often traces to barrier=0 history or cloud volumes lying about flush — not query plans. Benchmark with pg_test_fsync. For application logs, batch writes and accept loss on crash rather than fsync per line.
Where this goes next
Virtual Machines vs Containers shifts from persistence to isolation — how workloads share a kernel without sharing each other's view of processes, network, and filesystem.
Further Reading
Hands-On Tasks (Optional)
Low-setup exercises on your local machine. No autograding — the goal is to build intuition, not pass a test.
- Measure buffered vs fsync'd writes15m
Python: write 10MB to a file with `f.write(data)` and time it; then repeat with `f.write(data); os.fsync(f.fileno())`. fsync should be slower — that's the cost of durability.