Foundationপ্রথম নীতি থেকে
LEVEL 9লেসন ৬/১৯রিসার্চ-লেভেল১ ঘণ্টা ৫ মিনিট

Consistency Models — linearizability থেকে eventual ladder

Consistency Models: Linearizability to Eventual

Consistency model মানে replica-client চুক্তি — linearizability real-time recency চায়, sequential শুধু per-process ক্রম, causal কারণ-প্রভাব, eventual শুধু শেষে মিল; ধাপ নামলে latency লাভ, reasoning-ভার application-এ।

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

  • Linearizability-র সংজ্ঞা real-time interval আর linearization point ধরে বলতে পারবেন, আর একটা operation history দেখে সেটা linearizable কি না নিজে রায় দিতে পারবেন
  • Sequential consistency ঠিক কোন শর্তটা ফেলে দেয় তা চিহ্নিত করতে পারবেন, আর সেই ফেলে-দেওয়া শর্তেই কোন অপ্রত্যাশিত history legal হয়ে যায় দেখাতে পারবেন
  • Causal consistency আর eventual convergence-এর সীমানা টানতে পারবেন, আর monotonic-read violation-জাতীয় session anomaly দুই-replica timeline-এ trace করতে পারবেন
  • ZooKeeper, Cassandra, Dynamo, PostgreSQL — প্রত্যেকে ladder-এর কোন ধাপে কোন config-এ দাঁড়ায় তা mechanism-সহ বলতে পারবেন
  • Interleaved history-তে brute-force checker চালিয়ে প্রতিটা model-এর verdict আর counterexample order নিজের হাতে বের করতে পারবেন

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

আগে এটা বুঝি

CAP লেসনে map-টা দেখেছিলাম — linearizable > sequential > causal > eventual সিঁড়িটা গায়ে-গত্তে ছিল। গত লেসনে happens-before relation হাতে এলো। কিন্তু একটা অস্বস্তি ঝুলছে: ঠিক কী দাবি করে প্রতিটা ধাপ? “Strong consistency” বলা সহজ; কিন্তু interview-board-এ “strong মানে কোনটা?” জিজ্ঞেস করলেই ঘর পরিষ্কার হয়ে যায়।

আজকের hook একটা ছোট history:

A: w(x = 1)   t = 0..10        (complete!)
B: r(x) -> 0  t = 20..30      ← পড়ল init!

A-এর লেখা ১০ tick-এ শেষ হয়ে গেছে; B পড়েছে ২০-এ — তবু পুরনো মান পেল। প্রশ্ন: এটা কি bug? আশ্চর্যের উত্তর — কোন model-এর চুক্তি তার ওপর দাঁড়ায় সেটা না বললে প্রশ্নটাই অর্থহীন। এক model-এ এটা অপরাধ, আরেক model-এ একেবারে legal।

আজ চুক্তিগুলো আইনের ভাষায় পড়ব: প্রতিটা model মানে history-র ওপর একটা predicate — কোন interleaving মেনে নেবে, কোনটা ফেরত দেবে। আর শেষে বিচারক-কোড লিখব যে ইতিহাস পড়ে রায় দেয়।

মূল ধারণা

Model মানে history-র ওপর predicate

ভোকাবুলারি আগে। Operation-এর জীবন দুই মুহূর্তের: invocation (client call) আর response (ack ফেরা) — মাঝখানের জায়গাটা interval। Client-দের চোখে যত op-এর invocation/response সাজানো থাকে, সেটাই history। Consistency model = একটা নিয়ম: এই history-র পেছনে এমন কোনো এক-ক্রম (total order) আছে কি, যেটা নির্দিষ্ট শর্ত মেনে চলে? Model-ভেদে শর্ত বদলায় — system-গুলো আসলে বলে না “আমরা fast,” বলে “আমাদের history এই predicate পাস করে।”

Linearizability — real-time recency

শর্ত দুইটা:

  1. প্রতিটা op কোনো না কোনো মুহূর্তে ঘটে — সেই মুহূর্ত op-এর invocation আর response-এর মাঝে (একে linearization point বলি);
  2. সব op-এর linearization point-এর ক্রম একটা valid single-object total order দেয় — এবং সেই ক্রম real-time সম্মান করে: এক op-এর response অন্যটার invocation-এর আগে হলে, প্রথমটা order-এও আগে।

ফল প্রতিটা read-এর জন্য: completed write-এর পরে শুরু হওয়া read নতুন মান দেখবে (বা তার পরে কোনো write হলে সেটা)। Intro-র history-তে A-এর write complete (t=10) হয়েছে B-এর read-start (t=20)-এর আগে — real-time শর্ত B-এর read-কে write-এর পরে বসাতে বাধ্য করে, তখন read-এর 0 মেলে না → not linearizable।

দামটাও এখানেই: real-time respect মানে প্রতিটা op-এর জন্য replicas কারো সাথে না কারো coordination — consensus-জাতীয় যন্ত্র ছাড়া partition-এ এই promise রাখা অসম্ভব (CAP-এর C ঠিক এটাই)।

Sequential consistency — real-time ফেলে দাও

Lamport ১৯৭৯-এ একটা শর্ত বাদ দিয়ে দেখলেন: order-টা যদি শুধু প্রতিটা process-এর নিজের op-ক্রম সম্মান করে, cross-process ঘড়ি-ক্রম না মানে? Intro-র একই history এবার legal — order সাজাও [B:r->0, A:w(1)]: B একা, A একা, কারো নিজের ক্রম ভাঙে না, আর cross-process কোনো real-time duty নেই।

Mind-bending কিন্তু মনে রাখার মতো দুই মুখ:

  • Legal হয়ে যায়: “write complete-এর অনেক পরের read পুরনো মান দেখল” — যতক্ষণ ওই read-এর process-এর নিজের আগের op-গুলো মিলে যায়।
  • তবু lawless নয়: Dekker-ধাঁচের ফল — A: w(x=1) তারপর r(y)->0; B: w(y=1) তারপর r(x)->0 (দুইজনই দুই মান শূন্য দেখল) — এটা sequential consistency-ও ভাঙে: per-process order চাপাচাপি করে cycle বানায় w_x < r_y < w_y < r_x < w_x। CPU memory-model জগতে ঠিক এই ফলটাই acquire/release দিয়ে আটকানো হয়।

Causal consistency — শুধু কারণ-প্রভাব ধরো

গত লেসনের relation এখানে contract হয়ে ফেরে: যে op-pair-এর মধ্যে happens-before edge আছে, সব replica/observer একই ক্রমে দেখবে; concurrent write-গুলো নিয়ে কেউ কারো কাছে রাজি নয় — ভিন্ন replica ভিন্ন ক্রমে merge করলেও চলে। Reply-before-parent অসম্ভব হয়ে যায়, কিন্তু unrelated দুই post-এর display-order স্বাধীন থাকে।

Eventual — শুধু শেষ কথাটা

Promise একটাই: write থামলে (quiescence) আর replication network ঠিক থাকলে, সব replica শেষে একই value-তে converge করবে। পথের কোনো মুহূর্তে, কোনো read-এর, কোনো ক্রমের — কোনো দাবি নেই। Convergence-এর জন্য লাগে conflict-rule (LWW/vector/CRDT) — CAP লেসনের কথা মনে আছে।

শক্তি ▲
      │  linearizable   real-time + total order   <- consensus-দাম
      │  sequential     per-process order only
      │  causal         শুধু happens-before chain
      │  eventual       quiescence-এ convergence
      └──────────────────────────────► latency-লাভ

ভেতরে কী ঘটছে

Anomaly zoo — দুর্বল চুক্তির দৈনিক ভাড়াটে

Monotonic-read violation: user প্রথমে replica-A-তে (lag ০) comment-count 12 দেখল; refresh-এ load-balancer replica-B-তে (lag ৩s) — এখন 11। User-এর চোখে ভবিষ্যৎ ফিরে এলো। History-ভাষায়: দুই read ভিন্ন replica-র ভিন্ন snapshot — কোনো model-ভাঙা না, যদি চুক্তিটা eventual-ই থাকে।

Read-your-writes violation: গত লেসনের violation #2 — password বদলের ack-এর পরে lagging replica পুরনো hash দেখাল। Session-boundary security incident।

Bouncing-between-replicas: উপরের দুটোর common root — পরপর request ভিন্ন replica-তে পড়ছে; staleness distribution-এর random sample user নিজে দেখছে।

Sticky routing — সস্তা ঔষধ

Anomaly-গুলোর বেশিরভাগের cure মাত্র একটা: এক session-এর সব request এক replica-তে (বা catch-up না হওয়া পর্যন্ত redirect)। Load-balancer/app-tier-এ session-affinity — নতুন consensus, নতুন vector-clock কিছুই লাগে না। Read-your-writes আর monotonic-read দুই-ই session-ভেতরে ফিরে আসে। খরচ: node-down-এ rebind-মুহূর্তে anomaly-window, hot-replica skew, আর cross-session causality এখনো uncovered।

Implementation ladder — কে কোথায় দাঁড়ায়

SystemGuaranteeMechanismConfig
ZooKeeperwrites linearizable-ish; reads sequentialZAB total-order log + versioned znodesync-flag
Cassandraper-op tunable ONE→ALLreplica-count overlap + timestampsCL per query
DynamoDBeventually consistent / strongly (per-op)quorum + version contextConsistentRead
PostgreSQL primaryper-object linearizablesingle-writer serializationdefault
PG standby readeventualWAL lag windowhot_standby

খরচের সারাংশ: উপরের ধাপে উঠতেই coordination — প্রতি op বা প্রতি epoch consensus round-trip; নামলে replicas local-serve করে, latency floor ভেঙে পড়ে, আর reasoning-ভার application-কোডে চলে আসে। এই trade-off-এর সংখ্যা experiment-এ।

উদাহরণ

এক history, তিন আদালত

Register x, init 0। তিন client, ছয় op:

H:
  c1: w(x=1)          t = 0..1
  c2: r(x) -> 0       t = 2..3
  c2: r(x) -> 1       t = 4..5

আদালত ১ — linearizable: c2-এর প্রথম read start (t=2) > write-end (t=1) → real-time শর্ত write-কে আগে বসাতে বাধ্য করে; তখন read-এর মান 1 হওয়ার কথা, দেখল 0 → দোষী। Counterexample- morph দরকার নেই — শর্তই ভাঙা।

আদালত ২ — sequential: real-time শর্ত ফেলে order সাজানো যায়? [r->0, w(1), r->1] — প্রতিটা client-এর নিজের ক্রম অক্ষত (c2-এর দুই read ক্রমে), মান-মিল ঠিক (init 0 → read 0; write 1 → read 1) → খালাস। Legal order-টাই verdict-এর witness।

আদালত ৩ — causal: c1-এর write আর c2-এর read-এর মধ্যে message-path ধরা যায় না — এরা concurrent; causal-consistent world-এ এই history-র জন্য রায়ই লাগে না: কোনো causal-chain ভাঙেনি।

মোড়ের কথা — history-টাই যদি একটু ঘুরিয়ে দিই:

H':
  c1: w(x=5)       t = 0..1
  c2: r(x) -> 5    t = 2..3
  c2: r(x) -> 0    t = 4..5

এবার c2-এর per-process order (r->5 আগে, r->0 পরে) এমন order দাবি করে যেখানে 5 লেখার পরেও 0 পড়া যায় — value-check-ই cycle বানায় → sequential-ও দোষী। H আর H’-এর ফারাকটাই model- সংবেদনশীলতা — চোখে আঙুল দিয়ে দেখানো।

ছবিতে

overlap:   A: w(x=1)            non-overlap: A: w(x=1)
              ├───────┤                        ├──┤
           B: r(x)->0 or 1  ← দুই-ই legal      │
              ├────────┤                       ▼
                                        B: r(x)->1  ← শুধু এটাই
              (lin: দুই order-ই সম্ভব)      (r->0 = violation)
Real-time intervals — প্রতিটা op-এর ভেতরে একটা linearization point; বাঁয়ের জোড়া overlap করে (order স্বাধীন), ডানের জোড়া real-time-ordered (order বাধ্যতামূলক)।
 linearizable ── ZK-writes, PG-primary, etcd, Spanner*
      │                     (*TrueTime-bounded wait)
 sequential ──── ZK-reads (sync ছাড়া), CPU-TSO-ish
      │
 causal ──────── MongoDB-sessions, COPS, comment-threads
      │
 eventual ────── Cassandra ONE, DNS-cache, view-counters

 latency-cost ◄────────────────────────────► guarantee
Ladder-এ বাস্তব system-দের বাসা — একই system-এর ভেতরেও endpoint/config-ভেদে একাধিক তলা।

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

EXPERIMENT

R/W dial ঘুরিয়ে staleness distribution মাপা — আর linearizability-ভাঙা window ধরা

macOS / Linux / Windows (Python 3)· ২০ মিনিট
# staleness_probe.py — N=3 KV cluster-এ R/W dial আর violation window
import random


def run(W, R, rounds=300, seed=42):
    rng = random.Random(seed)
    N = 3
    applied = [0] * N          # প্রতিটা replica-র latest applied version
    inflight, ackq = [], []    # wire আর pending-ack queue
    acked = 0                  # client-confirmed সর্বোচ্চ version
    latencies, stales = [], []
    viol_trace = None

    for t in range(1, rounds + 1):
        v = t                                  # ---- write ----
        arrs = [(t + rng.randint(1, 3), v, i)
                for i in rng.sample(range(N), 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(N), R)             # ---- read ----
        obs = max(applied[i] for i in picks)        # max-version read
        lag = max(0, acked - obs)
        stales.append(lag)
        if lag > 0 and viol_trace is None:
            viol_trace = (t, acked, obs)            # lin-violation!

    s = sorted(stales)
    ls = sorted(latencies)
    pct = lambda xs, p: xs[min(int(len(xs) * p), len(xs) - 1)]
    stale_pct = 100 * sum(1 for x in s if x > 0) / len(s)
    return stale_pct, pct(s, .95), pct(ls, .50), pct(ls, .95), viol_trace


print(f"{'config':<10}{'stale%':>8}{'p95lag':>8}"
      f"{'w-p50':>6}{'w-p95':>6}   W+R>N?")
for W, R in [(1, 1), (2, 2), (3, 1)]:
    sp, l95, l50, l95, _ = run(W, R)
    mark = "হ্যাঁ" if W + R > 3 else "না"
    print(f"W={W},R={R:<4}{sp:>7.1f}%{l95:>8}{l50:>6}{l95:>6}   {mark}")

_, _, _, _, vt = run(1, 1)
t, exp, got = vt
print(f"\nviolation window (W=1,R=1): tick {t}-এ v{exp}-এর ack "
      f"already-complete, তবু read দেখল v{got}")

প্রত্যাশিত output (seed-fixed — deterministic):

config      stale%  p95lag w-p50 w-p95   W+R>N?
W=1,R=1      67.3%       3     2     3   না
W=2,R=2       0.0%       3     3     3   হ্যাঁ
W=3,R=1       0.0%       3     3     3   হ্যাঁ

violation window (W=1,R=1): tick 2-এ v1-এর ack already-complete, তবু read দেখল v0

পড়ার সময়: (1,1)-এ প্রায় দুই-তৃতীয়াংশ read stale — eventual- contract-এর দৈনিক জীবন; (2,2)-তে overlap-এর জাদু — window শূন্য (কেন, পরের লেসনের pigeonhole-proof); আর violation-line-টাই intro-র history-র simulation-রূপ — completed write, তবু পুরনো মান।

এটা কী প্রমাণ করে

Async replication-এ W/R dial staleness-এর histogram বদলায়; completed write-এর পরেও stale read ধরা পড়ে — linearizability violation live; আর QUORUM-overlap দিলে window বন্ধ হয়, যেটা পরের লেসনের NWR-গণিতের প্রিভিউ।

নিজে বানান

BUILD IT

ConsistencyChecker — history পড়ে প্রতি model-এ রায় + witness

Python · ●●●●○
  1. History-op model করুন: id, client, kind, val/seen, start, end
  2. Value-check: এক order-এ হাঁটুন, প্রতিটা read তার সামনের শেষ write-এর সাথে মিলছে কি
  3. Linearizable = value-check + real-time constraint মেনে কোনো permutation
  4. Sequential = শুধু value-check + per-client order edges
  5. n <= 7 brute-force — প্রতিটা model-এ witness-order বা দোষ ছাপুন

লক্ষ্য: উপরের আদালত-তিনটার automation। একই engine তিনবার চলবে — শুধু constraint-set বদলাবে; রায়ের সাথে legal order (বা তার অনুপস্থিতি) প্রমাণ হিসেবে ছাপবে।

# consistency_checker.py — ছোট history-তে model-verdict + counterexample
from itertools import permutations

H2 = [
    dict(id="w1", client="A", kind="w", val=1, start=0, end=1),
    dict(id="r1", client="B", kind="r", seen=0, start=2, end=3),
    dict(id="r2", client="B", kind="r", seen=1, start=4, end=5),
]
H3 = [
    dict(id="w1", client="A", kind="w", val=5, start=0, end=1),
    dict(id="r1", client="B", kind="r", seen=5, start=2, end=3),
    dict(id="r2", client="B", kind="r", seen=0, start=4, end=5),
]


def program_edges(hist):
    """একই client-এর op-গুলোর মধ্যে per-process order edge"""
    by_c, edges = {}, set()
    for op in hist:
        by_c.setdefault(op["client"], []).append(op)
    for ops in by_c.values():
        ops = sorted(ops, key=lambda o: o["start"])
        for a, b in zip(ops, ops[1:]):
            edges.add((a["id"], b["id"]))
    return edges


def feasible(order, hist, realtime, edges):
    pos = {op["id"]: i for i, op in enumerate(order)}
    cur = 0                                     # register init = 0
    for op in order:                            # value-consistency
        if op["kind"] == "w":
            cur = op["val"]
        elif op["seen"] != cur:
            return False
    if realtime:                                # linearizability-শর্ত
        for a in hist:
            for b in hist:
                if a is not b and a["end"] <= b["start"] \
                   and pos[a["id"]] > pos[b["id"]]:
                    return False
    return all(pos[a] < pos[b] for a, b in edges)


def find_witness(hist, realtime, edges):
    assert len(hist) <= 7, "brute-force n<=7"
    for perm in permutations(range(len(hist))):
        order = [hist[i] for i in perm]
        if feasible(order, hist, realtime, edges):
            return [op["id"] for op in order]
    return None


def verdict(name, H):
    print(f"-- history {name} --")
    for op in H:
        what = f"w(x={op['val']})" if op["kind"] == "w" \
            else f"r(x)->{op['seen']}"
        print(f"  {op['id']} [{op['client']}] {what} "
              f"t={op['start']}..{op['end']}")
    edges = program_edges(H)
    lin = find_witness(H, True, edges)
    seq = find_witness(H, False, edges)
    show = lambda w: "[" + ", ".join(w) + "]" if w else None
    print(f"  linearizable : {'হ্যাঁ ' + show(lin) if lin else 'না'}")
    print(f"  sequential   : {'হ্যাঁ ' + show(seq) if seq else 'না'}")
    if not lin and seq:
        print(f"  morph: real-time ফেলেই legal {show(seq)}")
    if not seq:
        print("  কোনো permutation-ই value+order মানে না — causal-ও ভাঙা")
    print()


verdict("H2", H2)
verdict("H3", H3)

প্রত্যাশিত output:

-- history H2 --
  w1 [A] w(x=1) t=0..1
  r1 [B] r(x)->0 t=2..3
  r2 [B] r(x)->1 t=4..5
  linearizable : না
  sequential   : হ্যাঁ [r1, w1, r2]
  morph: real-time ফেলেই legal [r1, w1, r2]

-- history H3 --
  w1 [A] w(x=5) t=0..1
  r1 [B] r(x)->5 t=2..3
  r2 [B] r(x)->0 t=4..5
  linearizable : না
  sequential   : না
  কোনো permutation-ই value+order মানে না — causal-ও ভাঙা

H2-র witness-order খেয়াল করুন — read আগে, write পরে; এটাই sequential-এর অনুমোদিত সময়-ভ্রম। H3-তে per-process order (r5 আগে r0) এমন order দাবি করে যেখানে 5 লেখার আগে 0 ফেরত দেওয়া যায় — কোনো permutation পারে না। নিজে বাড়ান: (১) H1 যোগ করুন (w(1) → single fresh read) — তিন verdict-ই হ্যাঁ; (২) n=7-এ runtime মাপুন — factorial growth চোখে; (৩) read-from edges যোগ করে পূর্ণ causal-validator বানান।

বাস্তব সিস্টেমে

Jepsen-শিল্প। Kyle Kingsbury-র distributed-systems analysis-গুলো আসলে আজকের build-এর industrial রূপ — real database-এর ওপর linearizability/consistency probe চালিয়ে violation-documentary; MongoDB, Redis, Elasticsearch সবাই একসময় ধরা খেয়েছে।

Cassandra-র PBS। Bailis-র VLDB paper দেখিয়েছে QUORUM-config-এ staleness-এর probabilistic bound — “eventual মানে অসীম” নয়, measure করা যায়; production-team আজও CL-dial ঘোরায় এই math-এ।

CPU memory model। আপনার laptop-এ x86-TSO আসলে sequential- consistency-র চেয়ে সামান্য দুর্বল চুক্তি — store-buffer-এর জন্য; acquire/release fences দিয়ে শক্ত করা হয়। Ladder-টা distributed- এর আগে silicon-এ জন্মেছিল।

DynamoDB-র dual-mode। একই API-তে ConsistentRead=false (cheap, eventual) আর true (strong) — per-operation model-choice-এর mass-market রূপ; bill-এ পার্থক্যটা সরাসরি দেখা যায়।

যে ভুলগুলো সবাই করে

“Linearizable আর serializable একই কথা।”

Scope আলাদা। Linearizable = single-object, real-time recency; serializable = multi-object transactions যেন কোনো serial ক্রমের সমান — serializable real-time মোটেই মানে না। Isolation-levels লেসনের transaction-জগৎ আর আজকের object- জগৎ — দুই আদালতের আইন আলাদা; কেউ কাউকে imply করে না।

“Sequential consistency মানে প্রায় strong — ব্যবহারিক পার্থক্য নেই।”

পার্থক্যটা ঠিক আজকের H2-history: completed write-এর পরে পুরনো মান — linearizability এটা আজীবন আটকায়, sequential একে অনুমোদনই করে। ZooKeeper-এর read-path দিনে-রাতে এই চুক্তিতেই চলে — এবং বেশিরভাগ config-workload-এর জন্য এটাই সঠিক দাম।

“Eventual consistency মানে milliseconds-এ মিলে যাবে।”

Model-এ কোনো time-bound নেই — শুধু quiescence-শর্ত। Network partition ঝুলে থাকলে divergence minutes-এ যেতে পারে; আর conflict-rule খারাপ হলে convergence-এর পরেও ভুল value-তে মেলে (LWW data-loss)। Bound চাইলে আলাদা করে কিনতে হয় — PBS-ধাঁচে measurement, assumption নয়।

“QUORUM read + QUORUM write = linearizable।”

Overlap fresh-read দেয় — কিন্তু linearizability আরও চায়: total order, recency-across-concurrent-writes। LWW clock-skew, non-sticky session, sloppy-quorum window — সব জায়গাই ফাঁক। এই ভুলটা এত জনপ্রিয় যে পরের লেসনের প্রথম কাজই একে সংখ্যায় ভাঙা।

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

1

Linearizability-র দুই শর্ত এক-বাক্যে করে; আর এটা কোন scope-এ কাজ করে?

স্মরণ

(১) প্রতিটা op-এর ভেতরে একটা linearization point আছে; (২) points-এর ক্রম valid total order এবং real-time সম্মান করে। Scope: এক object; transaction-level নয়।

2

Intro-র history (write complete t=10, read t=20 returns old) — sequential consistency-তে legal হওয়ার ঠিক কোন দরজা দিয়ে ঢুকল?

যুক্তি

Cross-process real-time শর্তটাই দরজা। Order [B:r->0, A:w(1)] — B একা, A একা, per-process ক্রম ভাঙে না; আর “write-এর response read- এর invocation-এর আগে” দেখার কেউ নেই। Sequential শুধু নিজের-হাতের ক্রমের প্রতিশ্রুতি দেয়।

3

Dekker-ধাঁচের ফল — দুই process-ই একে অপরের write শূন্য দেখল — sequential consistency-ও ভাঙে কেন, যেখানে H2-র মতো stale-read ভাঙে না?

যুক্তি

Per-process order-গুলো এবার বিপরীত দিকে টানে: w_x < r_y (A-র ক্রম) আর w_y < r_x (B-র ক্রম), অথচ read-দুটোর মান দাবি করে w_y > r_y, w_x > r_x — মিলিয়ে cycle, কোনো total order-ই সম্ভব নয়। H2-তে কোনো cycle ছিল না, শুধু real-time-শর্ত ছিল।

4

Monotonic-read violation-এ user 12 দেখে 11 দেখল। Sticky routing বসালে কোন মুহূর্তে কী বদলায়, আর কোন anomaly তবু থেকে যায়?

প্রয়োগ

Session-affinity প্রথম request-এর পরেই user-কে এক replica-তে pin করে — দ্বিতীয় read এখন একই snapshot-stream-এ, পিছিয়ে যাওয়া অসম্ভব। থেকে যায়: failover/rebind-মুহূর্তের window, আর cross-session causality (অন্য user-এর ক্রম) — সেটার জন্য session-guarantee যথেষ্ট নয়।

5

Product-এ তিন feature: feature-flag, like-counter, cart-item-list। তিনটার ladder-target আর mechanism — খরচের যুক্তিসহ।

ডিজাইন

Feature-flag: linearizable-leaning (consensus store) — ভুল flag মানে wrong deploy, খরচ justify। Like-counter: eventual + batch-flush — ±কয়েকটা like business-neutral, latency-লাভ full। Cart: session- scoped read-your-writes (sticky) + checkout-moment-এ strong validate। Per-operation targeting-ই সস্তা সঠিকতা।

6

Experiment-এ (2,2)-এ stale% 0 এলো — এটা কি linearizability-প্রমাণ? না হলে কী প্রমাণ, কী বাকি?

যুক্তি

না। Zero-stale প্রমাণ করে read সবসময় latest acked version দেখছে — freshness-under-this-workload। Linearizability আরও চায়: concurrent writes-এর total order, প্রতি op-এ real-time respect। বাকি ফাঁকগুলো পরের লেসনের NWR-limits আলোচনার খাদ্য।

এরপর কী

এরপর কী

চুক্তির ভাষা এবার হাতে — এবার সেই চুক্তি রাখার যন্ত্র। পরের লেসনে replication-এর তিন পরিবার: primary-backup-এর sync/async গভীরে, Dynamo-ধাঁচে leaderless NWR — W+R>N intersection-argument আর তার সীমা — sloppy quorum, hinted handoff, Merkle-tree healing, আর chain replication যেটা দেখায় strong consistency-ও throughput- scale করতে পারে। Misconception #4-এর QUORUM-রহস্যের মোচড়ও এখানেই।

আরও পড়ুন

  • How to Make a Multiprocessor Computer That Correctly Executes Multiprocess Programs — Leslie Lamport — IEEE Transactions on Computers C-28(9), 1979 · Sequential consistency-র জন্মসংজ্ঞা — শব্দগুলো আজও হুবহু উদ্ধৃত হয়
  • Linearizability: A Correctness Condition for Concurrent Objects — Maurice P. Herlihy, Jeannette M. Wing — ACM TOPLAS 12(3), 1990 · Linearizability-র formal ভিত্তি — interval semantics আর এই লেসনের সংজ্ঞার উৎস
  • Probabilistically Bounded Staleness for Practical Partial Quorums — Peter Bailis, Shivaram Venkataraman, Michael J. Franklin, Joseph M. Hellerstein, Ion Stoica — VLDB 2012 · Cassandra-ধাঁচে tunable consistency-এ staleness মাপার quantitative ভাষা
  • Designing Data-Intensive Applications, Chapter 9 — Martin Kleppmann · Ladder-এর practitioner-সংস্করণ — প্রতিটা ধাপের খরচের বাস্তব হিসাব