An m-tolerant code dies if m+1 fragments share a rack

An erasure code only delivers its m-fault tolerance if placement keeps faults independent — spread the k+m fragments so no rack, power domain, or hardware batch holds more than m of them, or one correlated failure exceeds the code.

Previously

The k+m code tolerates m losses only when those losses are independent — so we have to confront where the fragments actually sit, because real disks fail together.

Scene 05a

An m-tolerant code dies if m+1 fragments share a rack

  1. Watch
  2. Try it
  3. Predict
  4. Capture
placement across failure domains · 17+3SPREAD — at most m=3 fragments per rackplacement: SPREADrack 13 fragsd0d7d14rack 23 fragsd1d8d15rack 33 fragsd2d9d16rack 43 fragsd3d10p0rack 53 fragsd4d11p1rack 63 fragsd5d12p2rack 72 fragsd6d13DURABILITY11 nines (on paper)FAULT TOLERANCEtolerates 3 lossesPick a rack to kill — every disk in it fails at once.
What to watch for

Here is the exact 17+3 stripe from before, but now we ask the question the durability math quietly skipped: where do the 20 fragments actually sit? Under SPREAD placement no single rack holds more than m=3 of them. Watch one whole rack lose power — every disk in it dies at once — and the object still rebuilds.

Continue unlocks when the animation finishes.
Implementation

Highlighted lines are the ones running in the diagram right now.

Placer.assign(fragments, domains, m)
the placement rule — no domain may hold more than m fragments
def assign(fragments, domains, m):
per_domain = {d: 0 for d in domains}
for frag in fragments: # k + m of them
d = pick_domain(domains, per_domain)
if per_domain[d] >= m:
continue # full — keep faults independent
per_domain[d] += 1
frag.domain = d
return fragments
Cluster.failDomain(dead)
one power event = every fragment in that domain, at once
def failDomain(dead):
lost = [f for f in fragments if f.domain == dead]
for f in lost:
f.alive = False # one event, correlated loss
return len(lost) # all gone simultaneously
Reader.reconstruct(k)
any k survivors rebuild; fewer than k is unrecoverable
def reconstruct(k):
survivors = [f for f in fragments if f.alive]
if len(survivors) >= k:
return decode(survivors[:k]) # inverse-solve
raise Unrecoverable(survivors=len(survivors),
needed=k)

Where this sits in Build an S3-style distributed object store

Scene 05a of 12, in the Keep it alive act — Erasure coding, placement, the two planes, and repair.. The code's m-fault tolerance is real only if placement spreads fragments across independent failure domains.

Up next. We now have fragments scattered across domains for safety — but to read an object back we need something that remembers which storage node holds which fragment, which pulls the two planes together.

All 12 scenes in Build an S3-style distributed object store · Every curriculum

Built with Arqly
Every scene in Build an S3-style distributed object store builds on the one before it.All 12 Build an S3-style distributed object store scenes