System Design

7) Distributed systems theory — detailed guide

CAP & PACELC, consensus (Raft/Paxos), leader election + fencing, clocks & ordering, CRDTs — with a Facebook-like Home Feed example in every section.

A) CAP, PACELC, and consistency models

CAP (only forces a choice during partitions)

  • P (partition): timeouts, packet loss, region split, long GC pauses that look like a split.
  • Under P, you can’t guarantee both C (linearizable reads) and A (always respond successfully).
  • Real design: choose which endpoints become “strict” (C) vs “serve something” (A) when P happens.

PACELC (adds the normal-day trade)

  • If P → choose A or C.
  • Else → choose Latency or Consistency (e.g., cross-region quorum adds RTT).

Consistency models you can name in interviews

  • Linearizable (strong): reads see the latest committed write globally.
  • Sequential: everyone agrees on one order, but reads may not be “latest”.
  • Causal: preserves “happens-before”; independent writes may be seen in different orders.
  • Eventual / bounded staleness: replicas converge; may be stale for N seconds.
  • Session guarantees: read-your-writes, monotonic reads/writes (amazing UX win).
CAP/PACELC — template (what to say + how to decide)
Template
Checklist: • Identify your “must be correct” invariants (money, permissions, uniqueness). • Identify endpoints that can be stale (feeds, counters, recommendations). • Decide behavior during partitions: “fail closed” (C) vs “serve stale” (A). • Decide behavior when healthy: local low-latency (EL) vs quorum strong reads (EC). Interview line: “I’m PA/EL for feed reads and PC/EC for correctness paths like privacy + billing.”
Facebook-like Home Feed — CAP/PACELC choices
Example
Reads (feed timeline): • Prefer Availability + low latency → serve from nearest region (PA/EL). • Allow bounded staleness: ~1–10s. Writes (privacy settings / blocks): • Prefer Consistency → quorum write + read-your-writes (PC/EC). • During partitions: fail closed (don’t accidentally expose private content). Counters (likes/views): • Eventual with merge/CRDT; accuracy within seconds is OK.

B) Consensus & coordination (Raft/Paxos, ZooKeeper/etcd)

What consensus gives you

  • Single chosen leader (at a time) + quorum decisions.
  • Replicated log: committed entries survive node failures.
  • Safety: committed log entries aren’t “uncommitted” later (with correct Raft/Paxos).

Where to use (and where NOT to)

  • Use: leader election, distributed locks (with fencing), config/feature flags, membership metadata.
  • Don’t use: storing high-QPS business data (it will bottleneck and hurt reliability).
Consensus — template (how to explain it simply)
Template
• Pick Raft-backed systems (etcd/Consul/ZK) for coordination: leaders, locks, config. • Keep payloads tiny and traffic low (control plane). • Put business data in scalable stores/streams (data plane). • Treat coordination like a dependency with strict SLOs and tight access controls.
Facebook-like Home Feed — what uses consensus
Example
Control plane: • One “feed ranking config” source (feature flags, model version) stored in etcd. • Leader-elected “scheduler” per region triggers backfills and refresh jobs. Data plane: • Posts/reactions/events flow through Kafka/Kinesis. • Feed materialization data lives in partitioned storage (KV / wide-column / document), not etcd.

C) Leader election & fencing tokens (split-brain protection)

The real problem: split brain

  • GC pause or flaky network can make an old leader “think” it’s still leader.
  • Meanwhile a new leader is elected. Now two writers exist → corruption risk.

Fencing tokens (the fix)

  • Coordinator issues a monotonic token on leadership acquisition.
  • Every side-effect must carry the token; downstream stores max token and rejects lower.
Fencing token — template pseudo-code
Template
token = coordinator.acquire_leadership()     # monotonic, e.g. 1051

while coordinator.lease_valid(token):
    write_to_downstream(token, payload)

# Downstream:
# if token < max_seen_token_for_shard: reject as stale
Leader election + fencing — template
Template
• Use ephemeral nodes or leases for leader election. • ALWAYS attach fencing tokens to side-effects: - DB writes (store max token) - queue publishes (consumers validate token) - object store updates (conditional writes) • Prefer “reject stale writer” over “best effort”.
Facebook-like Home Feed — preventing split-brain fanout
Example
Scenario: • Region scheduler A (token=1051) publishes “rebuild feed” tasks. • A pauses (GC). Coordinator elects scheduler B (token=1052). • A resumes and tries publishing again. Protection: • Consumers store max_seen_token per partition/user. • Tasks from A (1051) are dropped; only B (1052) is accepted. Result: no duplicate or corrupt fanout.

D) Clocks & ordering (NTP, Lamport, vector clocks, HLC)

Clock types (what to use them for)

  • Wall clock (NTP): can jump backward/forward → fine for UI timestamps, risky for correctness.
  • Monotonic clock: timeouts, deadlines, backoff (per-node only).
  • Lamport clock: “happens-before” ordering without detecting concurrency.
  • Vector clock: detects concurrent updates (metadata grows with participants).
  • HLC: hybrid logical clock → near-time + monotonic, common in multi-region DBs.

Practical ordering rules

  • Total order needs consensus/log (Raft) or global sequencer.
  • Per-entity order is easy: partition by entity key (order within partition).
  • Don’t use timestamps for “latest wins” unless you control versioning (server-issued).
Clocks & ordering — template
Template
• For correctness: avoid wall-time ordering; use consensus or server-issued versions. • For streams: ordering is per partition → choose partition keys deliberately. • For causality-sensitive merges: version vectors / HLC only where needed. • For timeouts/retries: use monotonic clocks + deadlines, never wall clock.
Facebook-like Home Feed — ordering strategy
Example
Events: • Partition “post events” by post_id → edits/deletes are ordered for a post. • Partition “fanout tasks” by user_id → tasks for one user are ordered. Versions: • Use server-issued version numbers (or HLC) for “profile last updated” to avoid client clock skew. Timeouts: • Consumers use monotonic deadlines for processing + retries.

E) Conflict resolution & CRDTs (LWW, merges, repair)

Conflict strategies

  • LWW (last-write-wins): simplest; risks losing intent under concurrent writes.
  • CRDTs: converge via commutative/idempotent merges (great for counters/sets).
  • App merges: field-wise rules (sum/max/union), with audit + repair.

Repair / anti-entropy

  • Read repair: fix divergence when you read (good for hot keys).
  • Background repair: periodic reconciliation for cold keys.
  • Merkle trees: compare hashes to find divergent ranges efficiently.
Conflicts & CRDTs — template (how to choose)
Template
Checklist: • Does the field represent “intent” from many writers? (likes, follows, device list) → prefer CRDT. • Is the field “single-writer” or can be safely overwritten? (theme color, language) → LWW with server-issued version. • Are merges domain-specific? (ranking signals, counters with caps) → app merge rules + audit. Always: • Document conflict rules per field. • Add repair (read repair for hot, background repair for cold).
Facebook-like Home Feed — conflict choices by feature
Example
Reactions: • Likes/emoji counts → PN-Counter CRDT (no lost increments across regions). User lists: • “Muted creators”, “hidden posts” → OR-Set CRDT (add/remove converge correctly). Profile fields: • “Bio”, “profile photo” → LWW with server-issued version to reduce skew issues. Repair: • Hot users: read-repair on access. • Cold users: nightly background repair job.

Mini stance: multi-region settings (a clean design story)

Design goals

  • Low latency for user reads.
  • Correctness for privacy and identity invariants.
  • Clear conflict policy per field.
  • Operational clarity (observability + replay + repair).

Toolbox

  • Consensus for leaders/config.
  • Async replication cross-region.
  • CRDTs for intent-heavy sets/counters.
  • Versioned LWW for simple fields.
Mini stance — template you can reuse
Template
“I split the world into: 1) Correctness-critical invariants → strong/quorum, fail closed during partitions. 2) UX-critical reads → local/available, accept bounded staleness. 3) Intent-heavy shared state → CRDTs to avoid lost updates. Then I add fencing + repair + metrics to keep it safe in production.”
Facebook-like Home Feed — multi-region settings stance
Example
Privacy: • Writes require quorum; during partitions, block writes rather than risk exposure. Feed: • Reads served from nearest region; staleness ~1–10s is OK. State types: • Counters/sets: CRDT. • Simple preferences: LWW w/ server-issued versions. Ops: • Repair jobs + dashboards on divergence/conflicts + replay tools for streams.

Common pitfalls & fixes (what interviewers love)

  • Pitfall: “We’re strongly consistent” (everywhere). Fix: choose per endpoint; name invariants.
  • Pitfall: wall-time used for correctness. Fix: server-issued versions, consensus, HLC where needed.
  • Pitfall: leader election without fencing. Fix: monotonic tokens + downstream checks.
  • Pitfall: “exactly-once” belief. Fix: idempotency keys + atomic commits + dedupe stores.
  • Pitfall: no repair strategy. Fix: read-repair + background repair + monitoring.
Pitfalls — template (quick close)
Template
“I avoid global strong claims. I define invariants, choose per-endpoint guarantees, add fencing for leaders, use idempotency end-to-end, and back it with repair + metrics.”
Facebook-like Home Feed — pitfalls avoided
Example
• Don’t order global feed by timestamps across regions (clock skew). • Don’t store feed content in etcd/ZooKeeper (control plane only). • Expect duplicates; make consumers idempotent with dedupe keys. • Always provide replay + DLQ + repair tooling.

One-liners for interviews (memorable, accurate)

  • “CAP is about partitions; PACELC explains latency vs consistency on normal days.”
  • “Consensus is for control-plane choices; data plane stays partitioned for throughput.”
  • “Leader election needs fencing tokens; downstream must reject stale leaders.”
  • “Ordering is per partition unless you pay for a sequencer or consensus log.”
  • “CRDTs preserve user intent for counters/sets; LWW is for safe single-writer-ish fields.”
One clean closing line (template)
Template
“For a Home Feed: PA/EL for reads, strict for privacy and billing, CRDTs for reactions, and fencing + repair for safety.”
Facebook-like Home Feed — 20-second answer
Example
“Reads stay local and fast; we tolerate bounded staleness. Privacy and invariants use quorum/strong consistency. Events are ordered per key via partitioning. Counters and sets use CRDTs. Leaders use fencing tokens. And we back it all with repair, replay, DLQs, and dashboards.”