Skip to content
About
Main content

Checkpointing: sharp, fuzzy, incremental

What does each kind of checkpoint pay, and why do stalls turn into background rates with a cliff?

A writes to disk and records a position in the log, so that can start from there instead of from the beginning of time.

Sharp (consistent) checkpoint. Stop the world: pause writers, flush every dirty page, write a checkpoint record. The image on disk is transaction-consistent, recovery is trivial, and the cost is a stall of dirty_pages × page_size ÷ write_bandwidth.

This design is not rejected because its total work is too high. It is rejected because of its shape. A recurring 20-second stall is worse than a permanent 5% tax, even if the tax costs more work in total. Every queue upstream must be sized for the stall, and every timeout downstream must be longer than it. This is the rule that , and here it is the only reason the textbook design is used almost nowhere.

Fuzzy checkpoint. Do not stop. Write a begin-checkpoint record that captures the dirty page table and the active transaction table. Let the background flusher continue at its own pace. Then write an end-checkpoint record. The image on disk is consistent at no single instant: it is a smear across the checkpoint window. Recovery copes by starting replay at the smallest recoveryLSN (the of the oldest change not yet on disk) in the dirty page table, and re-applying everything. Pages that were already flushed past that point silently refuse the old records, because pageLSN ≥ recordLSN.

Notice the dependency, because it is the best example of mechanisms building on each other: the LSN rule bought in Logs and undo to make safe to repeat is what makes possible. Without repeatable redo, you need a consistent image. With it, you can accept an inconsistent one and let recovery sort it out. One mechanism, bought for one reason, pays for a second, unrelated one. A design that many other things depend on in this way is a sign of a sound foundation.

Incremental checkpoint. Remove the event completely. Keep a flush list ordered by each page's oldest modification LSN, and flush continuously from the head of that list. The checkpoint position then moves forward smoothly instead of jumping. InnoDB's is this: the flush rate is driven by (how far the log head has run ahead of the oldest unflushed change) as a fraction of the redo log capacity.

This is a general pattern: any periodic maintenance event whose cost is a visible stall eventually becomes a continuous background rate.

Stall versionContinuous version
Stop-the-world Concurrent garbage collection
Major compactionLevelled compaction
Adaptive flushing
Full Continuous replication

But be honest about what the conversion does not do. Costs are conserved. Smearing does not remove the stall. It turns a predictable periodic stall into a load-dependent threshold. When the write rate stays above the flush rate for long enough, checkpoint age approaches the redo capacity, and the engine has no choice but to slow down writes, first softly, then hard. , and its position depends on your workload. The system is smoother 99% of the time and less predictable in the 1% that matters.

The coupling nobody expects

In Postgres, the setting full_page_writes makes the first change to a page after each checkpoint write the whole page image into the . This protects against (When the storage lies). Therefore:

  • More frequent checkpoints → more "first touches" per unit of time → more full page images → more WAL volume.

For scattered single-row updates, an 8 KB full-page image replaces a record of about 100 bytes: 8,192 ÷ 100 ≈ 80× on first touch. So the checkpoint interval, which you adjusted to control recovery time, is also the main control on write amplification, and it moves the two in opposite directions. The best value is somewhere in the middle and depends on the workload: it depends on how many distinct pages your writes touch per interval, which is a property of access locality, not of write rate. This is the cleanest demonstration that the knobs in this area are not independent.

The same mechanism in a distributed system

Flink (a stream-processing engine) checkpoints with asynchronous barrier snapshotting (the Chandy–Lamport idea): special markers are injected at the sources and flow through the pipeline together with the records. An operator its state when the barriers have arrived on all its inputs.

  • Aligned checkpointing waits for the slowest input. The image is consistent, but the wait pushes backpressure (a slow part forcing upstream parts to slow down) into the pipeline.
  • Unaligned checkpointing (Flink 1.11 and later) saves the in-flight records instead of waiting. No stall, but the checkpoint contains the channel contents and is larger.

That is sharp versus fuzzy, rediscovered on a distributed system: pay a stall for a clean image, or pay storage and complexity for a smeared one. If you filed "database checkpointing" and "stream-processing checkpointing" as two unrelated topics, merge them. The only difference is that a distributed system cannot take a global instant, so it uses a barrier travelling through cause and effect as a substitute for one.