Write-ahead log & durabilityPro · lab evidenceSource-grounded2 real lab transcripts

Write-Ahead Logging: How WAL Guarantees Durability

A transaction is durable when, after COMMIT returns, its effects survive a crash. Flushing every changed data page to disk at commit would be ruinously slow and random. PostgreSQL instead uses Write-Ahead Logging (WAL): before any change reaches a data file, a compact, sequential description of that change is written and flushed to the WAL. The rule (the "write-ahead" rule) is enforced in the buff

Grounded in the source

Where this comes from in postgres/postgres

Each citation was checked against the REL_17_STABLE branch: real file path, symbol present, exact line number. Follow any link to read the code.

src/backend/access/transam/xact.cVerified · REL_17_STABLE

Primary symbol: CommitTransaction · line 2188

src/backend/access/transam/xlog.cVerified · REL_17_STABLE

Primary symbol: XLogFlush · line 2778

Primary symbol: XLogInsert · line 474

src/include/access/xlogdefs.hVerified · REL_17_STABLE

Primary symbol: XLogRecPtr · line 21

The one thing to understand first

A transaction is durable when, after COMMIT returns, its effects survive a crash. Flushing every changed data page to disk at commit would be ruinously slow and random. PostgreSQL instead uses Write-Ahead Logging (WAL): before any change reaches a data file, a compact, sequential description of that change is written and flushed to the WAL. The rule (the "write-ahead" rule) is enforced in the buffer manager and is the foundation of crash recovery.

PostgreSQL makes the log durable, not the data — the data pages catch up later. That single inversion explains why commits are fast, why a crash is recoverable, why replication is just "ship the log," and why a stuck WAL consumer can fill your disk.

The LSN: a byte address in the log

Every WAL record has a Log Sequence Number (LSN) — a 64-bit monotonically increasing byte offset into the logical WAL stream, represented as XLogRecPtr in src/include/access/xlogdefs.h. Each data page header stores the LSN of the last WAL record that modified it (pd_lsn). The buffer manager will not flush a dirty page to disk until the WAL up to that page's pd_lsn has been flushed — that single check is the write-ahead guarantee.

Lab-verified
SQL
SELECT pg_current_wal_lsn();          -- current insert position
SELECT pg_walfile_name(pg_current_wal_lsn());  -- which 16MB segment
Real captured output for this query is in the Pro lab notebook below.

Inserting a WAL record

Modifying code (heap, btree, etc.) builds a WAL record and calls XLogInsert() (src/backend/access/transam/xloginsert.c). The record describes the change as a redo action: enough information to re-apply it during recovery. Records are appended into shared WAL buffers; XLogInsert returns the LSN of the end of the record.

The first time a page is modified after a checkpoint, PostgreSQL writes a full-page image (FPI) into WAL. This protects against torn pages — a partial 8KB write during a crash — because recovery can restore the entire page from the log. Full-page writes are why WAL volume spikes right after a checkpoint.

What happens at COMMIT

On commit, RecordTransactionCommit() (src/backend/access/transam/xact.c) writes a commit WAL record and then calls XLogFlush() to force the WAL up to the commit LSN out to durable storage via fsync. Only after that flush succeeds does the backend report success to the client. The data pages themselves may still be dirty in shared buffers — they will be written lazily by the background writer or the next checkpoint.

The cost of that fsync is why synchronous_commit = off is so much faster: it lets the backend return before the flush, trading a tiny window of potential loss for throughput. It does not risk corruption — only the loss of the most recent commits.

Checkpoints bound recovery time

A checkpoint (CreateCheckPoint() in src/backend/access/transam/xlog.c) flushes all dirty buffers and records a checkpoint WAL record. After a crash, recovery only needs to replay WAL from the last checkpoint's redo point forward, because everything before it is known to be on disk. checkpoint_timeout and max_wal_size control how often this happens, trading steady-state I/O against recovery time.

Crash recovery: REDO

On startup after an unclean shutdown, StartupXLOG() reads the control file (pg_control) to find the redo point, then replays each WAL record by dispatching to that resource manager's rm_redo callback (for example, heap_redo). Each record is idempotent against its target page: the redo routine checks the page LSN and skips records already reflected on the page. When replay reaches the end of valid WAL, the database is consistent and opens for connections.

Layer 3 — Watch it happen on your own database

Lab-verified · corrected
SQL
-- See WAL generation rate and replay position
SELECT pg_size_pretty(pg_wal_lsn_diff(pg_current_wal_lsn(), '0/0')) AS total_wal;
SELECT slot_name, restart_lsn, pg_size_pretty(
       pg_wal_lsn_diff(pg_current_wal_lsn(), restart_lsn)) AS retained
FROM   pg_replication_slots;
Real captured output for this query is in the Pro lab notebook below.

Run an UPDATE, then call pg_current_wal_lsn() again — the LSN advances by the size of the WAL you just generated. The second query is the one you reach for in an incident: a slot whose retained bytes keep climbing is the reason your disk is filling.

Layer 4 — The levers this hands you

  • WAL is also the replication stream. Streaming replication ships these same records to standbys, which run the redo path continuously — so WAL volume is replication bandwidth.
  • Archiving WAL enables PITR. A base backup plus archived WAL lets you replay to any point in time; archive_command/archive_library is what makes that possible.
  • WAL bloat has real causes. An unconsumed replication slot or failing archive_command prevents segment recycling and can fill the disk — cap the damage with max_slot_wal_keep_size.
  • **synchronous_commit is a throughput dial, not a safety switch.** Turning it off skips the commit fsync wait and risks only the last few commits — never corruption.

Layer 5 — What an Oracle DBA should expect vs what they get

WAL is Oracle's redo log under a different name, with a few sharp differences:

  • One log, not redo + UNDO. Oracle separates redo (for roll-forward) from UNDO (for rollback and read consistency). PostgreSQL's WAL is roll-forward only; rollback and MVCC are handled by dead tuples in the heap, so there is no UNDO log to size or tune.
  • Full-page images instead of a fractured-block fix-up. The first change to a page after each checkpoint writes the whole page into WAL to defeat torn writes — which is why Postgres WAL volume spikes right after a checkpoint. Oracle's equivalent concern (fractured blocks) is handled differently; the post-checkpoint WAL surge surprises Oracle DBAs.
  • LSN is your SCN. The XLogRecPtr LSN plays the role of Oracle's SCN as the global progress marker, but it is a literal byte offset into the log — you can subtract two LSNs to get bytes, which is how you measure replication lag and WAL rate directly.
  • No separate archiver process by default tuning. Archiving is a command/library you configure, not a background ARCn you manage; getting archive_command right (and monitored) is the Postgres equivalent of babysitting the archive destination.

Key takeaway

Durability in PostgreSQL is a property of the log, not the data files. XLogInsert() appends a redo description, XLogFlush() forces it to disk at commit, and the dirty data pages are written lazily afterward. Crash recovery replays WAL forward from the last checkpoint's redo point. Internalize "make the log durable, ship the log, replay the log" and replication, PITR, and WAL-disk incidents all become the same story told three ways.

Interview lens

How this shows up in interviews

What an interviewer is actually testing, the same mechanism seen from three roles, and the gap between a shallow answer and one that shows real depth.

What the interviewer is testing

Whether you understand why WAL exists — turn scattered random page flushes into one sequential flush — and the exact write-ahead rule and commit/fsync path.

Questions you should be able to answer

  • After COMMIT returns, what guarantees durability if the changed data pages aren't on disk yet?
  • State the write-ahead rule.
  • What does synchronous_commit = off actually trade away?

Database engineer

You treat commit latency as WAL fsync latency: put pg_wal on fast storage and tune wal_buffers, commit_delay, and synchronous_commit deliberately.

Software engineer

You know a returned COMMIT means the WAL record is flushed, not that data files are updated; with synchronous_commit=off a crash can lose the last moments even though COMMIT returned.

Feature builder

You set per-transaction synchronous_commit=off for non-critical high-volume writes (telemetry) to cut latency, keeping it on for money movements.

Shallow answer

'WAL is the transaction log used for recovery.' Names it but misses the write-ahead rule and why it's fast.

Answer that shows depth

Before any change reaches a data file, a compact redo record describing it is written to WAL and, at commit, fsync'd — the write-ahead rule, enforced in the buffer manager. Durability comes from that single sequential flush, not from writing scattered 8KB pages; the pages are written later by the checkpointer/bgwriter and, after a crash, reconstructed by replaying WAL from the last checkpoint. synchronous_commit=off returns COMMIT before the WAL flush, trading a small bounded window of possibly-lost-but-still-consistent transactions for much lower commit latency.

Pro lab notebook

The captured run behind every query

Every SQL query above is real and the source citations are verified. Pro unlocks the captured psql output — each query re-run against a real PostgreSQL 17.10 lab, with its verbatim result.

Pro lab notebookTested, not just explained

2 real lab transcripts for this lesson

The explanation and the SQL above are free — so are the verified source citations. What Pro unlocks is the proof: every query in Write-Ahead Logging: How WAL Guarantees Durability re-run against a real PostgreSQL 17.10 instance, with its verbatim psql output captured line for line.

What Pro unlocks here

  • Every query in this lesson re-run against a real PostgreSQL 17.10 lab
  • The verbatim psql output — page headers, row versions, plan nodes, catalog rows
  • The corrected form of any query the source lesson got wrong, re-captured
  • A copy-ready notebook you can replay yourself to confirm every claim
Unlock with Pro — $24.99/moor $199/yr — save ~$100

How this was verified

This explanation is anchored to PostgreSQL's source tree — the files linked above, on the REL_17_STABLE branch — and cross-checked against the official documentation for versions 15 through 18. Every SQL query shown was executed and its output captured in a throwaway PostgreSQL 17.10 lab; where a query needed correcting to run, that correction is noted inline.

Official documentation: §30.3 Write-Ahead Logging (WAL)