Skip to the solution
MediumSystem Design15 min read

How do you design for high availability?

1.2k views

The problem

Question presented to candidate: "Our checkout service needs to be highly available. Walk me through how you would design it, how you would measure availability, and where the downtime actually comes from."

What a strong answer should cover:

  • A measurable target: availability as a fraction of successful requests or time, the nines and the downtime error budget each one allows (99.9% is about 8.8 hours a year, 99.99% about 53 minutes)
  • The arithmetic: components in series multiply (the chain is worse than its weakest part); redundant copies in parallel raise availability only if their failures are independent
  • Removing single points of failure across every tier, spreading copies across failure domains (availability zones, regions), and sizing N+1 capacity so the survivors are not overloaded
  • Failover is part of the design: detection time (health check interval times threshold), DNS TTL and promotion time are downtime on every failure
  • Keeping failures small: timeouts, retries with backoff, circuit breakers, load shedding, graceful degradation, and safe deployments, which cause a large share of real outages

Clarifying questions expected:

  • What availability target, measured how (successful requests, or minutes the service was up), and what does one minute of downtime cost?
  • Which parts must stay up (take payment) and which can degrade (recommendations)?
  • One region or several, and what data consistency does checkout need during a failover?

Code / implementation expected: Optional โ€” availability arithmetic for a real topology; the example computes serial and parallel availability, failover cost, correlated zone failure and N+1 sizing.

  • availability

Try it yourself

Sketch your approach before you read the solution. That is what the interview tests.

Stuck? Get a hint.

A short nudge toward the approach. It will not give the answer away.

Solution

The solution is hidden so you can try first

15 min read ยท 14 sections

Read the code

Availability arithmetic: serial chains, redundancy, failover time, correlated zone failure and N+1 capacity
// Availability arithmetic for a web service: nines, serial chains, redundancy,
// failover time, correlated (same-zone) failures and N+1 capacity.
// All component numbers are assumptions for the example, not vendor SLAs.
const YEAR_MIN = 365 * 24 * 60, MONTH_MIN = 30 * 24 * 60;
const pct = (a) => (a * 100).toFixed(6) + "%";
const down = (a) => { const m = YEAR_MIN * (1 - a); return m >= 1 ? m.toFixed(1) + " min/yr" : (m * 60).toFixed(2) + " s/yr"; };
const serial = (xs) => xs.reduce((p, x) => p * x, 1);
const parallel = (a, n) => 1 - Math.pow(1 - a, n);

console.log("== 1. what each target allows ==");
for (const [label, a] of [["99%", 0.99], ["99.9%", 0.999], ["99.95%", 0.9995], ["99.99%", 0.9999], ["99.999%", 0.99999]])
  console.log(label.padEnd(8) + " " + (YEAR_MIN * (1 - a)).toFixed(1).padStart(7) + " min/yr  " + (MONTH_MIN * (1 - a)).toFixed(1).padStart(6) + " min per 30 days");

console.log("");
console.log("== 2. one copy of everything (serial chain) ==");
const parts = { dns: 0.9999, lb: 0.9999, app: 0.999, db: 0.9995, cache: 0.999 };
const chain = serial(Object.values(parts));
console.log("dns x lb x app x db x cache = " + pct(chain) + " -> " + down(chain));
console.log("weakest part alone: app " + pct(parts.app) + "; the chain is worse than every part");

console.log("");
console.log("== 3. redundancy: independent copies in parallel ==");
for (const n of [1, 2, 3]) console.log("app x" + n + " = " + pct(parallel(parts.app, n)) + " -> " + down(parallel(parts.app, n)));

console.log("");
console.log("== 4. a standby does not help until failover finishes ==");
const FAILS = 4; // primary database failures per year (assumption)
for (const [name, detect, ttl, promote] of [["manual restore", 0, 0, 3600], ["health check 30s x3", 90, 60, 30], ["health check 10s x3", 30, 60, 30]]) {
  const sec = FAILS * (detect + ttl + promote), a = 1 - sec / (YEAR_MIN * 60);
  console.log(name.padEnd(20) + " " + String(detect + ttl + promote).padStart(4) + " s per failover x " + FAILS + " = " + String(sec).padStart(5) + " s/yr -> db " + pct(a));
}

console.log("");
console.log("== 5. correlated failure: three app copies in one zone vs three zones ==");
const zone = 0.9995;
const oneZone = zone * parallel(parts.app, 3), threeZones = parallel(zone * parts.app, 3);
console.log("3 copies, 1 zone  = " + pct(oneZone) + " -> " + down(oneZone));
console.log("3 copies, 3 zones = " + pct(threeZones) + " -> " + down(threeZones));

console.log("");
console.log("== 6. N+1 capacity: surviving a loss without overload ==");
const PEAK = 1200, PER = 400, ZONES = 3;
const base = Math.ceil(PEAK / PER);
console.log("peak " + PEAK + " rps, " + PER + " rps per instance: " + base + " needed, " + (base + 1) + " to survive one instance");
const perZone = Math.ceil(PEAK / (PER * (ZONES - 1)));
console.log("survive a whole zone: " + perZone + " per zone x " + ZONES + " zones = " + perZone * ZONES + " instances, " + Math.round((100 * PEAK) / (perZone * ZONES * PER)) + "% busy at peak");
console.log("same 6 instances if one zone dies: " + Math.round((100 * PEAK) / (perZone * (ZONES - 1) * PER)) + "% busy; with only " + (base + 1) + " instances spread 2/1/1, losing the 2-instance zone puts the other 2 at " + Math.round((100 * PEAK) / ((base - 1) * PER)) + "% of capacity");

console.log("");
console.log("== 7. the redesigned system ==");
const dbHA = 1 - (FAILS * 120) / (YEAR_MIN * 60);
const total = serial([parts.dns, parts.lb, threeZones, dbHA, parallel(parts.cache, 2)]);
console.log("dns x lb x app(3 zones) x db(10s checks) x cache x2 = " + pct(total) + " -> " + down(total));

Discussion

Other approaches, follow-ups and how it went in real interviews.

Sign in to join the discussion.

No comments yet. Share how you would answer it.

Question 4 of 106 in System design

Keep your momentum going.

Mark this question solved to track your progress, then take the next one while it is fresh.

Up nextMedium

Edge caching on Cloudflare โ€” how do you get 95%+ hit rate without stale data?

Continue