Quorum Replication — primary-backup, NWR, chain
Quorum Replication: Primary-Backup, NWR, Chain
W+R>N pigeonhole fresh-read দেয়, linearizability নয়; sloppy quorum চুক্তি ভেঙে availability কিনে, hinted handoff ঋণ শোধে, আর chain replication strong consistency-সহ read-throughput বাড়ায়।
আগে এটা বুঝি
Level 8-এ streaming replication দেখেছি — primary WAL পাঠায়, standby follow করে; consistency-models লেসন দিল চুক্তির ভাষা। এখন মূল প্রশ্ন: চুক্তি রাখার যন্ত্র কোন গুলো? একই hardware-এ তিন পরিবার তিন রকম চুক্তি দেয় —
1. primary-backup : একজন লিখবে, বাকিরা অনুকরণ করবে (leader-based)
2. leaderless NWR : যেকোনো replica-দল পড়বে-লিখবে (Dynamo-ধাঁচ)
3. chain : সারি করে দাঁড়াও, head লেখে tail পড়ে (strong+scale)Hook-টা সংখ্যায়: N=3, W=2, R=2 — তিন অক্ষর বদলালেই fresh-read
guarantee জন্মায়, latency বদলায়, failure-tolerance বদলায়। আর
সবচেয়ে জনপ্রিয় interview-mith — “quorum মানেই linearizable” —
আজ ভাঙব।
মূল ধারণা
Family map — leader-based বনাম leaderless
| Primary-backup | Leaderless NWR | Chain | |
|---|---|---|---|
| কে write করে | primary | W-টা replica | head |
| কে read করে | primary/standby | R-টা replica | tail (CRAQ: সবাই) |
| Strong read | primary-তে | না (freshness মাত্র) | হ্যাঁ |
| Partition-এ | fence + minority-block | sloppy-তে serve | reconfigure |
Primary-backup, গভীরে
databases/replication লেসনের ladder মনে করুন — synchronous_commit
off → local → remote_apply। প্রতিটা ধাপ আসলে RPO dial: async-এ
failover-এ lag-bytes হারায়, remote_apply-এ প্রায় শূন্য — দাম
প্রতি commit-এ network round-trip। RTO আলাদা axis: detection +
promotion time, automation দিয়ে ছোট হয়।
আর সবচেয়ে বিপজ্জনক ফাঁক: primary জীবিত কিন্তু network-isolated — monitoring ভাবে dead, standby promote হলো দুই primary। এটাই split-brain; একমাত্র প্রতিষেধক fencing — promote-এর আগে পুরনো primary-কে প্রমাণিতভাবে মারা। Consensus-tool (Patroni + etcd) এই ধাপ automate করে — consensus নিজে পরের অধ্যায়ের বিষয়।
WAL-ship-এর economy-টাও মনে করুন: এক log, দুই কাজ — crash-recovery আর replication-broadcast। দাম: write-path-এ primary-ই bottleneck, আর standby-read consistency নির্ভর করে lag-এর ওপর (গত লেসনের eventual ধাপ)।
Leaderless NWR — pigeonhole-এর জাদু
Parameters: N = per-key replica count; write ack পায় W-টা
replica apply-এর পরে; read জোগাড় করে R-টা replica-র উত্তর,
max-version নেয়।
proof-sketch (intersection):
W-set ∩ R-set ≥ W + R − N (pigeonhole)
W + R > N ⇒ intersection non-empty
⇒ read অন্তত একজন "latest-acked-write-দেখা" replica-কে জিজ্ঞেস করেছে
⇒ max-version read fresh value পায়শর্ত দুইটা লুকানো আছে: version-comparison লাগবেই (নইলে intersecting node-এর data চেনার উপায় নেই), আর guarantee টা freshness — last acknowledged write-এর মান বা তার পরের।
কেন এটা linearizability নয়
তিন ফাঁক:
- Concurrent writes-এ total order নেই — LWW-skew-এ পরে-complete করা write হার মানতে পারে;
- Monotonicity নেই — ভিন্ন R-set, ভিন্ন winner; user 12→11 দেখতেই পারে;
- Sloppy mode-এ overlap suspend — নিচে।
Sloppy quorum আর hinted handoff
Strict W-এর জন্য যথেষ্ট natural-replica জীবিত না থাকলে Dynamo-ধাঁচ হালছাড়ে না: preference-ring-এর বাইরের সুস্থ node write accept করে — sloppy quorum। Hint থাকে “এই version আসলে node-C-র”; C ফিরলে hinted handoff-এ ঘরে ফেরে। Availability-play — দাম: outage-window-এ W+R>N-এর promise suspend; drain-শেষে anti-entropy ঋণ শোধ করে।
Healing — read repair আর anti-entropy
Read repair: get-এ coordinator R-উত্তরে version-mismatch দেখলে newest-টা laggard-দের push করে — খরচ read-path-এ; যে key পড়া হয় সে-ই সেলে। Unread key-কে কেউ ঠেকবে না — তার জন্য anti-entropy: প্রতি replica key-range-ভিত্তিক hash- tree (Merkle tree); প্রতিবেশীরা root-hash মেলায়, mismatch-এ tree নেমে divergent sub-range-ই sync — full-scan ছাড়াই পার্থক্য-খোঁজ। Deleted key-দের জন্য tombstone বাধ্যতামূলক, নইলে “মৃত” row resurrection।
Chain replication — strong হয়েও scale
Configuration সারি: head → … → tail। Write: head append, chain ধরে এগোয়, tail apply-এর পর ack — tail-এর কাছে সব object fully- committed। Vanilla CR-এ read-ও tail-এই → strongly consistent by construction। CRAQ এগিয়ে: node-প্রতি object-version list (clean = tail-ack’d, dirty = pending); middle-node latest clean version serve করতে পারে — read-throughput chain-length-এ বাড়ে, চুক্তি একই। Write-throughput তবু head-bound — স্বীকার করা সীমা।
| primary-backup reads | chain/CRAQ reads | |
|---|---|---|
| Standby read | stale (async) | clean-version = committed |
| Scale | lag বাড়ে | length-এ বাড়ে |
| Contract | eventual | sequential/linear-ish |
ভেতরে কী ঘটছে
Parameter space — N বড় হলে কী হয়
N=5, W,R ∈ {1..5}। Overlap-শর্ত W+R>5; availability-শর্ত
write-এর জন্য alive >= W, read-এর জন্য alive >= R; f permanent
failure সহ্য করতে write-side চায় W <= N−f। তিন চাপ এক সূত্রে:
W+R>N : fresh read (quality)
W<=N-f : f-failure write (survival)
latency : ack = W-th slowest apply; read = R-th responseW বাড়লে freshness-survival ওঠে, latency ওঠে, availability নামে;
R-এর দিক উল্টো। (3,3) হলো N=5-এর classic balance — Dynamo-paper-ও
N=3,R=2,W=2-তে একই দর্শনে বসেছিল।
Freshness-এর সীমা — কোথায় ভাঙে
Experiment/build-এ দেখব এই model-এ W+R>N আসলেই stale% ≈ 0 দেয়,
permanent-failure-এও (dead node-এর কপি survivor-দের মধ্যে থাকলে
pigeonhole টিকে)। সীমাগুলো অন্য জায়গায়:
- Ordering: LWW-skew-এ পরে-complete করা write চিরতরে হারায় — freshness ≠ recency-across-concurrent-writes;
- Transient window: node-down-মুহূর্তে in-flight op-দের partially- dead ack-set → সংক্ষিপ্ত staleness;
- Healing debt: dead replicas-এর পেছনে থাকা data read-repair- অপেক্ষায়; unread range anti-entropy-ঋণে জমে।
- coordinatorclient-এর request; preference-ring shuffle
- target selectalive-দের মধ্যে প্রথম W (strict) / ring-out-of-N (sloppy)
- apply + versionvector/timestamp-stamp, W-th apply-এ ack
- read quorumR-জিজ্ঞাসা, max-version বাছাই
- repair branchmismatch দেখলে newest → laggards (read repair)
উদাহরণ
Full scenario — N=3 (A,B,C) + handoff-node D, W=R=2
t1 put(k,v1): targets {A,B} -> acked সুস্থ
t2 B crashes!
t3 get(R=2): {A,C} -> A:v1, C:stale -> repair: v1->C
t4 C crashes too (শুধু A জীবিত)
t5 strict: alive(1) < W(2) -> REJECT unavailable
t5' sloppy: ring -> {A,D} accept, D-তে hint available!
t6 get(R=2) -> {A,D}: দুজনেই latest fresh (window-এ!)
t7 B,C ফিরল -> handoff drain + C catch-up convergence
t8 সবাই latest; D-র residue sweep ঋণ শোধলক্ষ্য করুন তিনটা মোড: t3-এ overlap কাজ করেছে (C stale থাকলেও A fresh জানে); t5-এ strict চুক্তি রক্ষায় availability বিসর্জন (CAP-এর during-partition choose!); t5’-এ sloppy উল্টো বাছল — t7-এ ঋণের হিসাব। একই cluster, config-দর্শনে তিন চরিত্র।
ছবিতে
N = {A,B,C}
┌───────────────────┐
│ W-set={A,B} │
│ ┌───────┐ │
│ │ A ●B ●│◄─┐ │ |W∩R| ≥ W+R−N = 1
│ └───────┘ │ │ ছেদের node = latest acked copy
│ R-set={B,C}
│ └──● C │
└───────────────────┘
W+R <= N হলে sets disjoint হতে পারে → stale legal writes
▼
┌──────┐ fwd ┌──────┐ fwd ┌──────┐ reads (committed!)
│ HEAD │ ─────►│ MID │ ────►│ TAIL │──────────► client
└──────┘ └──────┘ └──────┘
▲ ▲ CRAQ: MID ← latest CLEAN version
└──── ack (tail-apply-confirmed) ◄─────┘
failure: master bypass-জোড়া বানিয়ে chain ছোট করে; recovery:
নতুন node predecessor-এর কাছ থেকে full-range syncনিজে চালিয়ে দেখুন
N=3 cluster-এ W/R sweep + node-down-এ hinted-handoff নাটক
# quorum_probe.py — N=3 cluster: W/R sweep + hinted-handoff outage
import random
def sweep_row(W, R, rounds=300, seed=42):
rng = random.Random(seed)
applied = [0] * 3 # replica -> latest version
inflight, ackq = [], []
acked, latencies, stales = 0, [], []
for t in range(1, rounds + 1):
v = t # ---- write: W-target apply ----
arrs = [(t + rng.randint(1, 3), v, i)
for i in rng.sample(range(3), W)]
inflight += arrs
ackq.append((max(a for a, _, _ in arrs), v, t))
done = [e for e in inflight if e[0] <= t] # ---- pump ----
inflight = [e for e in inflight if e[0] > t]
for _, ver, i in done:
applied[i] = max(applied[i], ver)
ready = [q for q in ackq if q[0] <= t]
ackq = [q for q in ackq if q[0] > t]
for at, ver, st in ready:
acked = max(acked, ver)
latencies.append(at - st)
picks = rng.sample(range(3), R) # ---- read ----
lag = max(0, acked - max(applied[i] for i in picks))
stales.append(lag)
s = sorted(stales)
ls = sorted(latencies)
pct = lambda xs, p: xs[min(int(len(xs) * p), len(xs) - 1)]
return (100 * sum(1 for x in s if x > 0) / len(s),
pct(ls, .50), pct(ls, .95))
print(f"{'config':<10}{'stale%':>8}{'w-p50':>6}{'w-p95':>6} W+R>N?")
for W in (1, 2, 3):
for R in (1, 2, 3):
sp, l50, l95 = sweep_row(W, R)
mark = "হ্যাঁ" if W + R > 3 else "না"
print(f"W={W},R={R:<4}{sp:>7.1f}%{l50:>6}{l95:>6} {mark}")
def outage(sloppy, horizon=170):
"""A,C মরে tick 41/61-এ, 101-এ ফেরে; W=2."""
alive = {"A": True, "B": True, "C": True}
store = {"A": 0, "B": 0, "C": 0, "D": 0}
hints, pending = [], [] # D-র জমা / drain-queue
rejects = offloaded = drained_at = 0
v = rr = 0
for t in range(1, horizon + 1):
if t == 41: alive["C"] = False
if t == 61: alive["A"] = False
if t == 101: # ফিরল -> drain শুরু
alive["A"] = alive["C"] = True
pending = sorted(hints); hints = []
natural = [n for n in "ABC" if alive[n]]
ring = natural[:] + (["D"] if sloppy and len(natural) < 2 else [])
k = rr % len(ring); pref = ring[k:] + ring[:k]; rr += 1
if len(pref) >= 2: # W=2 reachable?
v += 1
for n in pref[:2]:
store[n] = max(store[n], v)
if n == "D":
hints.append(v); offloaded += 1
else:
rejects += 1
if t >= 101 and pending: # প্রতি tick-এ এক hint ঘরে
store["C"] = max(store["C"], pending.pop(0))
if not pending:
drained_at = t
if sloppy and t % 40 == 0:
print(f" tick {t:>3}: pending-hints = "
f"{len(hints) + len(pending)}")
return store, rejects, offloaded, drained_at, v
store, rej, off, dr, v = outage(sloppy=False)
print(f"STRICT : outage rejects = {rej} (down)")
store, rej, off, dr, v = outage(sloppy=True)
print(f"SLOPPY : offloaded-on-D = {off}, drained at tick {dr}")
top = max(store.values())
for n_ in store: store[n_] = top # anti-entropy pass
assert store["A"] == store["B"] == store["C"] == v
print("OK: drain + anti-entropy -> converged")প্রত্যাশিত output (seed-fixed):
config stale% w-p50 w-p95 W+R>N?
W=1,R=1 67.3% 2 3 না
W=1,R=2 30.7% 2 3 না
W=1,R=3 0.0% 2 3 হ্যাঁ
W=2,R=1 21.0% 3 3 না
W=2,R=2 0.0% 3 3 হ্যাঁ
W=2,R=3 0.0% 3 3 হ্যাঁ
W=3,R=1 0.0% 3 3 হ্যাঁ
W=3,R=2 0.0% 3 3 হ্যাঁ
W=3,R=3 0.0% 3 3 হ্যাঁ
STRICT : outage rejects = 40 (down)
tick 40: pending-hints = 0
tick 80: pending-hints = 20
tick 120: pending-hints = 20
tick 160: pending-hints = 0
SLOPPY : offloaded-on-D = 40, drained at tick 140
OK: drain + anti-entropy -> convergedপড়ার সময়: sweep-এ প্রতিটা হ্যাঁ row একটা theorem-instance; outage-এ
hint-curve 61→101 উঠবে, drain-এ নামবে — availability আর healing-ঋণ live।
W+R>N rows-এ stale% সত্যিই শূন্য, বাকিগুলোতে না; strict quorum node-down-এ unavailable হয় যেখানে sloppy + hinted handoff চালু রাখে এবং recovery-তে queue-drain দেখায় — availability-vs-contract trade-off সংখ্যায়।
নিজে বানান
QuorumKV — N=5 virtual cluster, failure-injection, read-repair, LWW-limit
- Replica = version-store + alive-flag; single key, monotonic version
- put: shuffled preference থেকে W-টা alive target, apply -> ack
- get: R-টা alive probe, max-version, mismatch হলে read repair
- Mid-run crash schedule — f=0,1,2 তে (W,R) matrix চালান
- সীমা-demo: availability law (W ≤ N−f) আর LWW clock-skew anomaly
লক্ষ্য: W+R>N empirically verify — সহিত limits: availability-math আর LWW-recency-violation।
# quorum_kv.py — QuorumKV: N=5, tunable W/R, failures, read-repair
import random
class QuorumKV:
def __init__(self, n=5, seed=11):
self.rng = random.Random(seed)
self.n = n
self.applied = [0] * n
self.alive = [True] * n
self.repairs = self.ok_w = self.fail_w = self.ok_r = 0
def _pick(self, k):
idx = list(range(self.n)); self.rng.shuffle(idx)
return [i for i in idx if self.alive[i]][:k]
def put(self, v, W):
t = self._pick(W)
if len(t) < W:
self.fail_w += 1 # quorum-unavailable
return False
for i in t:
self.applied[i] = max(self.applied[i], v)
self.ok_w += 1
return True
def get(self, R, acked):
picks = self._pick(R)
vals = [self.applied[i] for i in picks]
top = max(vals)
if min(vals) < top: # laggard -> READ REPAIR
for i in picks:
if self.applied[i] < top:
self.applied[i] = top
self.repairs += 1
self.ok_r += 1
return top, max(0, acked - top)
def workload(W, R, ops=300, crashes=(), seed=11):
kv = QuorumKV(seed=seed)
acked = stale_hits = 0
for v in range(1, ops + 1):
for at, victim in crashes:
if v == at:
kv.alive[victim] = False # mid-run failure!
if kv.put(v, W):
acked = v
_, lag = kv.get(R, acked)
stale_hits += lag > 0
return {"stale%": 100 * stale_hits / ops,
"w%": 100 * kv.ok_w / ops,
"repairs": kv.repairs}
CRASHES = {0: (), 1: ((150, 4),), 2: ((150, 4), (225, 3))}
print(f"{'combo':<11}{'f=0':>13}{'f=1':>13}{'f=2':>13} W+R>5?")
for W in (2, 3, 4):
for R in (2, 3, 4):
cells, reps = [], 0
for f in (0, 1, 2):
r = workload(W, R, crashes=CRASHES[f])
cells.append(f"{r['stale%']:>3.0f}%/{r['w%']:>3.0f}%")
reps += r["repairs"]
mark = "Y" if W + R > 5 else "."
print(f"W={W},R={R:<4}" + "".join(f"{c:>13}" for c in cells)
+ f"{reps:>7} {mark}")
# সীমা ১: availability law — f=2-তে alive=3, W=4 অসম্ভব
a4 = workload(4, 3, crashes=CRASHES[2])["w%"]
a3 = workload(3, 3, crashes=CRASHES[2])["w%"]
print(f"availability: f=2 -> W=4:{a4:.0f}% W=3:{a3:.0f}%")
# সীমা ২: LWW skew — W+R>N তবু recency violation
kv = QuorumKV()
kv.applied[0] = kv.applied[1] = 20 # w1(ts=20)
kv.applied[2:] = [10, 10, 10] # w2(ts=10, later!)
kv.rng.seed(5)
obs = max(kv.applied[i] for i in kv._pick(3))
print(max(kv.applied)) # 20 -> w1 জিতলপ্রত্যাশিত output (seed-fixed):
combo f=0 f=1 f=2 W+R>5?
W=2,R=2 33%/100% 24%/100% 20%/100% 728 .
W=2,R=3 12%/100% 7%/100% 7%/100% 1355 .
W=2,R=4 0%/100% 0%/100% 0%/100% 1967 Y
W=3,R=2 9%/100% 4%/100% 4%/100% 526 .
W=3,R=3 0%/100% 0%/100% 0%/100% 907 Y
W=3,R=4 0%/100% 0%/100% 0%/100% 1181 Y
W=4,R=2 0%/100% 0%/100% 0%/ 75% 239 Y
W=4,R=3 0%/100% 0%/100% 0%/ 75% 389 Y
W=4,R=4 0%/100% 0%/100% 0%/ 75% 479 Y
availability: f=2 -> W=4:75% W=3:100%
20তিন পাঠ: (১) Y-rows সব f-level-এ stale-free — pigeonhole permanent-
failure-ও টেকে; (২) f=2-তে W=4-এর write 75% — availability-law
W <= N-f; (৩) শেষ line-এ LWW-দুর্ঘটনা: w1 complete, তারপর w2,
quorum-read তবু w1 — W+R>N-এর চুক্তি ordering-এর নয়।
নিজে বাড়ান: sloppy-mode + hint-queue যোগ করে build-এও outage চালান; multi-key history-তে monotonic-read violation ধরুন।
বাস্তব সিস্টেমে
Amazon Dynamo (2007)। Shopping-cart-এর N=3,W=2,R=2; sloppy quorum,
hinted handoff, vector-clock merge — toolbox-এর জন্মপত্র; DynamoDB,
Riak, Cassandra-র DNA। Cassandra practice: RF=3 + LOCAL_QUORUM =
(2,2)-ধাঁচ; anti-entropy-র নাম repair — Merkle-based ops-routine।
Azure Storage। Windows Azure Storage-এর stream-layer chain replication ব্যবহার করে — CR-theory-র বৃহত্তম production deployment।
Kafka ISR। acks=all + min.insync.replicas=2 — quorum-চিন্তার
leader-based রূপ: ISR min-size না হলে leader write-refuse করে।
যে ভুলগুলো সবাই করে
“W+R>N মানে linearizability পাওয়া।”
Overlap দেয় freshness — last-acked-write-এর মান। Linearizability চায় আরও: total order, প্রতি-op real-time respect। LWW-skew demo (build) দেখায় fresh-read তবু recency-ভাঙা হতে পারে — চুক্তিটা causal/eventual-ঘেঁষা, top-step নয়।
“(W=1,R=N) আর (W=N,R=1) একই জিনিস — শুধু উল্টে গেছে।”
Overlap symmetric, বাস্তব প্রোফাইল নয়: W=1-এ write sub-ms আর f=N−1-সহ্য, কিন্তু read সব replica round-trip; W=N-এ উল্টো। Availability, latency, cost — তিন axis-এই দুই combo বিপরীত মেরু।
“Sloppy quorum শুধু routing-trick — guarantee অটুট।”
Sloppy মানেই natural-ring-এর বাইরে serve — intersection-assumption ভিত্তি হারায়; window টা by-design weaker। Hinted handoff ঋণ ফেরতের যন্ত্র, contract-restore-এর নিশ্চয়তা নয় (drain-চলাকালীন আবার node মরলে নতুন ঋণ)।
“Chain replication-এ tail তো সবার শেষে — reads stale।”
উল্টোটাই design-কৌশল: write ack tail-apply-এর পরেই — tail-এর কাছে শুধু committed data, read strongly consistent। Stale-read-ঝুঁকি ছিল primary-backup-এর async-standby-তে; CRAQ middle-read-ও latest- clean serve করে।
“Read repair হলে cluster তো নিজে-নিজে ঠিক হয়ে যায়।”
Read repair শুধু যে keys পড়া হয় সেগুলোর touched-replica ছোঁয় — cold-range divergence বাইরে; সেই ঋণ anti-entropy (Merkle)-এর, যার নিজস্ব schedule আর bandwidth-cost। দুই-ই লাগে, substitute নয়।
বুঝেছেন কি না দেখুন
1NWR-এর তিন letter কী? W+R>N কোন দাবি দেয়, কোনটা দেয় না?
স্মরণ
N=per-key replica, W=write-ack threshold, R=read-quorum। দেয়: fresh read (intersecting node holds last-acked write)। দেয় না: total order / recency-across-concurrent-writes — linearizability নয়।
2Intersection-proof-এর লুকানো শর্ত দুইটা কী, না-মানলে কী হয়?
যুক্তি
(১) Version comparison — intersecting node-এর data চিনতে হবে, নইলে coordinator newest জানবে না; (২) max-version pick-rule — random-pick হলে stale-ই ফেরত যায়। Proof দেয় presence, মান-বাছাই আলাদা।
3Example-এ t3-এ get({A,C}) কীভাবে C-কে সুস্থ করল? কোন mechanism?
প্রয়োগ
{A,C}) কীভাবে C-কে সুস্থ করল? কোন mechanism?Read repair। Coordinator দেখল A:v1, C:stale — max-version (v1) laggard C-তে push। Cold-range এতে ঢলে না — সেটা anti-entropy-র কাজ।
4Strict মোড outage-এ ৪০ reject, sloppy ৪০ accept + D-তে hint। Sloppy
জয়ের দাম ঠিক কী?
যুক্তি
Window-টায় W-set natural-ring-এর বাইরে — intersection-assumption suspend; versions-এর durability এক extra node (D)-এর হাতে, contract drain+anti-entropy-শেষেই ফেরে। Availability কিনেছে deferred-contract দিয়ে।
5N=5-এ f=2 failure সহ্য এবং fresh-read চাই। W,R কী হবে, দাম কী?
ডিজাইন
Survival: W <= N−f = 3; freshness: W+R>5 → (W,R)=(3,3) minimum।
দাম: প্রতি write ৩-apply round-trip, প্রতি read ৩-probe; f=3-এই
write-dead — availability-ceiling মেনে capacity-plan।
6Async-standby read আর CRAQ middle-node read — দুই-ই “পেছনের node থেকে
পড়া”। Strong-consistency পার্থক্য কোথায়?
যুক্তি
Async-standby lag-bound — কোনো bound নেই, যত পুরনোই হোক legal (eventual)। CRAQ middle lag-bound-নয়: serve করে latest clean (= tail-ack’d) version, committed-prefix-এর বাইরে যায় না।
এরপর কী
এরপর কী
তিন পরিবারের যন্ত্র হাতে। কিন্তু leader-based পরিবারের সব promise primary জীবিত থাকা-ই ধরে — মরলে কে হবে? কে decide করবে? পরের লেসন: leader election — split-vote, quorum-of-eligibility, consensus-দরজা; এরপর Paxos-intuition আর Raft। Module driving question — “একদল machine কীভাবে একমত হয়?” — এবার মুখোমুখি।
আরও পড়ুন
- Dynamo: Amazon's Highly Available Key-value Store — Giuseppe DeCandia et al. — SOSP 2007 · NWR, sloppy quorum, hinted handoff, vector-clock reconciliation — এই লেসনের leaderless-অর্ধেকের মূল উৎস
- Chain Replication for Supporting High Throughput and Availability — Robbert van Renesse, Fred B. Schneider — OSDI 2004 · Chain replication-এর মূল paper — tail-read strongly consistent design-এর জন্ম
- Object Storage on CRAQ: High-throughput Chain Replication for Read-mostly Workloads — Jeff Terrace, Michael J. Freedman — USENIX ATC 2009 · CRAQ — middle-node reads দিয়ে chain-কে read-scale করার সংস্করণ
- Cassandra — A Decentralized Structured Storage System — Avinash Lakshman, Prashant Malik — SIGMOD 2010 · Dynamo-design-এর open-source রূপ; per-op consistency level-এর বাস্তব practice