Storage Engine: WAL, Checkpoints and Crash Recovery
Why databases log before they write, how a commit becomes durable, what recovery replays, and how the same log drives replication.
- log firstRule: log before data page
- 500 → 50,000Commits a second: solo versus group of 100
- 1 fsyncDisk wait on the commit path
Show what to say
“A database appends a small sequential log record before it changes a data page, and a commit is durable once that record is fsynced. Dirty pages are flushed lazily, a checkpoint bounds how much log recovery must replay, and recovery redoes committed work and discards the rest. The same log, shipped to replicas, drives replication.”
Trade-offs
- Frequent vs rare checkpoints: frequent ones shorten recovery but add page-write load; rare ones leave a long replay.
- Synchronous vs asynchronous replica: synchronous loses no commits on failover but waits on the network; asynchronous is fast and can lose the tail.
- Strict fsync vs relaxed commit: fsync every commit is safest; relaxing it raises throughput and risks the last few milliseconds.
Bring it up before they ask
A full log disk or a stuck replica that stops log recycling stops all writes, so alert on log size and replication lag.
A database writes a small, sequential log record before it changes data pages, so a commit is durable once the log is flushed. After a crash, recovery replays the log from the last checkpoint, and the same log, shipped to other servers, is .
Why a database needs a log
Data pages are slow to write in place, so the database keeps hot ones in a buffer pool and flushes later. A committed change can exist only in RAM.
Read more · 84 words
The fix is the (WAL): before a change may reach a data page, a small record of it is appended to the log, and the commit is acknowledged once that record is on disk.
The write path
The only disk wait in a commit is the log flush (fsync), which caps commits per second. Group commit shares one fsync: at 2 ms, one at a time allows 500 a second, batches of 100 allow 50,000.
Tap any box to see what it is and what it talks to.
- Client
- Cache
- Object storage
- Database
Text description
- UPDATE changes the page in memory. It is now dirty, but not on disk.
- A small log record describing the change is appended to the log.
- COMMIT: the log is flushed to disk (fsync), then the client is told it worked.
- Later, in the background, dirty pages are written to the data files. A checkpoint records how far this has got.
Checkpoints and crash recovery
A checkpoint flushes dirty pages and records a log position. Everything before it is on disk, so recovery starts there. Frequent checkpoints mean faster recovery but more write work.
Read more · 30 words
Recovery redoes committed changes the data files lack and ignores a torn tail with no commit record. Replay is harmless to repeat, so a crash during recovery is safe too.
// A toy database with a write-ahead log. `memory` is the buffer pool and is lost in a crash.
// `disk` holds data pages and is updated lazily. `log` is durable: appending to it is the fsync.
// A transaction counts as committed only when its commit marker is in the log.
export class Db {
constructor() {
this.disk = {};
this.log = [];
this.memory = {};
this.nextTx = 1;
}
begin() {
return { id: this.nextTx++, writes: [] }; // private until commit
}
set(tx, key, value) {
tx.writes.push({ tx: tx.id, key, value });
}
// Log first, then memory. `tornAfter` simulates a crash while the log was being written.
commit(tx, { tornAfter = null } = {}) {
const records = tx.writes.slice(0, tornAfter ?? tx.writes.length);
this.log.push(...records);
if (tornAfter !== null) return this.crash(); // died before the commit marker was written
this.log.push({ tx: tx.id, commit: true }); // <- the commit point
for (const { key, value } of tx.writes) this.memory[key] = value; // data pages are only dirty in memory
}
// Flush every page and forget the log up to here: recovery will start from this point.
checkpoint() {
this.disk = { ...this.disk, ...this.memory };
this.log = [];
}
crash() {
this.memory = {}; // RAM is gone; disk and log survive
}
// Start from the data pages, then redo every transaction that has a commit marker.
recover() {
const committed = new Set(this.log.filter((r) => r.commit).map((r) => r.tx));
this.memory = { ...this.disk };
for (const r of this.log) if (!r.commit && committed.has(r.tx)) this.memory[r.key] = r.value;
}
get(key) {
return this.memory[key];
}
}
// Each commit waits for one fsync. Group commit lets many transactions share it.
export const commitsPerSecond = (fsyncMs, groupSize = 1) => (groupSize * 1000) / fsyncMs;The replication log
The log is an ordered record of every change, so a applying the same records ends up with the same data.
Read more · 98 words
- Physical shipping sends page-level records (Postgres streaming): exact copies.
- Logical shipping sends row changes (MySQL binlog): works across versions and feeds change-data-capture.
A synchronous replica confirms before the commit is acknowledged: no loss on , but each commit waits on the network. An asynchronous one lags and can lose the last . More in the HLD path.
What fsync really means
write() only hands data to the operating system's page .
Read more · 75 words
Durability needs fsync, and disks and controllers can still hold data in a volatile cache unless they are told to flush or are battery-backed.
- Relaxed durability. Postgres
synchronous_commit = offacknowledges before the flush: a crash can lose the last few milliseconds of commits but never corrupts the database. - Torn pages. A crash can leave an 8 KiB page half written. Engines guard against it with full-page images in the log or a doublewrite buffer.
Log-structured and copy-on-write engines
Some engines make the log the data.
Read more · 115 words
An LSM-tree appends to a log and sorted files and never updates in place, as in the indexes lesson. Copy-on-write (LMDB) write new pages and switch a root pointer atomically, so they need no separate . is an append-only log offered as a product.
Backups and point-in-time recovery
A base backup plus the archived log gives point-in-time recovery: restore the backup, then replay the log up to just before the bad DELETE.
Read more · 115 words
Keeping the log is what turns "we have last night's backup" into "we can restore to 14:32:05".
Follow-up questions
What is a write-ahead log and why does it make commits durable?
A record of each change is appended to a sequential log before the page changes, and the commit is acknowledged once it is flushed. After a crash the log can redo the change.
What happens when a database restarts after a crash?
It starts from the last checkpoint, replays the log to redo committed changes, discards with no commit record, then accepts connections.
Synchronous or asynchronous replication?
Synchronous loses no committed data on but adds network to every commit. Asynchronous is faster but can lose the last transactions.
Check yourself
Question 1 of 7Score 2 out of 3 or better to mark this page complete.