Foundationপ্রথম নীতি থেকে
LEVEL 8লেসন ১৮/১৯কঠিন৪৫ মিনিট

CAP ও PACELC — theorem যেটা সবাই ভুল বোঝে

CAP Theorem and PACELC

Partition হলে Consistency না Availability — বাধ্যত-বাছাই; partition না থাকলে Latency না Consistency — PACELC-ই CAP-এর আসল operational অনুবাদ।

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

  • CAP-এর তিনটা অক্ষরের আনুষ্ঠানিক অর্থ আলাদা করে বলতে পারবেন — C মানে linearizability (single-object recency), A মানে প্রতিটা request-এর উত্তর, আর P মানে partition tolerance — যেটা **বাছাই করা হয় না, ধরা দেওয়া হয়**
  • Partition-মুহূর্তের binary দ্বন্দ্বটা প্রমাণ-ধাঁচে trace করতে পারবেন — ভাঙা network-এ write এক পাশে, read অন্য পাশে: reject করলে A-হানি, serve করলে C-হানি
  • PACELC-এর 'Else' শাখাটা ব্যাখ্যা করতে পারবেন — partition ছাড়া স্বাভাবিক জীবনে trade-off হলো Latency vs Consistency, আর sync-replication কেন CP-দের কর-ফর্ম
  • Eventual consistency-র সংজ্ঞা আর convergence-শর্ত বলতে পারবেন, আর linearizable → sequential → causal → eventual সিঁড়িটা কোথায় কে বসে তা চিনতে পারবেন
  • 'NoSQL = AP, SQL = CP' জাতীয় জনপ্রিয় framing কেন ভুল — PostgreSQL-সহ বাস্তব system-গুলোর আসল position scenario ধরে বিশ্লেষণ করতে পারবেন

আগে এটা বুঝি

প্রতিটা architecture-meeting-এ একবার হলেও ওঠে: “আমরা তিনটার দুইটা বাছব — CAP তো বলেই।” আর ঠিক সেই বাক্যটাই প্রায়ই ভুল। কারণ CAP theorem যেটা বলে, তার সাথে popular সংস্করণের মিল শব্দের স্তরে, অর্থের স্তরে নয়।

আসল theorem (Brewer-এর conjecture 2000, Gilbert–Lynch-এর proof 2002) তিনটা খুব নির্দিষ্ট claim-এর উপর দাঁড়িয়ে:

  • Consistency — clients যা দেখে তা single-copy-র মতো: completed write-এর ঠিক পরেই সব read নতুন মান দেখে
  • Availability — যেকোনো চলমান node-এর কাছে যাওয়া প্রতিটা request উত্তর পায় (error ছাড়া)
  • Partition tolerance — network ভেঙে গেলেও সিস্টেম চলে

আর proof-এর মর্ম: P-কে ছাড়তে পারবেন না — কারণ network ভাঙা physics-এর সত্য, feature নয়। তাই আসল সিদ্ধান্ত ঘটে ঠিক একটা মুহূর্তে: partition চলাকালীন C না A। বাকি সময়? সেখানে theorem নীরব — আর সেই নীরবতায়ই ঢুকে পড়ে আসল প্রতিদিনের trade-off: latency। এই সম্পূরক ফ্রেমের নাম PACELC।

মূল ধারণা

তিন অক্ষরের আনুষ্ঠানিক মানে

অক্ষরFormal মানেযা নয়
CLinearizability — single-object: write-complete-এর পরের প্রতিটা read নতুন মান দেখেtransaction-level serializability নয়, multi-object guarantee নয়
Aজীবিত node-এর প্রতিটা non-failing request যুক্তিসঙ্গত সময়ে উত্তর পায়“কোনো downtime হবে না”-ধরনের marketing-A নয়
PNetwork partition (node-গুলো alive, কিন্তু পরস্পর-অদৃশ্য) সহ্য করা“network ভাঙবে না” — এমন আশা নয়

C-র সংজ্ঞায় single-object শব্দটা খেয়াল করুন — CAP-এর C অনেক সস্তা জিনিস যা মানুষ ভাবে। Bank-account-এর একটা key-র recency guarantee; পুরো database-এর transactional সততা নয়।

Partition-মুহূর্ত — বাইনারি মুহূর্ত

Proof-এর core argument একটা ছোট দৃশ্য:

  1. Replica A আর B আছে; মাঝের network ভাঙল
  2. Client W, A-কে x=1 লিখতে বলল — A জীবিত, উত্তর দিতে পারে
  3. Client R, B-কে x পড়তে বলল — B-ও জীবিত, কিন্তু নতুন মানটা তার কাছে আসেনি

এখন B-র সামনে দুইটা মাত্র পথ:

  • Serve করো পুরনো x=0 → R উত্তর পেল (A ✓), কিন্তু linearizability ভাঙল (C ✗)
  • Reject করো → C রক্ষা (unavailable node-ই মিথ্যা বলতে পারে না — এটা CP-দর্শন), কিন্তু R-এর request-এর উত্তরই নেই (A ✗)

তৃতীয় কোনো পথ proof-এ নেই — এটাই “choose C or A during partition”। আর partition শেষ হলে? সিদ্ধান্ত-মুহূর্তও শেষ — reconciliation-এর নতুন অধ্যায় শুরু (conflict merge — AP- জগতের দৈনিক কাজ)।

PACELC — Else-এর জগৎ

Abadi-র সম্পূরক ফ্রেম: partition-এর বাইরের সময়টুকুও তো আছে —

if Partition      -> A (serve stale)  অথবা  C (block)
else (স্বাভাবিক)  -> L (দ্রুত, async)  অথবা  C (sync-replicate)

Else Latency vs Consistency: স্বাভাবিক অবস্থায় প্রতিটা write- ack অপেক্ষা করবে replica-র fsync/apply-র জন্য (C, কিন্তু ধীর), না সঙ্গে সঙ্গে ফেরবে (L, কিন্তু replica পিছিয়ে)? Replication লেসনের synchronous_commit ladder আসলে এই axis-ই ছিল।

CP-দের কর-ফর্ম: sync-replication মানে প্রতিটা write-এ network round-trip — দূরের replica হলে tens-of-ms। AP-দের লাভ: async ack, sub-ms — দাম partition-time stale-read আর heal-পরব convergence-ঝামেলা।

ভেতরে কী ঘটছে

Eventual consistency — সংজ্ঞা আর শর্ত

AP-জগতের promise-টা সাবধানে পড়ুন: eventual মানে —

  1. নতুন write না-হওয়া পর্যন্ত অপেক্ষা করলে (quiescence), সব replica একই final value-তে পৌঁছাবে (convergence)
  2. সেই পৌঁছানোর সময়সীমা সংজ্ঞায়িত নয় — milliseconds থেকে minutes

Convergence automatic নয় — এর জন্য লাগে conflict-resolution rule: last-write-wins (clock-এর উপর বিশ্বাস!), version-vector, CRDT-গণিত। Partition-চলাকালীন দুই পাশে একই key-তে দুই ভিন্ন write গেলে, heal-এর পরে একটা হেরে যাবে — LWW হলে clock-skew মানেই নতুন write পুরনোর কাছে হার মানতে পারে। “Eventually consistent” শব্দযুগল কোনো সমস্যা-মুক্তি-চিঠি নয়; এটা একটা contract যার ছোট-ছোট হরফে লেখা শর্ত আছে।

Consistency-র সিঁড়ি — Level 9-এর preview

শক্তিশালী ◄──────────────────────────────────────► দুর্বল

linearizable > sequential > causal > eventual

linearizable: single-object real-time recency (CAP-এর C)
sequential:   প্রতিটা process নিজের ক্রম দেখে, cross-process
              real-time ordering নেই
causal:       cause-effect সম্পর্কিত operation-দের ক্রম সবাই
              এক দেখে; unrelated-দের ব্যাপারে মতভেদ ok
eventual:     quiescence-এ মিল — পথে কোনো ক্রমের নিশ্চয়তা নেই

প্রতিটা ধাপ-নাম্বার নামানো মানে replication-এর উপর কম তালা — আরও latency-লাভ, আরও reasoning-ভার application-code-এর কাঁধে। এই সিঁড়ির পূর্ণ গাণিতিক বিবরণ distributed-systems module-এর মূল খাদ্য — এখানে map-টুকু মুখস্থ করুন।

PostgreSQL-এর আসল position

“PostgreSQL CP না AP?” — প্রশ্নটাই খারাপভাবে গঠিত। Single-primary PG: সব write এক node-এ → ওই node-এর reads linearizable; primary মরলে fencing-ছাড়া promote করা হয় না (replication লেসনের rule!) — অর্থাৎ availability ত্যাগ করে C ধরে রাখা হয়: failover-boundary- তে CP-আচরণ। কিন্তু standby-তে hot-standby read = async snapshot — eventual! একই সিস্টেমে দুই মোড, endpoint-ভেদে। CAP label মানুষের জন্য, মেশিনের জন্য নয় — আর বাস্তব সিস্টেম label ছাড়াই প্রতিটা operation-এ সিদ্ধান্ত নেয়।

উদাহরণ

একটা partition, তিনটা design, তিনটা ভিন্ন দিন

দৃশ্য: দুই datacenter (A: Dublin, B: Mumbai), inter-link ভাঙল ৪৫ মিনিটের জন্য। Application: shopping-cart (key=user)।

Design ১ — CP (etcd-style consensus, sync commit):

Mumbai-র users: cart-write প্রতিটা timeout/error
  -> availability শূন্য, কিন্তু কোনো user-এর cart কখনো
     পুরনো দেখায়নি; link ফিরল সব স্বাভাবিক
RPO/RTO মানসিকতা: data-loss অসম্ভব, revenue-loss ৪৫ মিনিট

Design ২ — AP (Dynamo-style, LWW):

Mumbai চলছে পূর্ণ speed — cart read/write সব local
Dublin-এ একই user একসাথে edit করল (traveler scenario)
  -> দুই শাখায় ভিন্ন cart-content; heal-এ LWW merge —
     Dublin-এর শেষ write জিতল, Mumbai-এর item-drop হারাল
     (user-এর চোখে: ঝুড়ি থেকে জিনিস নিখোঁশ!)
Availability ১০০%, correctness-এর বিল ব্যবহারকারীই দিল

Design ৩ — hybrid (PG primary + async standby, app-aware):

Writes: Mumbai traffic Mumbai-local cache-এ, queue-এ async
  replay Dublin-এ (eventual, window ≈ seconds)
Reads: cart-view Mumbai-cache থেকে (stale-ok UI);
  checkout-moment-এ force-sync validation primary-তে
Partition-এ: browsing চলল, checkout ব্লকড (queue + retry)
  -> ক্ষতি সীমাবদ্ধ, সুদৃঢ় point শুধু টাকা-তোলার মুহূর্তে

কোনটা সঠিক? প্রশ্নটা ভুল। সঠিক প্রশ্ন: কোন operation-এ কোন guarantee লাগে — আর Design ৩-ই সেই প্রশ্নের আধুনিক উত্তর। Per-operation consistency-choice-ই আজকের production-দর্শন।

ছবিতে

সিদ্ধান্ত-মানচিত্র

                      request এলো
                          │
              ┌─── network partition? ───┐
              │ YES                      │ NO
              ▼                          ▼
      ┌─── বাছাই ───┐           ┌─── বাছাই ───┐
      │             │           │             │
      ▼             ▼           ▼             ▼
   SERVE         REJECT      ASYNC ack     SYNC ack
   (stale)      (block)      = Latency    = Consistency
      │             │        সস্তা,replica  প্রতি write-এ
      ▼             ▼        পিছিয়ে থাকতে  replica-apply
   A রক্ষা       C রক্ষা      পারে          round-trip
   C হানি       A হানি         │             │
                               ▼             ▼
                         AP-systems     CP-systems
                         (Dynamo,       (ZooKeeper,
                          Cassandra)     etcd, Spanner*)
                                     (*TrueTime-nuance)

heal-এর পরে AP-দের বাড়তি কাজ: convergence — conflict-resolution
(LWW/version-vector/CRDT) ছাড়া দুই শাখা মেলে না
Partition-মুহূর্তের বাইনারি fork — আর Else-জগতের latency-fork; PACELC-র দুই শাখা এক ছবিতে।
guarantee ▲
strong    │  linearizable   <- CAP-এর C; single-object recency
          │        │        (bank balance, lock, config-flag)
          │  sequential     <- per-process order ঠিক; total
          │        │         real-time order নেই
          │  causal         <- কারণ-প্রভাব chain সবাই এক দেখে
          │        │         (reply-before-post অসম্ভব হবে)
weak      │  eventual       <- শুধু quiescent-convergence
          │                   (like-count, view-count)
          └────────────────────────────────► latency-cost
Consistency-সিঁড়ি — উপরে উঠলে বেশি guarantee, কম latency-budget; প্রতিটা ধাপ নামলে উল্টো।

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

EXPERIMENT

নিজের laptop-এ partition বানান — stale বনাম block চোখে

macOS / Linux / Windows (Python 3)· ১০ মিনিট

ছোট script — cap_probe.py; দুই replica, এক shared-wire, দুই mode:

import time

PARTITION = False          # এই switch-টাই network-cable
SYNC = False               # False=AP-ish async, True=CP-ish sync

class Node:
    def __init__(self, name):
        self.name = name
        self.kv = {}

a, b = Node("A"), Node("B")
wire = []                  # (deliver_after_tick, key, val)
tick = 0

def pump(tick):
    global wire
    due = [w for w in wire if w[0] <= tick]
    wire = [w for w in wire if w[0] > tick]
    for _, k, v in due:
        b.kv[k] = v                       # standby-তে apply

# --- write path ---
t0 = time.perf_counter()
a.kv["balance"] = 500                     # local accept
if not PARTITION:
    wire.append((tick + 2, "balance", 500))   # propagation delay
elif SYNC:
    print(f"[tick {tick}] WRITE BLOCKED — replica unreachable, "
          f"C-রক্ষা: ack অস্বীকার")
else:
    print(f"[tick {tick}] WRITE ACKED locally — replica "
          f"এখনো অন্ধ (AP)")

for tick in range(1, 6):
    pump(tick)
    if "balance" in b.kv:
        break

print(f"[tick {tick}] B-এর চোখে balance = "
      f"{b.kv.get('balance', '(আগের মান!)')}")

stale_ticks = tick                        # B কত-tick অন্ধ ছিল
print(f"\nobserved: stale-window={stale_ticks} ticks, "
      f"wall-clock={(time.perf_counter()-t0)*1000:.1f}ms")

খেলা ১ — AP-mode (default): চালান — write সঙ্গে-সঙ্গে acked, B কয়েক tick পুরনো মান দেখে, তারপর converge।

খেলা ২ — CP-mode: SYNC = True, PARTITION = True করুন — write blocked; B কখনো নতুন মান পায় না; কিন্তু ভুল মানও কেউ দেখেনি।

খেলা ৩ — heal: PARTITION = True রেখে ২০ iteration চালান, তারপর False — held writes propagate, convergence burst দেখুন (build-section-এর LogShipper-এর আত্মীয়!)।

প্রতিটা খেলার পরে নিজেকে জিজ্ঞেস করুন: এই মুহূর্তে এক bank, এক like-counter, এক shopping-cart — কোনটার জন্য কোন মোড গ্রহণযোগ্য?

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

Partition-মুহূর্তের দ্বন্দ্বটা theory নয় — দুই in-process replica-এর মাঝের wire বন্ধ করলেই CP-mode write আটকায়, AP-mode read পুরনো মান দেয়; আর sync/async-এর latency-gap সংখ্যায় বসে।

নিজে বানান

BUILD IT

MiniCAPLab — PACELC-দাবির quantitative matrix

Python · ●●○○○
  1. Two-node store — configurable W (কত replica-apply লেগে ack)
  2. Partition switch — propagation বন্ধ
  3. Client-probe — availability, staleness, latency গুনুন
  4. ৪টা মোডে (sync/async × partition on/off) workload চালান
  5. Matrix ছাপুন — CAP/PACELC দাবিগুলো সংখ্যায় মিলুন

লক্ষ্য: “CP partition-এ unavailable”, “AP stale দেয়”, “sync ধীর” — তিনটা textbook-বাক্যই এক matrix-row-এ সংখ্যা হয়ে ফুটবে।

import statistics

class Lab:
    """Two-node KV; W=1 local-ack-only, W=2 replica-apply-পরে-ack"""

    def __init__(self, sync_w2=False, net_delay=2):
        self.w2 = sync_w2
        self.delay = net_delay
        self.down = False
        self.inflight = []            # (arrive_tick, seq) — wire
        self.retained = []            # partition-চলাকালীন জমা WAL
        self.seq = 0                  # primary-র write-counter
        self.acked = 0                # client-confirm'd সর্বশেষ seq
        self.applied = 0              # B-তে apply'd সর্বশেষ seq
        self.m = {"ok": 0, "blocked": 0}
        self.lat = []
        self.max_stale = 0
        self.probes = 0

    def write(self, tick):
        if self.down:
            if self.w2:               # CP: replica-ছাড়া confirm নয়
                self.m["blocked"] += 1
                self.lat.append(20)   # timeout-abort cost
                return
            self.seq += 1             # AP: local-ই যথেষ্ট; log জমা থাকে
            self.retained.append((tick + self.delay, self.seq))
            self.acked = self.seq     # local ack — এটাই A-র দাম
            self.m["ok"] += 1
            self.lat.append(1)
            return
        self.seq += 1
        self.inflight.append((tick + self.delay, self.seq))
        if self.w2:                   # CP: apply-পর্যন্ত ack ঝুলিয়ে
            self.lat.append(1 + self.delay)
        else:                         # AP: সঙ্গে-সঙ্গে ack (Else-L)
            self.acked = self.seq
            self.lat.append(1)
        self.m["ok"] += 1

    def advance(self, tick):
        if self.down:
            return                    # partition: delivery বন্ধ
        due = [w for w in self.inflight if w[0] <= tick]
        self.inflight = [w for w in self.inflight if w[0] > tick]
        for _, s in sorted(due):
            self.applied = s
            if self.w2:
                self.acked = s        # sync-ack এখানেই ফিরে

    def probe(self):
        """standby-B-র চোখে staleness — version-gap-এ"""
        self.probes += 1
        gap = self.acked - min(self.applied, self.acked)
        self.max_stale = max(self.max_stale, gap)


def run(sync_w2, partitioned, n_writes=200):
    lab = Lab(sync_w2)
    heal_at = None
    for t in range(1, n_writes + 1):
        if partitioned and t == 121:
            lab.down = False          # cable ফিরল — retained flush
            lab.inflight.extend(lab.retained)
            lab.retained.clear()
        lab.down = lab.down or (partitioned and t <= 120)
        lab.write(t)
        lab.advance(t)
        if t % 5 == 0:
            lab.probe()
    return lab


print(f"{'mode':<24}{'avail%':>8}{'avg-lat':>9}"
      f"{'max-stale':>10}")
for name, s, p in [("AP (W=1) normal", False, False),
                   ("AP (W=1) partition", False, True),
                   ("CP (W=2) normal", True, False),
                   ("CP (W=2) partition", True, True)]:
    lab = run(s, p)
    total = lab.m["ok"] + lab.m["blocked"]
    avail = 100 * lab.m["ok"] / total
    avg = statistics.mean(lab.lat)
    print(f"{name:<24}{avail:>7.0f}%{avg:>9.1f}"
          f"{lab.max_stale:>10.0f}")

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

mode                       avail%  avg-lat  max-stale
AP (W=1) normal              100%      1.0          2
AP (W=1) partition           100%      1.0        ~115
CP (W=2) normal              100%      3.0          0
CP (W=2) partition             0%     20.0          0

Matrix পড়ুন — চার row-ই একটা করে theorem:

  • AP-partition: available ১০০%, কিন্তু staleness partition- জুড়ে জমে ~১১৫ version — এই gap-টাই async replication-এর RPO-জানালা; heal-এর পরে retained-log burst-apply-তে convergence
  • AP-normal: max-stale = net_delay — partition না থাকলেও async-এর ছোট, স্থির lag (PACELC-র Else-L-লাভের উৎস)
  • CP-normal: staleness শূন্য, কিন্তু avg-latency ৩× — PACELC-র Else-C-র কর!
  • CP-partition: availability শূন্য — “during partition, choose” — এই row-ই CAP-এর হৃদয়বাক্য

নিজে বাড়ান:

  1. Read-your-writes session-affinity mode — client সবসময় leader থেকে পড়লে stale% কোথায় যায়?
  2. Quorum-W=1,R=2 বনাম W=2,R=2 — partition-এর কোন পাশ serve করবে হিসাব করুন (Level 9-এর quorum-গণিতের আভাস)
  3. LWW-conflict inject — partition-চলাকালীন দুই পাশে ভিন্ন key- value, heal-এ কে জেতে দেখুন (clock-ভিত্তিক LWW-র বিপদ)

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

বাস্তব জগতে CAP-এর ছায়া

Theorem-টার জন্মকথা। Eric Brewer 2000-এ keynote-conjecture দিলেন, Gilbert ও Lynch 2002-এ formal proof দিলেন, আর 2012-তে Brewer নিজেই IEEE Computer-এ লিখলেন theorem-টা কীভাবে সবাই ভুল quote করছে — creator-এর নিজের correction পড়া বিরল সৌভাগ্য।

CP-shrine। ZooKeeper, etcd, Consul — consensus-core (Raft/ ZAB); configuration, locking, service-discovery — যেখানে ভুল মানে বিপদ, সেখানেই এদের ঠিকানা। Partition-এ minority-side unavailable — সচেতনভাবে।

AP-shrine। Cassandra, DynamoDB, Riak — shopping-cart, session, telemetry, feed; write-availability business-critical যেখানে, conflict-rule সাথে কিনে নিয়ে। Dynamo-paper (2007)-এর LWW + vector-clock আলোচনা এখনো পড়ার মতো।

Nuance-শিকারিরা। Google Spanner: technically CP, তবু low- latency — GPS+atomic-clock (TrueTime) দিয়ে sync-এর wait ছোট bound-এ বাঁধে; প্রমাণ করে “CP মানে ধীর” formula ভাঙা যায় hardware দিয়ে। আর CRDT-based systems (Redis Enterprise CRDB, Automerge) AP হয়েও conflict-free convergence গ্যারান্টি দেয় — math দিয়ে।

আপনার প্রতিদিন। DNS (eventual, TTL-cache), Git (offline-commit, later-merge — AP-darshan), airline-reservation (overbooking = availability-র দাম আগে-ই-গোনা) — partition-thinking শুধু server-room- এর নয়।

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

“CA system বানানো যায় — 'three pick two'-তে CA একটা option।”

Distributed context-এ নয়। P-optional-ভাবা-ই ভুল: network partition আপনার সিদ্ধান্তে আসে না। “CA”-label পাওয়া প্রতিটা system আসলে single-node (বা data-plane-network-হীন)। Brewer নিজেই 2012-তে এই reading-কে invalid বলেছেন — partition-tolerance বাছাইযোগ্য feature নয়, environment-এর শর্ত।

“NoSQL মানে AP, SQL মানে CP — database-শ্রেণি-ই consistency ঠিক করে।”

Category-ভিত্তিক mapping বাজার-সাহিত্য, theorem নয়। MongoDB (default config) CP-leaning, tunable; Cassandra per-keyspace consistency-level-এ CP-to-AP dial করা যায়; PostgreSQL single- primary linearizable-per-object, কিন্তু standby-read eventual। আসল unit-of-analysis: operation, শ্রেণি নয় — প্রতিটা read/ write কোন guarantee-তে চলবে সেটা config/session/code-এ ঠিক হয়।

“Eventual consistency মানে শেষমেশ সব ঠিক হয়ে যাবে — চিন্তা নেই।”

Convergence-এর জন্য লাগে (১) quiescence — নতুন write বন্ধ, (২) একটা sound merge-rule। Partition-চলাকালীন conflicting writes-এ LWW নীরবে data ফেলে দিতে পারে — “শেষ” মানে শেষ-লেখা নয়, শেষ- জেতা। Cart-item হারানো, duplicate-charge, resurrected-delete — production-এর ক্লাসিক AP-ঘাত। Eventual মানে contract-এর নাম, amnesty নয়।

“CAP-এর C আর ACID-এর C একই — আর "linearizable = serializable"।”

ACID-এর C = constraint-consistency (invariant ভাঙে না); CAP-এর C = linearizability — ভিন্ন universe। আর linearizable (single-object real-time recency) ≠ serializable (multi-object transaction-equivalence) — একটা object-scoped, অন্যটা transaction-scoped; কোনোটা অন্যটাকে imply করে না। Interview-classic confusion — সংজ্ঞা আলাদা করে মুখস্থ।

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

1

CAP-এর C-র formal নাম আর এক-বাক্য সংজ্ঞা; এটা কোন scope-এ কাজ করে?

স্মরণ

Linearizability: কোনো write complete হওয়ার পরে শুরু হওয়া প্রতিটা read সেই নতুন মান (বা তার পরের কোনো মান) দেখবে — single-object, real-time recency। Scope: এক object; transaction- level নয়।

2

“P tolerance-টা তো ভালো জিনিস — সবাই বেছে নেবে, তাহলে দ্বন্দ্ব কোথায়?” — এই প্রশ্নের ভুলটা ধরুন।

যুক্তি

P-কে feature ভাবা-ই ভুল। P মানে “network ভাঙলেও সিস্টেম দাঁড়িয়ে থাকুক” — আর ভাঙা network-কে বাছাইয়ের টেবিলে বসানোর সুযোগ নেই; switch/fiber/shark যখন-তখন ঠিক করে দেবে। তাই P হলো premise, choice নয়; choice ঘটে ভাঙা-মুহূর্তে C-vs-A — এখানেই দ্বন্দ্ব।

3

Build-এর matrix-এ CP-normal-এর avg-latency 3, AP-র 1। এই 2-tick ব্যবধান ঠিক কী খরচের হিসাব? Real-world এই ব্যবধান কখন নগণ্য, কখন অসহ্য?

প্রয়োগ

ব্যবধান = replica-apply-পর্যন্ত অপেক্ষার net-delay (delay=2) — sync-replication-এর round-trip। নগণ্য: co-located replicas (sub-ms) — write-rate-এর তুলনায় নীরব। অসহ্য: geo-distributed replica (50-150ms প্রতি write) — interactive-path-এ user-চোখে lag, throughput-ceiling। তাই geo-sync systems হয় region-local leader (Spanner-model), নয় async + reconciliation।

4

Heal-এর পরে AP-cluster-এ convergence automatic-ভাবে “ঠিক” হয় না — ঠিক কোন দুই component-এর অনুপস্থিতিতে ভুল merge হতে পারে?

যুক্তি

(১) Sound conflict-rule — LWW clock-skew-এ পুরনো write জিততে পারে; version-vector/CRDT ছাড়া causality-র হিসাবই থাকে না। (২) Quiescence — merge চলাকালীন নতুন conflicting write এলে rule-টা আবার খেলে। Rule-হীন convergence মানে silent data-loss — “eventual” শব্দটা merge-semantics লুকিয়ে রাখে।

5

Product-requirement: (a) username-registration — duplicate অসম্ভব হতে হবে; (b) profile-view-count — ±৫% ভুল ok; (c) password-change — পুরনো password পরের ৬০ সেকেন্ড কাজ করলে security-incident। তিনটার জন্য consistency-target আর mechanism propose করুন।

ডিজাইন

(a) Strong/linearizable — unique-index সহ single-authority store (consensus-backed); partition-এ registration-block acceptable। (b) Eventual — async counter, batch-flush, sampling-ও চলে; stale window seconds-scale harmless। (c) Causal-at-minimum, practically read-your-writes + short-TTL-invalidation — পরিবর্তনের পরে সব auth-path 60s-এর মধ্যে new-hash দেখতে বাধ্য (session-token revocation push)। মূল সূত্র: requirement-ভেদে per-operation target — এক system-এ তিন মোডই স্বাভাবিক।

এরপর কী

এরপর কী

CAP/PACELC lens এখন হাতে — এবার সেই lens দিয়ে দেখা যাক যে movement-টা “SQL-বিরোধী বিপ্লব” বলে market হয়েছিল: NoSQL — document, key-value, wide-column, graph চার পরিবার; কে কী দেয়, কোথায় ভাঙে, আর JSONB-যুগের PostgreSQL কীভাবে সীমানাগুলো ঝাপসা করে তুলল। এটাই databases module-এর শেষ লেসন — শেষে পুরো module wrap-up আর Level 9 distributed-systems-এর দরজা।

আরও পড়ুন

  • Brewer's Conjecture and the Feasibility of Consistent, Available, Partition-Tolerant Web Services — Seth Gilbert, Nancy Lynch — ACM SIGACT News 33(2), 2002 · CAP-এর formal proof — theorem-টার আসল গাণিতিক উৎস
  • CAP Twelve Years Later: How the Rules Have Changed — Eric Brewer — IEEE Computer, 2012 · Theorem-টার স্রষ্টার নিজের hindsight — misinterpretation-গুলোর সরাসরি জবাব
  • DBMS Musings — Problems with CAP, and Yahoo's little known NoSQL system (PACELC) — Daniel J. Abadi · PACELC-র মূল formulation — Else-Latency শাখার জন্মদাতা
  • Designing Data-Intensive Applications, Chapter 9 — Consistency, Consensus — Martin Kleppmann · Linearizability আর CAP-এর সূক্ষ্ম সম্পর্কের সেরা practitioner আলোচনা