SD System Design In Depth
0%
lesson system-designrelational-databasesrecovery

Database WAL and recovery

Module 2.1: Relational Modeling & Query Execution Updated Sep 17, 2026
📄 Cheat Sheet
Write-Ahead Logs. The secret to fast database queries.
Ben Dicken Write-Ahead Logs. The secret to fast database queries. Watch on YouTube ↗

Ben Dicken (PlanetScale) animates the WAL write path, fsync, checkpoints and crash replay with excellent visuals: the clearest current explanation.

0:00 / --:--

Under the Hood: Write-Ahead Logging & The ARIES Protocol

When a transaction commits, writing modified data pages to random locations on disk would require dozens of slow random I/O seeks. To achieve both ACID durability and high throughput, relational databases enforce the Write-Ahead Logging (WAL) invariant: No data page may be written to physical disk until the corresponding log entry recording the change is safely flushed to non-volatile storage.

The WAL Commit and Checkpoint Lifecycle

flowchart TD Tx["Client Commit Transaction"] --> WALBuffer["Append Record to In-Memory WAL Buffer"] WALBuffer -->|"1. fsync() to Disk (group commit batches many transactions)"| WALDisk[("WAL Log on Disk: O_APPEND (Sequential)")] WALDisk --> Ack["2. Return COMMIT OK to Client"] subgraph AsyncBackground ["Asynchronous Dirty Page Flusher"] Dirty["Dirty Pages in Shared Buffers (DRAM)"] -->|"3. Lazy write / Checkpoint (PostgreSQL default checkpoint_timeout: 5 min)"| DataFile[("Database Table Pages (NVMe Disk)")] end

Under the Hood: Crash Recovery (ARIES Algorithm)

If power fails, dirty pages in memory are lost. Upon reboot, ARIES-style engines (InnoDB, SQL Server, DB2) execute the three phases of the ARIES protocol (Algorithms for Recovery and Isolation Exploiting Semantics). PostgreSQL uses a redo-only variant: it replays WAL from the last checkpoint's redo point and relies on MVCC to keep uncommitted changes invisible, so it has no undo phase or CLRs.

  • 1. Analysis Phase: Scans the WAL forward from the last successful checkpoint to identify active transactions and dirty pages at the instant of failure.
  • 2. Redo Phase: Rolls the database forward starting from the smallest recLSN in the dirty page table (which can be earlier than the checkpoint), reapplying all changes (even for transactions that were subsequently aborted) to restore the exact physical state before the crash.
  • 3. Undo Phase: Scans backward, rolling back all transactions that were still uncommitted at the moment of the crash and writing Compensating Log Records (CLRs).

Worked example: why group commit matters

An NVMe SSD with power-loss protection completes an fsync in roughly 0.05-0.5 ms. A consumer SSD or cloud network volume may take 1-10 ms.

  • With one fsync per commit at 2 ms per fsync, a single connection manages at most about 500 commits/s.
  • With group commit, the WAL writer flushes all commit records waiting in the buffer with one fsync. If 50 sessions commit within the same 2 ms window, one fsync makes all 50 durable, giving about 25,000 commits/s on the same disk.
  • synchronous_commit = off in PostgreSQL acknowledges before the fsync. You may lose the last few hundred milliseconds of commits (about 3x wal_writer_delay) on a crash, but the database will not corrupt. It is a durability trade-off, not a consistency one.

Crash recovery timeline

sequenceDiagram participant C as "Client" participant B as "Buffer pool" participant W as "WAL on disk" participant D as "Data files" C->>B: "UPDATE row, page dirtied in RAM" B->>W: "Append WAL record LSN 1000" C->>W: "COMMIT: fsync WAL up to LSN 1000" W-->>C: "COMMIT OK" Note over B,D: "Crash before page flush" D->>W: "Restart: find last checkpoint REDO point" W->>D: "Replay records after REDO point" D-->>C: "Database consistent, commit preserved"

Trade-offs in WAL configuration

SettingLower valueHigher value
checkpoint_timeout / max_wal_sizeFaster recovery, more I/O and full-page writesSmoother I/O, longer recovery after a crash
synchronous_commitoff: faster, may lose recent commitsremote_apply: waits for a replica, slower but no data loss on failover
full_page_writesoff: less WAL but torn pages possibleon (default): protects against partial 8 KB writes

How real systems use the log

  • PostgreSQL: redo-only WAL. There is no undo phase, because MVCC makes aborted tuples invisible and vacuum removes them later. The same WAL feeds streaming replication and logical decoding (CDC).
  • MySQL InnoDB: redo log plus separate undo logs, which is closer to ARIES. The binlog is a second, logical log used for replication, coordinated with the redo log via two-phase commit.
  • Aurora and Neon: "the log is the database". Compute ships only WAL to a distributed storage layer, which materializes pages itself.
  • Kafka and Raft: the same append-only log idea generalized to distributed systems.
Check yourself Question 1 of 3
When a commit is durable recall
From: Under the Hood: Write-Ahead Logging & The ARIES Protocol

Under the WAL invariant, at what point can the database safely reply COMMIT OK to the client?

Write-Ahead Log and Crash Recovery

Every change is appended to the log (LSN + checksum) before any data page changes. A commit is acknowledged only after fsync makes its COMMIT record durable. Pages are written lazily, and only once the log is durable up to their page LSN (the WAL rule). Transaction changes reach the page cache at commit (no-steal), so recovery only needs redo.

Log (newest last)
Event log

Key Takeaways

The WAL invariant guarantees durability by flushing sequential append-only logs before dirty data pages touch disk.
Transactions commit as soon as their WAL records are fsynced; data pages are flushed asynchronously in the background.
The ARIES recovery protocol restores consistency after sudden power failure via Analysis, Redo, and Undo phases.
Was this lesson helpful? Spot an issue or have a suggestion? Let us know on GitHub.
Report an issue