Foundationপ্রথম নীতি থেকে
LEVEL 9লেসন ৭/১৯অ্যাডভান্সড১ ঘণ্টা

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 বাড়ায়।

এই লেসন শেষে আপনি পারবেন

  • Primary-backup replication-এর sync/async trade-off RPO/RTO আর split-brain fencing lens দিয়ে বিশ্লেষণ করে বলতে পারবেন কোন ack-level-এ failover-এ কী হারায় তা বিচার করতে পারবেন
  • NWR parameter-space হিসাব করতে পারবেন — W+R>N intersection argument দিয়ে fresh-read guarantee-র proof-sketch আঁকতে পারবেন, আর সেই guarantee linearizability নয় কেন counterexample-সহ বলতে পারবেন
  • Sloppy quorum কখন availability-র জন্য overlap-guarantee ভাঙে আর hinted handoff ঋণ কীভাবে ফেরত দেয় তা timeline-এ trace করতে পারবেন
  • Read repair আর Merkle-tree anti-entropy healing-এর mechanism ব্যাখ্যা করে বলতে পারবেন কোনটা read-path-ব্যয়, কোনটা background-ঋণ — তুলনা করতে পারবেন
  • Chain replication (CRAQ)-এর throughput math primary-backup-এর সাথে তুলনা করে বলতে পারবেন কেন tail-read strongly consistent হয়, আর CRAQ-র clean-version read কীভাবে কাজ করে তা ব্যাখ্যা করতে পারবেন

আগে যা বোঝা থাকা দরকার

আগে এটা বুঝি

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-backupLeaderless NWRChain
কে write করেprimaryW-টা replicahead
কে read করেprimary/standbyR-টা replicatail (CRAQ: সবাই)
Strong readprimary-তেনা (freshness মাত্র)হ্যাঁ
Partition-এfence + minority-blocksloppy-তে servereconfigure

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 নয়

তিন ফাঁক:

  1. Concurrent writes-এ total order নেই — LWW-skew-এ পরে-complete করা write হার মানতে পারে;
  2. Monotonicity নেই — ভিন্ন R-set, ভিন্ন winner; user 12→11 দেখতেই পারে;
  3. 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 readschain/CRAQ reads
Standby readstale (async)clean-version = committed
Scalelag বাড়েlength-এ বাড়ে
Contracteventualsequential/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 response

W বাড়লে 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-ঋণে জমে।
leaderless write/read path — কোথায় কোন সিদ্ধান্ত
  1. coordinatorclient-এর request; preference-ring shuffle
  2. target selectalive-দের মধ্যে প্রথম W (strict) / ring-out-of-N (sloppy)
  3. apply + versionvector/timestamp-stamp, W-th apply-এ ack
  4. read quorumR-জিজ্ঞাসা, max-version বাছাই
  5. 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
Intersection argument — write-এর W-set আর read-এর R-set N-node-এ অবশ্যই ছেদে (W+R>N হলে); ছেদের node-ই fresh-read-জামিন।
   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
Chain replication — write head→tail, ack tail→head; tail-read always committed; CRAQ middle-node latest-clean-read (dashed) যোগ হয়।

নিজে চালিয়ে দেখুন

EXPERIMENT

N=3 cluster-এ W/R sweep + node-down-এ hinted-handoff নাটক

macOS / Linux / Windows (Python 3)· ২০ মিনিট
# 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 সংখ্যায়।

নিজে বানান

BUILD IT

QuorumKV — N=5 virtual cluster, failure-injection, read-repair, LWW-limit

Python · ●●●●○
  1. Replica = version-store + alive-flag; single key, monotonic version
  2. put: shuffled preference থেকে W-টা alive target, apply -> ack
  3. get: R-টা alive probe, max-version, mismatch হলে read repair
  4. Mid-run crash schedule — f=0,1,2 তে (W,R) matrix চালান
  5. সীমা-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 নয়।

বুঝেছেন কি না দেখুন

1

NWR-এর তিন 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 নয়।

2

Intersection-proof-এর লুকানো শর্ত দুইটা কী, না-মানলে কী হয়?

যুক্তি

(১) Version comparison — intersecting node-এর data চিনতে হবে, নইলে coordinator newest জানবে না; (২) max-version pick-rule — random-pick হলে stale-ই ফেরত যায়। Proof দেয় presence, মান-বাছাই আলাদা।

3

Example-এ t3-এ get({A,C}) কীভাবে C-কে সুস্থ করল? কোন mechanism?

প্রয়োগ

Read repair। Coordinator দেখল A:v1, C:stale — max-version (v1) laggard C-তে push। Cold-range এতে ঢলে না — সেটা anti-entropy-র কাজ।

4

Strict মোড 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 দিয়ে।

5

N=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।

6

Async-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