ApiaryActive
Try: pause · settings · learn · wipe
← Community / Reading Room
TM
databases · 14 min read

The Mechanics of Write-Ahead Logging

Modern applications—from online banking to real‑time analytics—rely on the guarantee that once a transaction is reported as “committed,” its effects will…

Durability and atomicity are the twin pillars that keep data systems trustworthy when the unexpected strikes. Write‑Ahead Logging (WAL) is the engineering discipline that makes those pillars stand firm, even when power flickers, disks fail, or a rogue process crashes. In this article we pull back the curtain on WAL, walk through every step a system takes to survive a crash, and show how the same principles echo in bee colonies and self‑governing AI agents.

Modern applications—from online banking to real‑time analytics—rely on the guarantee that once a transaction is reported as “committed,” its effects will never disappear. That guarantee is the Durability part of the ACID properties. Atomicity ensures that a transaction’s changes are all‑or‑nothing, even if the system halts midway. Write‑Ahead Logging is the mechanism that enforces both, by forcing every modification to be recorded in a sequential, immutable log before the actual data pages are altered on disk.

When a crash occurs, the log becomes a forensic timeline: each entry tells the recovery engine exactly what happened, in what order, and whether the operation succeeded. By replaying (or undoing) those entries, the system can reconstruct a consistent state without losing any committed work. The elegance of WAL lies in its simplicity—write once, read many—yet its implementation is a tapestry of careful ordering, checksums, checkpoints, and concurrency tricks that together deliver millisecond‑level latency while safeguarding terabytes of data.

Below we dive deep into the mechanics, from the low‑level layout of a log record to the high‑level policies that keep the log from growing without bound. Along the way we sprinkle concrete numbers, real‑world examples, and occasional analogies to bee colonies and autonomous AI agents, because nature often mirrors the same resilience patterns we engineer into software.


1. Foundations: ACID, Transactions, and the Need for a Log

At the heart of any relational or key‑value store lies the transaction—a logical unit of work that must appear to execute atomically. The classic ACID acronym (Atomicity, Consistency, Isolation, Durability) was first coined by Jim Gray in the 1970s to describe the guarantees a database should provide.

  • Atomicity: All or nothing. If a transaction writes to three tables, either all three writes persist or none do.
  • Durability: Once a transaction reports success, its effects survive power loss, hardware failure, or OS crash.

Without a log, guaranteeing durability would require writing every modified page to stable storage before acknowledging the transaction. On a system with 10 GB of dirty pages, that could mean a multi‑second pause—unacceptable for interactive workloads.

Enter the Write‑Ahead Log: a sequential file (or set of files) that records intention before action. The process looks like this:

  1. Begin Transaction – assign a unique Transaction ID (XID).
  2. Generate Log Records – for each data modification, create a log entry containing the XID, the page identifier, the before‑image (optional), and the after‑image.
  3. Flush Log to Stable Media – issue an fsync or equivalent to guarantee the log entry is on durable storage.
  4. Apply Changes to In‑Memory Buffers – the actual data pages are updated in RAM.
  5. Commit – write a special “COMMIT” record to the log and flush it. Only now does the system tell the client that the transaction succeeded.

Because the log is written before the data pages, the system can later replay the log to bring the database back to a state that includes all committed transactions and excludes any that were only partially applied. This separation of log and data is the core of WAL’s power.

Cross‑link: For a broader view of transaction processing, see transaction-logging.

2. Inside the Log: Records, LSNs, and Checksums

A WAL file is not a free‑form text dump; it is a tightly packed binary format designed for speed and recoverability. Most mature engines share three common concepts:

ComponentPurposeTypical Size
Log Sequence Number (LSN)Monotonically increasing identifier that orders every record.8 bytes (uint64)
Record HeaderStores LSN, transaction ID, record type, length, and a CRC32/CRC64 checksum.12–24 bytes
PayloadThe actual change: either a full page image (physical logging) or a logical operation (e.g., “INSERT key=42”).4 bytes – 8 KB (page size)

Log Sequence Numbers (LSNs)

Every write to the log increments a global counter. In PostgreSQL, the LSN is a 64‑bit integer that can address 2⁶⁴ ≈ 1.8 × 10¹⁹ bytes—more than enough for a petabyte‑scale system that writes 1 GB per second for 500 years. The LSN serves two purposes:

  • Ordering – recovery can replay records in strict LSN order, guaranteeing causality.
  • Visibility – each data page on disk stores the LSN of the last log record that modified it (the “page LSN”). During recovery, if a page’s LSN ≤ the last checkpoint LSN, the page is considered up‑to‑date and can be skipped.

Checksums and Redundancy

A corrupted log entry could cause catastrophic data loss. Therefore each record carries a checksum (CRC32C is common). When the recovery engine reads a record, it recomputes the checksum; a mismatch triggers a log corruption handling path, typically aborting recovery and alerting the DBA. Some systems, like MySQL InnoDB, also store a log block trailer with a log block checksum that validates a whole 512‑byte block, providing early detection of disk errors.

Physical vs. Logical Logging

  • Physical (page‑level) logging writes the entire 8 KB page image to the log. PostgreSQL uses this model; it simplifies recovery because the page can be restored in one step. The trade‑off is larger log volume—roughly the same size as the data itself in the worst case.
  • Logical (operation‑level) logging records the intent (e.g., “INSERT row X”). This can dramatically shrink the log, especially for small updates, but recovery must re‑execute the operation, which may be more CPU‑intensive. SQLite’s WAL mode uses a hybrid approach: it writes whole pages but also stores a small “commit flag” per page to avoid unnecessary writes.
Cross‑link: For a deeper dive into log formats, see wal-format-specs.

3. Crash Scenarios and the Three‑Phase Recovery Process

When a crash strikes—whether it’s a power outage, kernel panic, or a container being killed—the database engine restarts in a recovery mode. The goal: end up with a state that reflects all committed transactions and none of the uncommitted ones. Most systems follow a three‑phase algorithm: Analysis, Redo, and Undo.

3.1 Analysis Phase

The engine scans the WAL from the last checkpoint LSN forward, building an in‑memory Transaction Table that records the state of each transaction (active, committed, aborted). It also constructs a Dirty Page Table (DPT) that tracks which data pages have been modified since the checkpoint. The analysis phase is I/O‑bound but typically completes in a few seconds for a 100 GB log because the log is sequential and can be read at 300 MB/s on modern SSDs.

Example: In PostgreSQL, a checkpoint writes a REDO pointer (the LSN of the first log record that must be replayed) and a RECOVERY pointer (the LSN of the last record that was flushed). During analysis, PostgreSQL reads from the REDO pointer to the end of the WAL, populating the ProcArray and DirtyPageTable.

3.2 Redo (Replay) Phase

Now the engine re‑applies every log record in LSN order to bring the data files up to the point of the crash. For physical logging, this is a simple copy of the payload into the appropriate page buffer, followed by a write‑back to disk. For logical logging, the engine re‑executes the operation (e.g., re‑run an INSERT).

The redo phase must be idempotent: if a record is applied twice, the outcome is the same. This property allows the engine to safely re‑apply records that may have already been flushed to data files before the crash.

Performance tip: many engines use parallel redo, assigning different WAL segments to separate worker threads. PostgreSQL 13 introduced parallel recovery that can achieve up to 2× speed‑up on a 16‑core machine when replaying a 200 GB log.

3.3 Undo (Rollback) Phase

After redo, the database may still contain changes from transactions that never committed. The undo phase walks the Transaction Table backward, applying compensating log records (often called CLR – Compensation Log Records) to revert those pages to their pre‑transaction state.

In InnoDB, each uncommitted transaction’s modifications are stored in undo segments that are themselves logged. During recovery, InnoDB reads those undo logs and applies them in reverse LSN order, guaranteeing that the final state reflects only committed work.

3.4 Time Estimates

SystemTypical Log SizeAnalysis TimeRedo TimeUndo TimeTotal Recovery
PostgreSQL 15 (SSD)20 GB2 s8 s1 s≈ 11 s
MySQL 8.0 (InnoDB, HDD)50 GB12 s45 s8 s≈ 65 s
SQLite WAL (mobile)200 MB0.1 s0.3 s0 s≈ 0.4 s

These numbers illustrate why WAL is essential: even with a 50 GB log, a well‑tuned system can be back online in under a minute, far faster than the alternative of a full data‑file scan.

Cross‑link: For a step‑by‑step guide to crash recovery, see crash-recovery-process.

4. Checkpointing: Keeping the Log Manageable

A WAL that grows without bound would eventually consume all storage and make recovery unbearably slow. Checkpointing is the periodic operation that writes all dirty pages from the buffer pool to the data files and records a stable point in the log from which recovery can safely start.

4.1 How a Checkpoint Works

  1. Flush Dirty Pages – The buffer manager writes each page whose LSN is greater than the last checkpoint LSN to disk.
  2. Write a Checkpoint Record – A special log entry containing:
  • The LSN of the checkpoint record itself.
  • The REDO pointer (first LSN to be replayed after a crash).
  • The UNDO pointer (first LSN of any uncommitted transaction).
  • A snapshot of the Dirty Page Table (optional, used for faster recovery).
  1. fsync the Log – Guarantees that the checkpoint record is on stable media.
  2. Truncate the Log – Once the checkpoint record is safely persisted, the system can discard any earlier log segments that are no longer needed for recovery.

4.2 Frequency and Trade‑offs

Checkpoint frequency is a balancing act:

  • Frequent Checkpoints (e.g., every 5 seconds) keep the log short, reducing recovery time, but they increase I/O load because dirty pages are flushed more often.
  • Sparse Checkpoints (e.g., every 10 minutes) reduce steady‑state I/O but can cause the log to balloon. A 1 TB database that writes 200 MB/s would generate 12 TB of WAL in ten minutes—far beyond typical disk capacities.

Most systems adopt an adaptive checkpoint interval based on a target checkpoint completion time (e.g., 30 seconds). PostgreSQL’s checkpoint_timeout defaults to 5 minutes, but the max_wal_size setting (default 1 GB) forces a checkpoint sooner if the WAL grows beyond that limit. MySQL’s innodb_max_dirty_pages_pct controls how much of the buffer pool may be dirty before a forced checkpoint.

4.3 Log Truncation and Recycling

After a checkpoint, WAL segments older than the REDO pointer can be recycled. PostgreSQL, for instance, uses a pool of 16‑MB WAL segment files named 0000000100000000000000A1. When a segment is no longer needed, it is either deleted (if the filesystem supports fast unlink) or renamed for reuse, avoiding the overhead of creating new files.

In distributed systems, log truncation must be coordinated across replicas to avoid losing entries that a lagging follower still needs. Raft‑based systems keep the log until all followers have persisted the entries, a concept known as log compaction.

Cross‑link: For more on checkpoint tuning, see wal-checkpointing.

5. Performance Optimizations: Group Commit, Parallelism, and Compression

While WAL guarantees safety, it also introduces latency because each commit must wait for an fsync. Over the years, database engineers have invented several techniques to hide or amortize that cost.

5.1 Group Commit

Instead of flushing each transaction’s commit record individually, the engine batches several commits together. The steps are:

  1. Transactions write their log records to the in‑memory WAL buffer.
  2. When the first transaction reaches the commit point, the engine starts a fsync timer (often 1–5 ms).
  3. Any subsequent transactions that commit before the timer expires are appended to the same WAL buffer.
  4. After the timer, a single fsync flushes all pending commits at once.

Group commit can cut the average commit latency dramatically. Benchmarks on a 4‑core Intel Xeon with an NVMe SSD show:

WorkloadIndividual fsync latencyGroup Commit (5 ms window)
1‑row insert3.2 ms0.9 ms
10‑row batch2.8 ms0.7 ms
100‑row batch2.5 ms0.6 ms

The improvement is more pronounced under high concurrency because the probability of multiple transactions arriving within the timer window rises.

5.2 Parallel WAL Writing

On systems with multiple storage channels (e.g., RAID‑10 or a set of NVMe namespaces), the log can be split into segments that are written concurrently. PostgreSQL’s wal\_writers daemon can flush several buffers in parallel, and Oracle’s Log Writer (LGWR) can target multiple disks. This reduces the wall‑clock time for a large batch of log records.

5.3 Log Compression

Because many log records contain repetitive data (e.g., updates to the same page), some engines compress log payloads on the fly. RocksDB, a key‑value store built on the LSM tree, offers ZSTD and LZ4 compression for its WAL. The trade‑off is CPU usage: compressing a 4 KB record with ZSTD at level 3 takes ~0.6 µs on a modern core, which is negligible compared to an SSD’s 0.1 ms write latency.

5.4 Asynchronous Replication and WAL

In a primary‑replica architecture, the primary writes to its local WAL and then ships the same records to replicas. Replication can be:

  • Synchronous – the primary waits for at least one replica to acknowledge receipt of the commit record before confirming to the client. This adds 0.5–2 ms of latency, depending on network distance.
  • Asynchronous – the primary returns immediately; replicas lag behind by a few milliseconds to seconds.

Both modes reuse the same WAL, so the primary’s durability guarantees are unchanged. The replica’s own crash recovery will replay any WAL it received but not yet applied.

Cross‑link: For a guide to tuning WAL performance, see wal-tuning.

6. WAL in Modern Database Engines

6.1 PostgreSQL

PostgreSQL’s WAL is a textbook implementation of physical logging. Each WAL segment is 16 MB, and the system maintains a wal\_writer\_delay (default 200 ms) that determines how often the background writer flushes dirty WAL buffers.

Key numbers:

  • Maximum WAL size (max_wal_size) defaults to 1 GB. If the log exceeds this, PostgreSQL forces a checkpoint regardless of the timeout.
  • Minimum WAL size (min_wal_size) defaults to 80 MB, allowing the engine to shrink the log after a period of low activity.
  • Checkpoint Completion Target (checkpoint_completion_target) defaults to 0.9, meaning PostgreSQL spreads the checkpoint work over 90 % of the checkpoint interval to avoid I/O spikes.

6.2 MySQL InnoDB

InnoDB uses a physical redo log split into a configurable number of log files (default 2) each 48 MB in size (innodb_log_file_size). The total redo log size (innodb_log_buffer_size) defaults to 16 MB, which is the in‑memory buffer before flushing to disk.

Important metrics:

  • Redo Log Throughput – In benchmarks, InnoDB can sustain ~250 k log writes per second on a 2 TB SSD array.
  • Group Commit – InnoDB’s innodb_flush_log_at_trx_commit=2 mode writes the log to the OS cache on each commit and flushes to disk once per second, trading a small durability window for higher throughput.

6.3 SQLite WAL Mode

SQLite’s WAL mode was introduced in version 3.7.0 (2010). It stores one WAL file per database and a shared memory (shm) file that coordinates readers and writers. The WAL file grows in increments of the database page size (default 4 KB).

  • Checkpoint StrategiesPRAGMA wal_checkpoint(TRUNCATE) forces a full checkpoint and truncates the WAL, useful for mobile apps that need to limit storage usage.
  • Performance – On an iPhone 14 Pro, SQLite WAL can handle ~12,000 simple INSERTs per second with a 1 ms average latency, thanks to the fact that most writes stay in RAM and are flushed in batches.

6.4 RocksDB

RocksDB, built on the Log‑Structured Merge (LSM) tree, writes a Write‑Ahead Log for each column family. The WAL is used primarily for crash recovery; the actual data is persisted in immutable SST files.

  • WAL Size – By default, RocksDB caps the WAL at 64 MB (max_total_wal_size). When the limit is reached, it triggers a WAL roll and a background flush to SST files.
  • Durability OptionsdisableWAL = true can be set for bulk loads where durability is not required, achieving up to 10× speed‑up.
Cross‑link: For a comparative table of WAL implementations, see wal-implementations.

7. Beyond Databases: WAL in File Systems, Message Queues, and AI Agents

The same principles that protect a relational table can protect any mutable state that must survive crashes.

7.1 File System Journaling

Modern file systems such as ext4, XFS, and NTFS embed a journal that functions like a WAL. When a file is modified, the file system writes a description of the change (metadata updates, block allocations) to the journal before altering the actual on‑disk structures.

  • Ext4 defaults to a journal size of 1 % of the partition, capped at 128 MB.
  • XFS can allocate a journal up to 10 % of the file system size, allowing multi‑TB journals for high‑throughput workloads.

The result is that after an unclean shutdown, the file system can replay the journal and guarantee a consistent namespace, much like a database’s recovery.

7.2 Message Queues (Kafka, Pulsar)

Apache Kafka stores each message in an append‑only log that is essentially a WAL for a distributed publish‑subscribe system. The log guarantees that once a producer receives an acknowledgment, the message is persisted on disk and can be replayed by any consumer, even after a broker crash.

  • Replication Factor – Kafka replicates each partition’s log across multiple brokers; the leader’s WAL is the source of truth, while followers replay the same log for redundancy.
  • Retention Policies – Kafka can retain logs for days, weeks, or indefinitely, turning the WAL into a long‑term archive.

7.3 Self‑Governing AI Agents

In the Apiary ecosystem, autonomous AI agents maintain internal state (goals, learned policies, sensor histories). To survive a node reboot, an agent writes a state checkpoint to a WAL‑style log:

  1. Intent Log – Records each decision (e.g., “move to flower #42”) before the action is taken.
  2. State Snapshot – Periodically writes a compressed snapshot of the agent’s neural network weights.

If the host crashes,

Frequently asked
What is The Mechanics of Write-Ahead Logging about?
Modern applications—from online banking to real‑time analytics—rely on the guarantee that once a transaction is reported as “committed,” its effects will…
What should you know about 1. Foundations: ACID, Transactions, and the Need for a Log?
At the heart of any relational or key‑value store lies the transaction —a logical unit of work that must appear to execute atomically. The classic ACID acronym (Atomicity, Consistency, Isolation, Durability) was first coined by Jim Gray in the 1970s to describe the guarantees a database should provide.
What should you know about 2. Inside the Log: Records, LSNs, and Checksums?
A WAL file is not a free‑form text dump; it is a tightly packed binary format designed for speed and recoverability. Most mature engines share three common concepts:
What should you know about log Sequence Numbers (LSNs)?
Every write to the log increments a global counter. In PostgreSQL, the LSN is a 64‑bit integer that can address 2⁶⁴ ≈ 1.8 × 10¹⁹ bytes—more than enough for a petabyte‑scale system that writes 1 GB per second for 500 years. The LSN serves two purposes:
What should you know about checksums and Redundancy?
A corrupted log entry could cause catastrophic data loss. Therefore each record carries a checksum (CRC32C is common). When the recovery engine reads a record, it recomputes the checksum; a mismatch triggers a log corruption handling path, typically aborting recovery and alerting the DBA. Some systems, like MySQL…
References & sources
  1. Apiary Reading RoomOpen, cited knowledge base — funded to keep bee & practical research free.
From the Apiary Reading Room. Opinion & editorial — not financial advice. We don't overclaim.
More from the Reading Room