NEW: ML Mock & Coaching with a 20+ year experience coach. Save $20 on sessions for Premium.ML Mock & Coaching now available
NEW: ML Mock & Coaching with a 20+ year experience coach. Save $20 on sessions for Premium.ML Mock & Coaching now available
Understand how write-ahead logging ensures durability and crash recovery in databases, streaming systems, and distributed storage engines.
Finished this lesson?
Sign in to save your progress across devices.
Tip: use ← and → to move between lessons.
Was this lesson helpful?
Rehearse this with a Staff+ engineer
45-minute mock with a Staff+ engineer who interviews at your target company. Premium members save $20.
Every database you've ever used — PostgreSQL, MySQL, SQLite — relies on the same fundamental trick to guarantee your data survives a crash: before changing anything on disk, write the change to a sequential, append-only log first. This is Write-Ahead Logging (WAL).
The idea is deceptively simple. If the system crashes after writing to the log but before updating the actual data files, it can replay the log on startup and finish the job. If it crashes before the log entry is complete, the partial entry is safely discarded. Either way, you never end up with half-written, corrupted data. WAL is the reason databases can promise durability — and understanding it unlocks how replication, LSM-trees, and streaming systems like Kafka all work under the hood.
Without WAL, a crash mid-write leaves data in a corrupted, half-written state — the torn page problem. Consider a simple bank transfer: debit $100 from Account A, credit $100 to Account B. If the system crashes after the debit but before the credit, the money vanishes. The database is now inconsistent, and there's no way to know what operation was in progress.
WAL eliminates this class of failure entirely. The full operation is logged before any changes are applied, so the system always knows exactly where it left off.
| Crash Scenarios: Without WAL vs With WAL | ||
|---|---|---|
| Scenario | Without WAL | With WAL |
| Crash during write | Partial/corrupted data | Replay log, complete the write |
| Power failure | Lost in-flight transactions | All committed changes recovered |
| Disk full mid-write | Inconsistent state | Log entry incomplete, safely discarded |
The write path follows a strict sequence designed to guarantee that the log is always ahead of the data:
Why is WAL fast? Sequential writes to the WAL are 10–100x faster than random writes to data files. A B-tree database would need to seek to scattered pages across disk to update indexes and data in place. WAL turns all of that into a single sequential append — then handles the slow, random I/O in the background when it's convenient.
The core value of WAL is what happens after a crash. On startup, the system reads the log from the last checkpoint and re-applies every entry to reconstruct the state that was in memory when the crash happened.
The replay process works as follows:
Each WAL entry must be safe to replay multiple times. If the system crashes during recovery itself, it simply replays from the same checkpoint again. This is why WAL entries typically contain the final value, not a delta — SET balance = 150 instead of balance += 50. Replaying SET balance = 150 ten times still leaves the balance at 150. Replaying balance += 50 ten times would not.
Every WAL entry gets a monotonically increasing sequence number called an LSN. The checkpoint records the LSN it flushed up to. Recovery starts from that LSN and moves forward. This is how PostgreSQL, MySQL/InnoDB, and Kafka all track position in the log — the LSN is the universal "where am I?" marker.
Not all logs work the same way. The two main approaches differ in what they record and how recovery works:
| Log Types Compared | |||
|---|---|---|---|
| Log Type | What It Records | Recovery Action | Used By |
| Redo log | New values after the change | Replay forward to re-apply committed writes | PostgreSQL, RocksDB, Kafka |
| Undo log | Old values before the change | Roll back uncommitted transactions | InnoDB (combined with redo) |
| Combined (ARIES) | Both old and new values | Redo committed, undo uncommitted | |
Connection to Event Sourcing: Log replay is conceptually identical to event replay in Event Sourcing and CQRS — both rebuild state by sequentially applying a log of changes. The WAL is a redo log of physical changes; an event store is a redo log of domain events.
Without cleanup, the WAL grows indefinitely. A checkpoint solves this: flush all in-memory dirty pages to the permanent data files, record the LSN of the last flushed entry, then truncate (or archive) all WAL entries before that point.
The checkpoint frequency is a classic trade-off:
| Checkpoint Frequency Trade-offs | |||
|---|---|---|---|
| Checkpoint Frequency | WAL Size | Recovery Time | Write Performance |
| Every 1 minute | Small | Fast | Slightly slower (more flushes) |
| Every 10 minutes | Medium | Moderate | Good throughput |
| Every 1 hour | Large | Slow (replay more log) | |
Most production databases default to checkpointing based on WAL size (e.g., PostgreSQL triggers a checkpoint when the WAL reaches max_wal_size, default 1 GB) or time interval, whichever comes first.
| WAL Across Real Systems | ||
|---|---|---|
| System | WAL Implementation | Key Detail |
| PostgreSQL | WAL (pg_wal) | Supports streaming replication by shipping WAL segments to replicas |
| MySQL (InnoDB) | Redo log | Circular buffer of fixed size; doubles as crash recovery and replication source |
| Kafka | Commit log | The log IS the data store; consumers read from it directly |
| RocksDB / LevelDB | WAL + memtable | |
Disk I/O. WAL benefits from fast sequential I/O. In production, it's common to place the WAL on a separate disk or SSD from the data files so that WAL appends don't compete with random reads from queries.
fsync trade-offs. Calling fsync on every single write gives maximum durability but adds latency. Batching multiple writes into a single fsync (group commit) is faster but risks losing the last few writes on crash. PostgreSQL exposes this as the synchronous_commit setting — turning it off can give 2–3x write throughput at the cost of potentially losing the last ~10ms of transactions after a crash.
WAL archiving. For point-in-time recovery (PITR), WAL segments are archived to external storage before truncation. Combined with a base backup, this enables restoring the database to any moment in time — not just the last checkpoint but any arbitrary point.
Replication. WAL is the foundation of streaming replication. The primary ships WAL entries to replicas, which replay them to stay in sync. This is how PostgreSQL streaming replication, MySQL binary log replication, and Kafka consumer groups all work — the log is the replication protocol.
See it in practice: In Scaling Writes, the LSM-tree write path appends to a WAL before writing to the memtable — this is what makes append-only storage engines fast. In Time-Series Metrics Ingestion, the TSDB write-optimized path uses the same WAL + memtable + compaction architecture. The Event Sourcing and CQRS pattern uses an append-only event log that is conceptually identical to a WAL — every state change is logged before being materialized into read models. In Payment Systems, WAL-backed transactions ensure that financial operations are never partially applied.
WAL rarely appears as a standalone interview question, but understanding it is essential for explaining how databases achieve durability, how replication works, and why LSM-trees are fast for writes. When an interviewer asks "how does the database ensure writes aren't lost?" — the answer starts with WAL.
If you're designing a system that requires crash recovery, exactly-once processing, or write-optimized storage, you should be able to sketch the WAL write path and explain why sequential appends followed by background flushes outperform random in-place updates. That mental model applies to everything from a single PostgreSQL instance to a distributed Kafka cluster.
| InnoDB, SQL Server |
| Maximum throughput |
| WAL protects the memtable; once flushed to SSTable, WAL is discarded |
| SQLite | WAL mode | Readers don't block writers; WAL enables concurrent reads during writes |
| etcd / Raft | WAL | Every state change logged before applying to state machine; enables consensus replay |