Skip to content
About
Main content

End-to-end checksums, scrubbing and bit-rot detection

What must a checksum cover to catch real corruption, and how often must you scrub before the last good copy expires?

One word, three failure models

The word "" is used for three different jobs. They answer three different questions against three different adversaries:

QuestionAdversaryMechanism
Are these the bytes I wrote?an indifferent storage systemblock checksums, , parent-pointer hashes (this mechanism)
Are these the right values?a buggy pipeline or upstream systemreconciliation, hash aggregates, cell-level diffs
Were these bytes written by someone authorized?an intelligent attackerMACs (message authentication codes), signatures, key management

The checksum here must survive indifference, not malice. That is why CRC32C (a fast, simple checksum) is adequate here and completely inadequate against an attacker. Different threat model, different tool, same word.

The failure model, corrected

Folklore says "bit rot": charge slowly leaking from the medium. Field evidence says otherwise. Large studies (CERN's 2007 silent-corruption audit; Bairavasundaram et al.'s multi-year study of about 1.5 million drives) found that the corruption you actually meet is dominated by the path, not the medium:

  • firmware bugs;
  • misdirected writes: the drive wrote your block perfectly, to the wrong address;
  • lost writes: acknowledged, never performed;
  • ;
  • bad cables and host bus adapters (HBAs);
  • memory without error correction (non-ECC DRAM);
  • ordinary software bugs.

Rates are of the order of 10⁻⁴ to 10⁻² of drives per year showing at least one silent error. And, critically, That is exactly where redundancy plans that assume independence fail (Durability across machines).

This correction drives the whole design. Media decay would be caught by the drive's own error-correcting code (ECC). Misdirected and are not caught: the block that comes back is perfectly intact. It is just the wrong block, or the correct-looking stale one. No amount of integrity checking inside one device catches that.

The end-to-end argument, plus an anchor

The checksum must be computed by the party that produced the data and verified by the party that consumes it. And, the part that most designs get wrong, it must cover the block's identity, not only its contents. A checksum stored inside the block validates a perfectly, because the block and its checksum travelled together and agree with each other.

Three real designs:

  • ZFS stores each block's checksum in the parent block pointer, not in the block itself. A misdirected write cannot bring a matching checksum with it. The tree validates itself from the root.
  • InnoDB and Postgres embed the page number in the page and checksum it. That detects misdirection within the file.
  • T10 DIF/PI (a standard for data-integrity fields) attaches 8 bytes to each sector: a guard (checksum), a reference tag holding the expected block address (LBA), and an application tag. It is verified at every hop, from the host adapter to the media.

The general rule:

Six forms of the same rule: parent-pointer checksums, page number inside a page, T10 reference tags, Merkle trees (hash trees where each parent hashes its children), content-addressed stores (data named by its own hash), and table-format manifests (the data file is valid, but only the manifest says it belongs to the table). A seventh appears in distributed systems: fencing tokens, because a leader cannot prove from inside itself that it is still the leader.

Scrubbing, derived from a timing inequality

Latent corruption is found only by reading. Nobody reads cold data. So corruption collects exactly in the data least likely to be read, which is also the data whose redundant copies are aging in parallel, under the same conditions. Hence scrubbing: proactively reading all data and checking its checksums on a schedule.

The design constraint is an inequality that almost nobody writes down:

scrub_period + detection_to_alert_lag  <  retention_period of the last good copy

Cost scales with stored bytes, permanently:

  • 100 TB on a 30-day cycle: 100 × 10¹² B ÷ (30 × 86,400 s) ≈ 40 MB/s of background reads. Practically free.
  • 10 PB on the same cycle: ≈ 4 GB/s. Real capacity.
time to read every stored block once
how long backups keep a good copy
Data table
Background reads needed, by data stored (30-day cycle)
Data storedScrub read rate (bytes/s)
931 GB377 KB
1.05 TB435 KB
1.21 TB502 KB
1.4 TB580 KB
1.62 TB670 KB
1.87 TB774 KB
2.16 TB893 KB
2.49 TB1.01 MB
2.88 TB1.16 MB
3.32 TB1.34 MB
3.84 TB1.55 MB
4.43 TB1.79 MB
5.11 TB2.07 MB
5.91 TB2.39 MB
6.82 TB2.76 MB
7.88 TB3.19 MB
9.09 TB3.68 MB
10.5 TB4.25 MB
12.1 TB4.91 MB
14 TB5.67 MB
16.2 TB6.54 MB
18.7 TB7.56 MB
21.6 TB8.72 MB
24.9 TB10.1 MB
28.8 TB11.6 MB
33.2 TB13.4 MB
38.4 TB15.5 MB
44.3 TB17.9 MB
51.1 TB20.7 MB
59.1 TB23.9 MB
68.2 TB27.6 MB
78.8 TB31.9 MB
90.9 TB36.8 MB
105 TB42.5 MB
121 TB49.1 MB
140 TB56.7 MB
162 TB65.4 MB
187 TB75.6 MB
216 TB87.2 MB
249 TB101 MB
288 TB116 MB
332 TB134 MB
384 TB155 MB
443 TB179 MB
511 TB207 MB
591 TB239 MB
682 TB276 MB
788 TB319 MB
909 TB368 MB
1,050 TB425 MB
1,210 TB491 MB
1,400 TB567 MB
1,620 TB654 MB
1,870 TB756 MB
2,160 TB872 MB
2,490 TB1,010 MB
2,880 TB1.14 GB
3,320 TB1.31 GB
3,840 TB1.52 GB
4,430 TB1.75 GB
5,110 TB2.02 GB
5,910 TB2.33 GB
6,820 TB2.69 GB
7,880 TB3.11 GB
9,090 TB3.59 GB
10,500 TB4.15 GB
12,100 TB4.79 GB
14,000 TB5.53 GB
16,200 TB6.39 GB
18,700 TB7.38 GB
21,600 TB8.52 GB
24,900 TB9.84 GB
28,800 TB11.4 GB
33,200 TB13.1 GB
38,400 TB15.2 GB
44,300 TB17.5 GB
51,100 TB20.2 GB
59,100 TB23.3 GB
68,200 TB26.9 GB
78,800 TB31.1 GB
90,900 TB35.9 GB

The usual answer is to lengthen the period, which lengthens , which tightens the inequality. There is no escape, only a choice of where to pay:

The boundary of what detection buys

A checksum recovers nothing. It turns silent loss into loud loss. That is a real improvement: spreads into backups, replicas and derived tables, while loud corruption stops at the reader. But the repair needs a second copy.

This is why ZFS, Ceph, HDFS and Colossus all pair checksums with automatic repair from a replica instead of offering detection alone.

Failure modes of the verifier

  • A mismatch cannot tell you, from the inside, whether the data is corrupt, the checksum is corrupt, or the checksum code has a bug. So a system that panics on a mismatch can be taken down by a bug in its own integrity code.
  • When two layers both checksum and they disagree, you get the "which layer is lying?" problem. It has the same shape as the unretryable fsync failure from Group commit and fsync: the stack gives you no way to find which witness is at fault.

How the mechanisms of this article sit on three trade-offs

Mechanism vs. Space vs. timeDurability vs. latency
and batched; small latency cost2× data-page bytesnone: protects the meaning of an ack already given
Full-page writesuses bandwidthup to ~80× on first touchtied to the interval
Device atomic writesremoves both costs—trades a mechanism for a promise you cannot verify
Battery-backed cache / 50–100× lower flush cost—moves the durability point; same ack, nearer
Persistent memoryflush becomes store + fence—atomic unit drops to 8 B; rewrites the protocol
Checksums~1–3% CPU on write and reada few bytes per blocknone: verifies a past ack
Scrubbingpermanent background read taxdetection lag versus bandwidthnone