SherlockLiu Logo SherlockLiu
Back to all posts
Engineering

The SRE Book, Ten Years On — Part 10: Never Trust a Backup You Haven't Restored

SL
Sep 04, 2026 18 min read
The SRE Book, Ten Years On — Part 10: Never Trust a Backup You Haven't Restored

Two ideas that almost nobody enjoys learning, and that every surviving company eventually wishes it had internalised sooner: data processing pipelines, and data integrity. The first explains why batch jobs are secretly distributed systems with their own thundering-herd and split-brain-adjacent failure modes; the second delivers the rule that’s been tattooed on every DBA since 2016: you don’t want backups — you want restores.

Both halves of what follows are really the same warning, wearing different clothes: a system can look fine from where you’re standing and still be quietly wrong.

A pipeline can run green while sibling pipelines it can’t see conspire to overload the resource underneath it. A byte can rot in storage for months before anything reads it back and notices.

The fix, in both cases, is the same shape — instrument for the interaction you can’t observe directly, and treat “we have a backup” as an unverified claim until someone has actually pulled the trigger on a restore.

In this part

  1. Why pipelines are secretly distributed systems — the Moiré effect, the hanging-chunk trap, and the four guarantees it takes to make "exactly once" actually mean exactly once.
  2. Why data integrity isn't availability — the same 99.99% means "excellent" for uptime and "catastrophic" for bytes, and defence in depth is what closes the gap.
  3. What GitLab and Atlassian proved the hard way — two companies with backup layers on paper, neither of which had ever exercised them.

Pipelines: The Moiré Problem

The headline insight here is visual, best shown with a graph rather than a definition. When multiple periodic pipelines read from the same shared resource — a daily export, a weekly rollup, a monthly report — their load curves overlap and interfere, producing a beating pattern known as the Moiré effect: interference between overlapping periodic patterns.

Plot three pipelines’ resource usage separately and each looks tame; stack them and the peaks align in ways no single pipeline’s author could ever have predicted, because none of them can see the other two.

The Moiré Effect: Tame Alone, Unpredictable Together time → nobody predicted this Pipeline A (daily export) Pipeline B (weekly rollup) Pipeline C (monthly report) Combined load

Figure: each pipeline's own load looks manageable — the combined peak, where two curves' shoulders overlap, is what nobody without visibility into all three could ever have predicted.

The aggregate load isn’t the sum of the averages — it’s the sum of the peaks. “Just add more workers” makes it worse: one documented case found that adding workers to one stage of a chained pipeline decreased end-to-end throughput, because downstream stages couldn’t absorb the burst those workers created.

The other structural insight here is the hanging chunk problem, and it’s a trap with real teeth. Big Data pipelines cut work into “embarrassingly parallel” chunks, but the chunks are rarely even — if you shard by customer, the largest customer’s chunk sets the runtime floor for the entire batch, because most pipeline code waits for every chunk before advancing to the next stage (sorting, in particular, forces this).

One slow or stuck chunk stalls the whole run. The instinctive fix — kill the job and restart it — is usually the wrong one: without checkpointing, killing a hung job throws away every other chunk’s completed work too, wasting exactly the compute and human attention the restart was meant to save.

Google’s actual answer, Workflow (built in 2003, still running the vast majority of Google’s continuous data processing today), reframes the whole problem as a distributed-systems design pattern borrowed, oddly, from Model-View-Controller: a Task Master holds all job state in memory for speed while synchronously journaling every mutation to disk (the model); stateless workers pull tasks, do the work, and write results (the view); an optional controller handles scaling, snapshotting, and rollback.

The correctness guarantees stack four deep, and each one closes a specific hole the previous three leave open:

# Guarantee The hole it closes
1 Worker holds a currently-valid lease before it can commit Two workers racing on the same chunk can’t both win
2 Every worker writes to a uniquely named output file An orphaned worker whose lease expired keeps writing harmlessly — it can never commit, because the lease-holder’s filename is the one that counts
3 Every commit references the task ID of the configuration actually used A mid-flight config change invalidates in-progress work from the old config instead of committing inconsistent results
4 Every operation carries a server token naming its Task Master A misconfigured load balancer routing to two Task Masters can’t cause a silent identity collision

None of these four is sufficient alone — it took all four, discovered the hard way, to make “exactly once” actually mean exactly once. For true business continuity across whole datacenters, Workflow’s global variant leans on the same primitives Part 9 covered in the abstract: journals in Spanner for globally consistent low-throughput storage, and Chubby (Google’s consensus-backed lock service) to elect which Task Master gets to write — distributed consensus, reappearing exactly where you’d expect it to.

Data Integrity: Whatever Users Think It Is

Data integrity starts with a definition that refuses to stay put: “data integrity is whatever users think it is.” A user who sees an empty mailbox for four days doesn’t distinguish “your data is gone” from “your data is fine but inaccessible” — both destroy trust identically.

That reframing produces the sharpest quantitative argument here, and it’s worth sitting with because the numbers are genuinely counterintuitive: a service with 99.99% uptime loses only about an hour a year, a bar most users would call excellent. A service with 99.99% good bytes in a 2GB artifact has up to 200KB of silent corruption — random opcodes in executables, unloadable databases.

The same-looking number means “basically fine” for availability and “catastrophic” for integrity, because corruption doesn’t distribute evenly across a file the way downtime distributes across a year. The conclusion is the one line worth memorizing: the secret to superior data integrity is proactive detection and rapid repair, not prevention — an artifact corrupted once a year but caught and fixed within 30 minutes is, from the user’s perspective, still effectively 100% intact.

24 Failure Modes, Three Crossed Dimensions Root cause (× 6) user error · operator error application bug infrastructure defect hardware fault site catastrophe × Scope (× 2) widespread narrow × Rate (× 2) big-bang creeping 6 × 2 × 2 = 24 — the hardest is "creeping, narrow, application bug"

Figure: the taxonomy's three independent axes — a study of 19 real Google data-recovery efforts found the worst combination wasn't the dramatic one.

The rule

  • You don't want backups — you want restores. A backup nobody has tested is a hypothesis, not a guarantee.
  • Track the restore path itself as a metric — "how many backups deep is our restore path" — not just "do we have backups."
  • Replication is not recovery. A replicated datastore faithfully copies a bad delete to every replica before anyone notices there was a problem.

A study of 19 real Google data-recovery efforts found the hardest variant wasn’t the dramatic one. It was low-grade corruption from a software bug, discovered weeks or months after the bug shipped — by which point the corrupted data has already propagated into derived tables, caches, and every backup taken since.

This is why replication is not recovery: a replicated datastore faithfully copies a bad delete to every replica, usually before anyone notices there was a problem to protect against.

The defence-in-depth answer layers three mechanisms, each worth understanding for why it exists rather than just what it’s called:

Mechanism Primary defence against The detail that matters
Soft (and lazy) deletion User error at the application layer; developer error at the storage layer Mark for deletion, destroy only after a delay — Google settles on 30–60 days, because most account-hijacking and integrity issues surface within 60. The two layers are deliberately separate: a bug that bypasses the application’s soft delete shouldn’t also bypass the storage system’s own copy of the idea
Tiered backups, not one backup Discovering too late that the only surviving copy is the wrong age Fast, expensive, short-retention closest to live data; slower, cheaper, longer-retention further out; offline/archival last. Reverse-engineer every choice from “how fast do we need this back, and how much can we afford to lose” — never from “what’s cheap to store”
Out-of-band validation (“trust but verify”) Implementation bugs the theory doesn’t model Check correctness outside the write path, because even formally-proven consensus, as Part 9 covered, ships with timeout tuning, retries, and edge cases

One line puts the last one best: “in theory, Paxos ignores failed nodes and makes progress with a quorum. In practice, ignoring a failed node corresponds to timeouts and retries beneath the implementation” — trust the algorithm’s proof, but verify the actual bytes it produced.

At Google’s own scale the arithmetic gets absurd enough to need its own fix: validating 700 petabytes at a naive 300MB/s takes roughly eight decades. The answer is trust points — treat data as immutable once it’s settled, verify and back up only what’s changed since the last trust point, and shard the verification job across enough parallel workers that wall-clock time drops from decades to hours.

Restoring is the same problem in reverse: a full restore that has to replay a thousand sequential incremental backups since the last full backup is its own reliability risk, which is exactly why “how many backups deep is our restore path” is a metric worth watching on its own, not just “do we have backups.”

Defence in Depth for Data Soft deletion — mark first, delete later. Human error becomes recoverable. Local snapshots — fastest restore, shortest history Backups (recent state) — the product is the restore, not the backup Archives / offline (tape, immutable object store) — compliance and apocalypse tier Replication is not recovery. A deleted row replicates to every replica.

Figure: four layers, different freshness-vs-survivability trades — and the caption that replication is not a backup.

Ten Years On: The Two Postmortems Everyone Should Read

These rules were proven the hard way by companies whose names everyone knows.

GitLab, January 31, 2017. An engineer, intending to work on the secondary database, wiped ~300GB from the primarydb1 — while tired at 23:00. Then the layers fell, one by one, exactly the way a defence-in-depth model warns they will: pg_dump backups had been silently failing (a Postgres version mismatch plus email alerts swallowed by DMARC), the S3 backup bucket was empty, Azure snapshots weren’t enabled for database servers, and replication was broken.

The rescue came from an LVM snapshot taken ~6 hours earlier — a layer nobody was managing as a layer. GitLab lost ~6 hours of data (about 5,000 projects, 5,000 comments, 700 users), live-streamed the entire recovery, and published one of the best postmortems in industry history.

The lesson, verbatim from their own incident doc: “out of 5 backup/replication techniques deployed, none are working reliably or set up in the first place.” Five layers, zero restores — because nobody had done the one thing that matters: practiced the restore, not just configured the backup.

Atlassian, April 2022. A maintenance script deleted data for 775 customers — wrong IDs (site IDs instead of app IDs) and wrong mode (permanent delete instead of mark-for-delete). The recovery took two weeks, rebuilding tenants from backups in batches of 60, because no per-tenant point-in-time recovery existed. The “soft deletion first” rule, ignored at scale, cost 775 organisations their Jira for a fortnight.

Two Real Incidents, Same Taxonomy GitLab, 2017 Root cause: operator error Scope: widespread · Rate: big-bang Layer that failed: every tiered backup — none had been tested Saved by an untracked LVM snapshot Atlassian, 2022 Root cause: application bug Scope: widespread · Rate: big-bang Layer that failed: soft deletion — bypassed by a script with delete rights Two weeks to rebuild, 60 tenants at a time

Figure: different root causes, same taxonomy cell (widespread, big-bang) — and in both cases, the layer that should have caught it had never actually been exercised.

The lesson

A backup you haven't restored isn't a backup — it's an assumption. Both companies had layers on paper; neither had exercised them, and the gap only showed up at the worst possible moment.

And the 2026 addition nobody could have predicted in 2016: ransomware. The new data-loss vector doesn’t come from bugs but from attackers who specifically target backups first, which reorders the recovery priorities entirely.

That makes the modern equivalent of tape — immutable, versioned object storage (S3 Object Lock, GA since November 2018) — a defence-in-depth layer against a threat model that simply didn’t exist a decade ago: an adversary with valid credentials trying to delete the backup on purpose.

Every tier of the recovery pyramid now needs a “can an attacker with my credentials delete this?” audit alongside its original “how fast can we restore” question.

AI-assisted incident response reaches this territory from an unexpected angle: the hardest failure mode above — low-grade corruption discovered months after the triggering bug shipped — is precisely the pattern that trace-and-log fusion approaches like RC-LLM are best suited to, if the out-of-band validators described above are emitting the telemetry to fuse in the first place.

An AI investigator correlating “this table’s row count started drifting from its historical baseline eleven weeks ago” against a deploy log is doing, faster and continuously, exactly what “trust points” and out-of-band checks were manually designed to catch.

The prerequisite is the same one Practical OpenTelemetry’s discussion of the five clocks inside your MTTR makes explicit: you can’t detect a drift you never instrumented — and the earlier “the point where the bug shipped” and “the point where a human clocked it” converge, the smaller the eventual restore has to reach back.

Key takeaways

  • A backup you haven't restored is an assumption, not a backup. GitLab had five backup and replication techniques configured and none that worked; the layer that actually saved them was an untracked LVM snapshot nobody was managing.
  • The same 99.99% means opposite things for uptime and for bytes. An hour of downtime a year is excellent; 200KB of silent corruption in a 2GB artifact is catastrophic — because corruption doesn't spread evenly the way downtime does.
  • Proactive detection beats prevention. An artifact corrupted once a year but caught and repaired within 30 minutes is, from the user's side, effectively 100% intact.
  • Replication is not recovery. A replicated datastore faithfully copies a bad delete to every replica, usually before anyone notices there was something to protect against.
  • The dangerous failure is the boring one. Across 19 real Google recovery efforts the hardest case wasn't the dramatic outage — it was low-grade corruption from a software bug, found weeks later, already propagated into every derived table and every backup since.

Next: Part 11: Ship It Without Sinking It — Releases, Tests, and Launches, where the change finally reaches production safely.


References

Comments