Chapter 09 · Storage Engines: WAL, B-Trees, LSM Trees, Memtables, SSTables, and Compaction

Write-Ahead Logs and Durability Before Mutating Indexed State

Trace an AtlasMart write through write-ahead logging, durability acknowledgement, page mutation, checkpointing, and crash recovery, including the concrete data-loss window created by acknowledging before durable log persistence.

Intermediate95–115 minutesWAL durability + crash-recovery labPython 3.13+ · standard libraryVendor-neutral mechanismsLast reviewed: August 2026

Learning outcomes

AtlasMart's checkout service acknowledges an order update in 12 ms, but after a host crash the order returns to its previous state. The application log says “success”; the recovered database says otherwise. This lesson makes the missing boundary visible: acknowledgement is a durability claim only when the required recovery information has crossed the storage system's durable boundary.

01

Define a write-ahead log (WAL), log sequence/order, dirty page, checkpoint, flush, fsync, group commit, REDO, and durability boundary before relying on them.

02

Trace a write from WAL record construction through durable log flush, indexed-state mutation, acknowledgement, checkpoint, and crash recovery.

03

Distinguish logical versus physical logging conceptually without pretending every database uses one universal record format.

04

Produce and diagnose an acknowledged-write loss caused by acknowledging before the durable log boundary.

05

Reason about fsync policy, group commit, replication, failure domains, and recovery evidence in production.

Tooling and version snapshot · checked 29 August 2026

The mandatory lab uses Python 3.13+ standard library only; it has no database server, driver, container, cloud, or paid-feature dependency. Optional implementation references were rechecked against PostgreSQL 18 documentation (PostgreSQL License), SQLite 3.53.4 (public domain), Apache Cassandra 5.0.9 (Apache License 2.0), and RocksDB 11.1.2 (Apache 2.0 or GPLv2 at the user's option). Product defaults are examples, not portable storage-engine guarantees.

Prerequisite connection

Chapters 05 and 06 treated replication acknowledgements and quorums. Those acknowledgements ultimately depend on each replica's local durability semantics. “Two replicas acknowledged” is incomplete unless you know what each replica had persisted when it replied.

1. The WAL rule: log durable information before dependent data pages

A write-ahead log is an append-oriented record of changes sufficient for recovery. The core ordering rule is not “write everything twice”; it is that recovery information describing a change reaches the required durable medium before a data/index page whose correctness depends on that change is considered safely persistent. A dirty page is an in-memory page that differs from its persisted copy. A checkpoint records a recovery boundary so replay need not start from the beginning of history.

For an AtlasMart order transition PENDING → PAID, a simplified path is: validate the invariant; assign transaction/log identity; append a WAL record; flush/synchronize that WAL according to the configured durability contract; update buffer/index state; acknowledge; later write dirty pages/checkpoint. After a crash, the engine replays durable log records not yet reflected in data pages. This is the REDO intuition described by PostgreSQL's WAL documentation.

Stage Observable evidence What it proves
WAL append log record / LSN / byte offset The change is represented; append alone does not prove durable media persistence.
WAL fsync / durable flush sync completion, durable offset, storage metrics Under the OS/device contract, preceding WAL bytes crossed the configured durability boundary.
Buffer/index mutation dirty-page state, cache contents Current process can read the new value; it may still vanish on crash.
Acknowledgement client-visible success Only the documented acknowledgement policy—not the word “success”—defines the durability guarantee.
Checkpoint checkpoint LSN/position, flushed pages Recovery can begin from a later consistent boundary; it is not a substitute for WAL ordering.

2. Logical and physical logging are recovery strategies, not API slogans

Physical logging describes low-level changes to storage structures/pages; logical logging describes higher-level operations such as inserting or updating a record. Real engines may mix approaches, log physiological changes, or maintain multiple log types. The useful design question is: what information is needed to redo/undo or reconstruct a valid state after interruption, and what ordering constraints exist between that information and data pages?

Do not infer log semantics from a file named “wal”. A storage engine may use separate commit logs, manifests, journals, double-write buffers, or copy-on-write structures. The mechanism must be verified from implementation documentation and crash tests.

3. Group commit amortizes synchronization without erasing durability semantics

Group commit lets several transactions share one durable synchronization. If transactions T3 and T4 both append log records and one subsequent fsync makes both durable, both can be acknowledged after that common boundary. This can reduce synchronization overhead, but it may add queueing latency while the group forms. The tail-latency effect depends on device latency, scheduler behavior, group policy, and concurrent load.

Relaxed settings may acknowledge after an OS write, after a local memory copy, or with WAL disabled. Those policies can be intentional for reconstructible caches or ingestion buffers, but they create a documented data-loss window. “Fast commit” is therefore incomplete unless paired with “what failure may erase it?”

4. Wrong approach: acknowledge before the recoverable state is durable

A practitioner may optimize checkout latency by replying immediately after copying the change into an in-memory page or unsynchronized log buffer. The request succeeds, then power is lost before the log is durable. On restart, recovery has no durable record for the acknowledged write. This is not a stale read; it is an acknowledged-write durability violation relative to the intended contract.

The safe repair is contract-first: decide the required failure domain, move acknowledgement after the corresponding durable boundary, instrument sync latency, and test crash recovery. If a lower-durability mode is deliberately chosen, expose that fact in the service-level objective and make the lost interval reconstructible.

5. AtlasMart lab — durable offset, unsafe acknowledgement, and group commit

The lab uses a temporary local directory. It performs real Python file writes and os.fsync, but the “power loss” is deliberately modeled by truncating bytes after the last remembered fsync offset. That makes the teaching deterministic; it does not claim a particular operating system or SSD would lose exactly those bytes.

python · wal_durability.py
import json, os, tempfile

records = [
    {"tx": 1, "key": "order:501", "value": "PAID"},
    {"tx": 2, "key": "order:502", "value": "PAID"},
    {"tx": 3, "key": "inventory:sku-9", "value": 41},
    {"tx": 4, "key": "inventory:sku-10", "value": 18},
]

def append_record(f, rec):
    line = (json.dumps(rec, sort_keys=True) + "\n").encode()
    f.write(line)
    f.flush()
    return line

def recover(path):
    state = {}
    with open(path, "rb") as r:
        for line in r:
            rec = json.loads(line)
            state[rec["key"]] = rec["value"]
    return state

with tempfile.TemporaryDirectory() as d:
    wal = os.path.join(d, "atlasmart.wal")
    with open(wal, "w+b", buffering=0) as f:
        append_record(f, records[0])
        os.fsync(f.fileno())
        durable_offset = f.tell()
        print("tx1 ACK after fsync; durable_offset=", durable_offset)

        append_record(f, records[1])
        print("tx2 WRONG ACK before fsync; volatile_end=", f.tell())

        # Controlled crash model: bytes after the last fsync boundary are treated
        # as lost. This is pedagogical and does not predict a specific OS/device.
        f.truncate(durable_offset)
        f.flush()
        os.fsync(f.fileno())

    state = recover(wal)
    print("after crash recovery:", state)
    print("tx2 survived?", "order:502" in state)

    with open(wal, "ab", buffering=0) as f:
        append_record(f, records[2])
        append_record(f, records[3])
        os.fsync(f.fileno())
        print("tx3+tx4 group commit: one fsync for two log records")

    state = recover(wal)
    checkpoint = os.path.join(d, "checkpoint.json")
    with open(checkpoint, "w", encoding="utf-8") as c:
        json.dump(state, c, sort_keys=True)
        c.flush()
        os.fsync(c.fileno())
    print("checkpoint state:", state)
    print("recovery source remains WAL until checkpoint policy says otherwise")

Run with python wal_durability.py. No cleanup is required because the temporary directory is removed automatically.

text · expected output
tx1 ACK after fsync; durable_offset= 47
tx2 WRONG ACK before fsync; volatile_end= 94
after crash recovery: {'order:501': 'PAID'}
tx2 survived? False
tx3+tx4 group commit: one fsync for two log records
checkpoint state: {'order:501': 'PAID', 'inventory:sku-9': 41, 'inventory:sku-10': 18}
recovery source remains WAL until checkpoint policy says otherwise

Verification

  • Transaction 1 survives because the model records a durable WAL boundary before acknowledgement.
  • Transaction 2 is acknowledged by the intentionally wrong path and then disappears after the controlled crash.
  • Transactions 3 and 4 share one fsync, demonstrating group commit rather than one-sync-per-transaction.
  • The checkpoint contains recovered state, but the WAL remains the source of recovery truth until the checkpoint policy permits older log removal.

Check your understanding

  1. Why can a dirty page contain a value that is not yet durable?
  2. What does fsync completion prove?
  3. Why can group commit improve throughput?
  4. Why is WAL not the same as backup?
  5. What should a client success response mean?
Review the answers

1. Because an in-memory mutation is visible to the process before the required recovery information is guaranteed on durable storage.

2. Only the documented OS/filesystem/device durability contract for writes ordered before that call; it does not prove replication or backup.

3. Multiple transactions can amortize one synchronization, trading per-transaction sync overhead for batching/queueing behavior.

4. WAL is a recovery mechanism for database state and may replicate operator mistakes or corruption; backups provide independent recovery points and retention.

5. Exactly the durability/replication semantics documented for that operation, not a vague promise that no imaginable failure can lose it.

6. Production judgment

Use strict WAL-before-data ordering when acknowledged state must survive process/host/power failures covered by the storage contract. Relax durability only for explicitly reconstructible state and document the maximum loss window. In replicated systems, combine local WAL semantics with replica acknowledgement semantics; “quorum committed” and “durably persisted on each quorum member” are separate questions.

Production dimension Questions to record before adopting the mechanism
Correctness / invariants Which writes may be lost, reordered, replayed, or observed through stale storage state? Which invariant is protected above the storage engine?
Durability / recovery What acknowledgement point is durable on the required failure domain? How are WAL replay, checkpoints, backups, replicas, and restore tests verified?
Latency / tails What happens to p95/p99 latency during fsync, page split, flush, compaction, cache miss, or device saturation rather than only during warm steady state?
Amplification / capacity Measure logical versus physical reads/writes and temporary disk headroom. Include compaction debt, tombstones, index/filter memory, and replication overhead.
Device / topology Account for SSD endurance, IOPS, bandwidth, fsync semantics, controller caches, filesystem behavior, replication placement, and failure domains.
Security / tenancy Protect WAL, temporary files, caches, backups, and storage metrics; noisy tenants can create compaction or cache-pressure side channels and denial of service.
Operations / rollback Define telemetry, failure injection, migration, version compatibility, format upgrades, rollback limits, and the skill needed to diagnose storage stalls.
Cost Price storage headroom, SSD replacement/endurance, extra replicas, cache memory, background I/O, backup retention, and engineering time—not only raw GB/month.

Bridge to Lesson 2: WAL explains recoverability, but an engine still needs an indexed structure for point/range access. We next inspect B/B+ tree pages, splits, locality, and update behavior.

Authoritative references

Keep knowledge open

Help the academy stay free and grow.

If these tutorials save you time, a small donation supports new lessons, technical review, diagrams, examples, and long-term maintenance.

ETHEthereum / ERC-20 only
0x716c4Ab160C4B66F31a28AE2448BfF68fc3a2ef0

Send only Ethereum or ERC-20 compatible assets to this address.