Distributed Storage

Recovered State Is a Checkpoint Plus the Log After It

There are exactly two ways to remember what a system knows: write down the current state, or write down every change. Every recovery scheme worth using does both — a periodic snapshot of state, plus the ordered log of everything since. The same pair, unchanged, is how a replica catches up.

▶ Run the lab

The question this answers

The question

After a crash, how does a node rebuild what it knew — and why does the same structure make replication work?

The guarantee — the property claimed, and its scope

A node that reloads checkpoint C and then applies every log record after C, in log order, reaches a state that is *identical* to the one it would have had at that log position — provided the operations are deterministic and the log is truly ordered and gap-free. Anything acknowledged but not in the log is not recovered, and no amount of replay creates it.

Everything below is bought to hold this sentence. "Strongly consistent" with no scope attached is a slogan, not a guarantee — read what it actually covers, and what it explicitly does not.

What a node knows — observation versus inference

A recovering node knows its newest complete checkpoint and the log position that checkpoint corresponds to. It knows the log records it holds locally after that position. It does *not* know whether the log has records beyond what it holds — a node recovering from local disk cannot tell "the log ends here" from "I have not received the rest yet", which is why a recovering replica must ask the current leader rather than declaring itself caught up.

A node knows its own state and the messages that arrived. Everything else is inference from evidence that was already stale. "B has not replied in five seconds" is knowledge; "B is down" is a decision — and usually the bug.

What guarantee?What does a node know?How does it work?What can fail?How does it fail?Where is coordination?What holds under failure?How does it recover?How would you know?What is the simpler thing?
checkpointlogrecoveryreplaystate machine

The two ways to remember, and why you need both

Consider a counter at 4,000,001. You can persist the number, or you can persist four million increments. State is compact and fast to load, but it must be written atomically and it destroys history. A log is cheap to append — it is a sequential write, the one thing every storage device is good at — but replaying four million records to learn one number is absurd.

The resolution is the oldest structure in systems: checkpoint the state occasionally, log every change continuously, and recover by loading the newest checkpoint and replaying the log records after it. The checkpoint bounds replay time. The log bounds data loss. Neither alone does both.

The vocabulary changes by layer and the structure does not. A database calls it a write-ahead log plus a checkpoint. A consensus implementation calls it a snapshot plus the The Raft Log: Commit Index, Divergence and Reconciliation suffix. A stream processor calls it state-backend checkpointing plus offsets. An event-sourced application calls it a snapshot plus the event stream. If you have understood it once, you have understood all of them, and the Database domain owns the mechanics of doing it well on one machine.

Recovery = newest checkpoint + the log suffix after it
loadread forwardresultCheckpoint @ log 900 state as of record 900Log records 901…1247 ordered, gap-freeReplay in order deterministic applyState @ 1247 identical to pre-crash
ClientGateway / LBServiceWorkerDatabaseCacheQueue / LogObject storageCDNExternal system

Write-ahead is the ordering rule that makes it sound

The whole scheme rests on one ordering constraint: the log record must be durable before the change it describes is applied to the main structure. That is the "write-ahead" in write-ahead logging, and reversing it breaks recovery — a crash between applying and logging produces a state whose history nobody has.

From that constraint two useful properties follow. Recovery is *idempotent*: replaying a record whose effect is already present must be harmless, which is why records are keyed by a monotonically increasing position and application is conditional on that position. And recovery is *bounded*: you only ever replay from the checkpoint forward, so replay time is a function of checkpoint frequency, not of the system’s age.

The Database domain owns how this is implemented — record formats, group commit, fuzzy checkpoints, the interaction with the buffer pool. What belongs here is the observation that the constraint is about order and durability of the record relative to the change, and that this is a property you can state and check for any system that claims to be recoverable, at any layer.

[recover] latest checkpoint = 000900 (14.2 GB, written 09:41:07)
[recover] local log holds 000901..001247
[recover] replaying 347 records ...
[recover] record 001102 already applied (position <= state position) — skipped, no-op
[recover] replay complete, state position = 001247, 1.9s
[recover] asking leader for records beyond 001247 ...
[recover] leader log ends at 001260 — fetching 13 records   <-- the local log was NOT the end
A recovery log excerpt: what the node prints, and what each line means

The same structure is the replication protocol

Here is the observation this lesson exists for. Recovery and replication are the same operation with a different destination. Recovery replays the log into the same node after a crash. Replication ships the log to a different node and replays it there. In both cases the receiving state machine starts from a known position and applies an ordered sequence of deterministic changes.

That is not an analogy — it is literally how the systems are built. Postgres streams the WAL to standbys. Raft *is* a replicated log whose commit rule decides which prefix everyone must apply. Kafka replicas fetch the leader’s log and apply it. When a new replica joins with no state at all, it does not get a special protocol: it gets a checkpoint transfer followed by the log from that checkpoint’s position, which is the recovery procedure with a network hop inserted.

This explains why log-based systems dominate this space. Choosing a log as your durability mechanism gets you replication, point-in-time recovery, and change data capture from the same artefact, because all three are "read the ordered record of changes from some position". Choosing to persist state only gets you none of them. It is also why The Log Is Not a Queue and A Topic Is Not One Log: Ordering Lives Inside a Partition keep reappearing in this domain: the log is the primitive that makes several otherwise-separate problems into one.

The catch is determinism. Replay reproduces the original state only if applying a record is a pure function of the record and the prior state. A record saying "set expires_at to now() + 1 hour" replays to a different value; the log must therefore record the *computed result*, not the instruction that computes it. Any non-determinism — wall-clock reads, random values, external calls — has to be resolved at record time and written down, or the recovered state diverges from the state you lost.

  • Replication = recovery, with the replay happening on another machine.
  • Bootstrapping a new replica is checkpoint transfer plus log from that position — not a separate mechanism.
  • Log records must carry results, not instructions, wherever the instruction is non-deterministic.
  • One log artefact yields durability, replication, point-in-time recovery and change capture at once.
  • The commit rule — which log prefix everyone must apply — is where consensus enters; see The Raft Log: Commit Index, Divergence and Reconciliation.

Choosing the checkpoint interval is choosing your recovery time

Checkpoint frequency is a straight trade with exactly two sides. Frequent checkpoints mean short replay and therefore short recovery, at the cost of steady I/O and, for large state, a visible latency bump while the checkpoint is taken. Infrequent checkpoints are cheap in steady state and expensive exactly once — during an incident, when you are watching a node replay for forty minutes and can do nothing about it.

The asymmetry is what people get wrong. Checkpoint cost is paid continuously and is easy to see on a graph; replay cost is paid rarely and only when you can least afford it. Teams therefore drift toward long intervals, because the graph rewards it and nothing punishes it until an incident. The number to hold is not the checkpoint interval but the resulting worst-case replay time, and that number should be measured rather than estimated — by actually restarting a node with a full log suffix and timing it.

The second trap is log retention. Truncating the log before the checkpoint that covers it makes the state unrecoverable — an operational mistake with no gradual warning, only a cliff. Correspondingly, retaining log beyond the oldest checkpoint any *replica* might still need is what lets a lagging replica catch up without a full state transfer; truncate too eagerly and a replica that was thirty seconds behind now needs a fourteen-gigabyte snapshot copy.

IntervalSteady-state costRecovery timeLog retention needed
Every 30 secondstypicalHigh and constant I/O; latency bumps on large stateSecondsSmall
Every 10 minutestypicalModerate, usually invisibleTens of seconds to minutesModerate
Every hourtypicalLowMinutes to tens of minutes, exactly during an incidentLarge
Never (log only)protocolNoneGrows without bound with system ageEverything, forever
Checkpoint interval, both sides of the trade

Key points

  • Recovered state = newest checkpoint + every log record after it, applied in order.
  • Write-ahead is the ordering rule that makes it sound: the record is durable before the change is applied.
  • Replay must be idempotent and deterministic, which means records carry computed results, not instructions.
  • Replication is the same replay with the destination on another machine — that is why log-based designs get replication for free.
  • Checkpoint interval directly sets worst-case recovery time; measure that number rather than estimating it.
  • Truncating the log ahead of the checkpoint that covers it, or ahead of a lagging replica’s position, are two different cliffs with no warning.

The chain, answered

Every field here is required, which is why no lesson in this domain can recommend a design without naming what an operator sees when it fails, what survives the partition, what repairs it afterwards, and the simpler thing to consider first.

How it works
  • Every change is appended to an ordered log with a monotonically increasing position, and made durable there first.
  • The change is then applied to the in-memory or on-disk main structure.
  • Periodically, a checkpoint records the complete state together with the log position it corresponds to.
  • Log records older than the newest checkpoint — and older than any consumer or replica still needs — may be truncated.
  • On restart, the node loads the newest complete checkpoint and replays every log record after that position, skipping records already reflected in the state.
  • A replica performs the same procedure against a checkpoint transferred over the network and a log stream fetched from the leader.
What can fail at the boundary
  • A crash occurs mid-checkpoint, leaving a partial checkpoint that must be recognisable as incomplete rather than loaded.
  • The log has a gap because a record was lost, making everything after it unreplayable.
  • A record encodes a non-deterministic instruction, so replay produces a different state from the original.
  • The log is truncated past the point a replica or a checkpoint still needs.
  • The recovering node’s local log is shorter than the true log, and the node declares itself caught up.
How it fails — what an operator sees
  • Recovery that never finishes: a node restarts and spends forty minutes replaying, while operators watch a process at 100% CPU with no progress metric. The checkpoint interval was set years ago and the write rate has grown tenfold since.
  • Divergent replay: a recovered node’s values differ subtly from what it had — timestamps shifted, expiries recomputed. Nothing errors; a downstream consistency check catches it later, if one exists.
  • Snapshot storm: a replica that fell slightly behind is forced into a full state transfer because the log it needed was truncated. The transfer saturates the network and pushes other replicas behind, which then need transfers too.
  • Silent partial checkpoint: a checkpoint written during a crash is loaded as if complete; the node comes up with state that never existed and serves it happily.
  • Log growth to disk-full: checkpointing has been failing for days, so nothing can be truncated. The first symptom is the disk filling, not the failed checkpoints.
Where coordination is required
  • None is needed to checkpoint a single node — the state is entirely local, which is what makes single-node recovery cheap.
  • Deciding which log prefix is committed *is* consensus when several nodes must agree; that decision is the subject of The Raft Log: Commit Index, Divergence and Reconciliation, not of checkpointing.
  • Truncation is a coordinated decision: the safe truncation point is the minimum over every consumer and replica that might still need the log, so one lagging replica pins retention for everyone.
  • Capturing a checkpoint of state that spans several nodes is a genuinely different problem, and the subject of A Consistent Cut, Without Stopping the World.
What still holds under failure
  • Any change that reached the log durably is recoverable; anything acknowledged before reaching the log is not, and this is exactly the boundary Acknowledged, Durable, Replicated: Three Different Things describes.
  • A partial checkpoint is discarded and the previous complete one is used, so a crash during checkpointing costs replay time rather than correctness.
  • Replay is idempotent, so a crash *during recovery* is safe: restart and replay again from the same checkpoint.
  • The recovered state is a legal prior state of the system, not an arbitrary one — the log position tells you exactly which prior state it is.
How it recovers
  • Detect: a node that is up but not serving is usually replaying; expose replay progress as a position and an estimated remaining count, not as a log line.
  • Contain: keep the node out of the serving set until its state position has caught up, or it will serve stale reads that look like a consistency bug.
  • Recover: load newest complete checkpoint, replay forward, then ask the leader for anything beyond the local log end.
  • Reconcile: after catching up, verify the state position against the leader’s commit position rather than against the local log end.
  • Verify: rehearse it. A restart drill that measures actual replay time is the only way the checkpoint interval stays connected to reality.
How you would know
  • Replay progress during startup — records remaining and estimated time — which turns an opaque forty-minute wait into a known one.
  • Time since last successful checkpoint, alerted well before the log fills the disk. Failed checkpointing is silent until it is catastrophic.
  • Log size on disk and the current safe truncation point, together, so a pinning consumer is visible before the disk is.
  • Distance between each replica’s applied position and the leader’s commit position, in records rather than in bytes.
  • Measured cold-start time from a real drill, tracked over time — it grows silently as write volume grows.
When it helps
  • Any stateful service that must survive restarts without losing what it knew — which is nearly all of them.
  • Systems that need replication, point-in-time recovery or change capture, because one log artefact provides all three.
  • Large in-memory state, where reloading from a checkpoint is the difference between a ten-second and a ten-hour restart.
When it hurts
  • Genuinely derivable state: if a cache can be rebuilt from a source of truth in seconds, checkpointing it is machinery protecting nothing.
  • Very small state, where writing the whole state on every change is simpler and fast enough — the log adds a moving part for no benefit.
  • Workloads whose apply step is inherently non-deterministic and cannot be made otherwise; forcing them into a replay model produces confident, wrong recoveries.
Simpler alternatives
  • Rebuild from the source of truth instead of recovering, when one exists upstream — a stream processor can often reset to an offset and recompute rather than checkpointing state at all.
  • Write full state on every change, when the state is small. Simpler, obviously correct, and the log is unnecessary.
  • Keep no state at all: push it into a database that already solved this — the stateless-service default, which removes the problem rather than managing it.
  • Snapshot-only with a longer accepted data-loss window, when the changes since the last snapshot are cheaply reproducible from upstream.

Checkpoint often and pay always; checkpoint rarely and pay during the incident

Recovered state is a checkpoint plus the log after it
Checkpoint often and you spend the machine writing state. Checkpoint rarely and the log you must replay grows. The number to hold is not the interval — it is the resulting worst-case replay time.
steady-state overhead
5.9%
worst-case replay
1.3 min
expected recovery
1.0 min
log retention needed
20,820 records
interval (s): 15steady-state overhead + amortised recovery3,600
5.9% steady-state overhead buys an expected 1.0 min recovery: 23.7 s to load the checkpoint plus 37.5 s of replay, because a crash lands on average half an interval after the last checkpoint. The asymmetry is what drives the drift: checkpoint cost is paid continuously and is easy to see on a graph; replay cost is paid rarely and only when you can least afford it. Teams therefore drift toward long intervals, because the graph rewards it and nothing punishes it until an incident — a node restarts and spends forty minutes replaying while operators watch a process at 100% CPU with no progress metric, because the interval was set years ago and the write rate has grown tenfold since.
[recover] latest checkpoint = 000900 (14.2 GB, written 09:41:07)
[recover] local log holds 000901..001247
[recover] replaying 347 records ...
[recover] record 001102 already applied (position <= state position) — skipped, no-op
[recover] replay complete, state position = 001247, 0.2s
[recover] asking leader for records beyond 001247 ...
[recover] leader log ends at 001260 — fetching 13 records   <-- the local log was NOT the end
Two cliffs, neither with a gradual warning. Truncate the log ahead of the checkpoint that covers it and the state is unrecoverable. Truncate ahead of a lagging replica’s position and a replica that was thirty seconds behind now needs a 14.2 GB snapshot copy instead — which is how one aggressive truncation becomes a snapshot storm. And note the last line above: a recovering node cannot tell “the log ends here” from “I have not received the rest yet”. Replay must also be deterministic: a record saying set expires_at to now() + 1 hour replays to a different value, so the log must carry the computed result, not the instruction that computes it. Bootstrapping a new replica is this same procedure with the destination on another machine — which is why log-based designs get replication for free.
Steady-state costRecovery timeLog retention needed
Every 30 secondstypicalHigh and constant I/O; latency bumps on large stateSecondsSmall
Every 10 minutestypicalModerate, usually invisibleTens of seconds to minutesModerate
Every hourtypicalLowMinutes to tens of minutes, exactly during an incidentLarge
Never (log only)protocolNoneGrows without bound with system ageEverything, forever
Checkpoint interval, both sides of the trade. The steady-state column is on a graph; the recovery column is not.
typicalIntervals from thirty seconds to an hour are the common range, but the right number is entirely a function of write rate and state size and should come from a measured restart rather than from a default. Checkpoint and restore are priced here at flat bandwidths; a real engine’s snapshot cost is not linear in state size.

What people believe, and what is true

Claim

A checkpoint is a backup.

Reality

A checkpoint is for restarting this node. A backup is for the case where this node and its disks no longer exist, and lives somewhere else with a separate retention and restore path.

Claim

Longer checkpoint intervals are just a small performance saving.

Reality

They are a direct multiplier on recovery time, paid during an incident. The saving is visible daily; the cost is invisible until it is the outage.

Claim

Replay always gives back the same state.

Reality

Only for deterministic operations. A record that says "expire in one hour" replays to a different value than it originally produced.

Claim

Replication and recovery are separate features.

Reality

They are the same replay against different destinations. That is why log-based systems get replication almost as a side effect and state-only systems do not.

Go deeper

Only the levels this lesson can honestly fill — a missing level is a claim nobody had.

Overview

Save the state occasionally; log every change continuously. To recover, load the newest save and replay the changes after it. The save bounds how long recovery takes; the log bounds how much you lose.

Practical

Measure real cold-start time in a drill rather than trusting the checkpoint interval. Alert on time-since-last-successful-checkpoint, not on disk usage. Keep log retention above the slowest replica’s needs so a small lag never escalates into a full state transfer. Make sure every log record stores computed values, not instructions that recompute.

Advanced

The reason this structure is everywhere is that a log is a *total order over changes*, and a total order is exactly what both recovery and replication need. Once you have committed to one, you can hand the same artefact to a replica, a point-in-time restore, a change-data-capture consumer and an audit trail without designing anything further. The remaining hard question — which prefix of the log is committed, when several nodes hold different suffixes — is not a storage problem at all. It is Total Order Broadcast Is Consensus Wearing a Different Hat, and consensus is what answers it.

Apply it

Interview questions
  • 💬 Why not just checkpoint on every change and skip the log?
  • 💬 A node restarts and takes forty minutes to come back. What do you change, and what do you measure first?
  • 💬 Explain why a log-based system gets replication almost for free.
  • 💬 What breaks if a log record says "set expiry to now plus one hour"?