fivenines
19/25

Guided Problem

MiniPG Build 13: Reopen Only After Crash State Is Coherent

Time
30m
Level
advanced
Artifacts
not specified
Progress0%

Build Your Own PostgreSQL

Checkpoints and Crash Recovery

Time
6 min
Prerequisites
Vacuum And The Cost Of History

What You Will Learn

  • Explain why a checkpoint bounds redo work but does not make commits durable
  • Trace recovery by comparing WAL positions with page state
  • Connect full-page images, transaction status, and WAL retention to recovery
checkpointredo pointcrash recoverypage LSNredofull-page imagetorn pageWAL retention

Crashes are part of the design

Power fails after the bookstore commits several orders. Their WAL is durable, some table pages are current, other pages are old, and every shared buffer has vanished. This uneven disk state is expected under WAL. Crash recovery reconstructs a coherent state, while checkpoints keep the amount of WAL that must be reconsidered within an operational bound.

A checkpoint is a recorded point in the WAL stream where the database has flushed enough dirty data pages that recovery can start from there instead of from the beginning of time. It does not mean every later change is on disk. It means all changes before the checkpoint's redo position are safely represented in data files, so redo only needs to consider WAL after that point. The checkpoint record tells recovery where to begin.

The recovery starting line

During normal operation, the checkpointer writes dirty buffers to disk gradually and emits checkpoint records. This work has to be paced. Checkpoints that are too aggressive create heavy write bursts that compete with user queries. Checkpoints that are too infrequent force crash recovery to replay a large amount of WAL, which extends downtime. Checkpoint tuning balances steady-state I/O against restart time.

Recovery timeline

Rendering diagram…

Trace redo against page state

Recovery locates the checkpoint's redo point and reads WAL forward. For each relevant record, it obtains the target page and compares the page's log sequence number with the record. If the page already reflects that record or a later one, redo skips it. If the page is older, redo reapplies the described change and advances page state. This makes replay safe across the arbitrary mix of flushed and unflushed pages left by a crash.

Commit durability did not wait for this checkpoint. It came from flushing required WAL at commit. The checkpoint supplies a trustworthy starting line for later redo by ensuring older changes are represented in data files. Confusing those contracts leads to a dangerous model in which commits become safe only at periodic checkpoints; PostgreSQL's WAL contract makes them recoverable earlier.

Redo makes uneven pages coherent

A system with no checkpoints turns recovery into a long, expensive process. If it keeps WAL forever, recovery has to replay from the first log record still needed to construct the database, and that span can be enormous. A system that checkpoints by stopping all work and flushing everything immediately has the opposite problem: recovery is simple, but foreground latency suffers. A practical database checkpoints concurrently, accepting extra complexity to avoid freezing the server.

Redo does most of the work in crash recovery. Recovery reads WAL records and reapplies changes when the corresponding data page does not already reflect them. Page log sequence numbers make redo idempotent. If a page already includes a change, recovery skips it; if not, recovery applies it. This matters because a crash may happen after some data pages were flushed and before others. Recovery has to be safe whether each page is old or new.

Commit records decide which transactions count as durable. If a transaction's commit record was flushed before the crash, recovery preserves its effects. If not, its changes must not become visible as committed work. In an MVCC design, uncommitted tuple versions may physically exist after recovery, but transaction status marks them aborted or in progress and later resolved as aborted. Cleanup removes them afterward. The recovered database can hold physical traces of failed work without exposing them as truth.

Torn pages and timelines

Full-page images solve a subtle storage problem. A page write can be torn, meaning only part of the page reaches disk before failure. If recovery then tries to redo changes against a corrupted page image, it may not have a valid base. By logging a full-page image the first time a page changes after a checkpoint, the database can restore the page to a known state before applying later records. This costs WAL space but protects against partial writes.

Recovery runs in phases. It locates the checkpoint, reads WAL, reconstructs transaction status, replays records, and reaches a consistent point. In archive or standby scenarios, it may continue applying WAL beyond local files. In point-in-time recovery, it stops at a requested target. The same basic machinery supports ordinary crash restart, backups, replicas, and time travel operations.

Checkpoints and WAL retention are closely connected. WAL before a checkpoint may still be needed for replication, backups, or point-in-time recovery even if crash recovery no longer needs it locally. The system has to know which consumers require old WAL segments. Removing them too early can break a standby or invalidate a backup chain. Keeping them forever fills disks. Retention is one more thing the system must coordinate.

Crash recovery succeeds when users never notice it. The server starts, replays, cleans up transaction state, and opens for connections with committed data intact. That quiet restart depends on every earlier layer: WAL records, buffer flush rules, page LSNs, checkpoints, tuple visibility, and transaction status.

Separate three recovery claims

Commit records which history must survive, a checkpoint bounds where redo begins, and each page LSN says how much of that history the page already contains. Recovery combines the three. None substitutes for another, and keeping them separate makes both restart behavior and checkpoint tuning understandable.

Into catalogs

So far, the database has stored user data and maintained physical truth. It also stores the definitions that make user data intelligible: tables, columns, types, indexes, functions, and permissions. The next lesson turns to catalogs, the database's own self-description.

Recovery can restore bytes, but bytes alone do not make a relational system. The server also needs durable meaning: which bytes belong to which table, which columns exist, and which operations are valid. That meaning lives in metadata that has to be protected with the same seriousness as user rows.

Next step

See what actually stuck.

Take the practice scenarios now.