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

Gossip Protocol ও CRDT — ছড়িয়ে পড়া আর conflict-free গণিত

Gossip Protocols and CRDTs

Leader ছাড়া cluster-এ খবর rumor-এর মতো ছড়ায় — মাত্র O(log N) round-এ; আর CRDT-র merge-algebra ওই এলোমেলো arrival-order-কেও অগ্রাহ্য করে সব replica-কে এক state-এ মিলিয়ে দেয়।

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

  • Push, pull, push-pull round-এর যন্ত্র ব্যাখ্যা করে কেন gossip খবর O(log N) round-এ পুরো cluster-এ পৌঁছায় — probability-sketch হাতে করে — বলতে পারবেন
  • Anti-entropy আর rumor-mongering variant আলাদা করে SWIM membership-র failure-detection চক্র পড়তে পারবেন
  • Strong eventual consistency-র সংজ্ঞা দিয়ে merge-algebra-র তিন শর্ত — commutative, associative, idempotent — ঠিক কোন বাস্তবতা থেকে আসে ব্যাখ্যা করতে পারবেন
  • G-Counter, PN-Counter, LWW-Register, OR-Set-এর merge-semantics হাতে trace করে কোনটা কোন intent রক্ষা করে আর কোথায় data হারায় বলতে পারবেন
  • CAP trade-off-এর AP-side answer হিসেবে gossip + CRDT stack-কে সঠিক জায়গায় বসাতে পারবেন

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

আগে এটা বুঝি

quorum-replication-এ consistency-র দাম (R + W > N), Raft-এ leader আর majority vote — সবখানেই এক assumption: coordination চাই। কিন্তু ৯০০- node Cassandra ring ভাবুন — তিন মহাদেশে ছড়ানো, প্রতি ঘণ্টায় node ওঠানামা। এখানে leader নেই, সবাই peer। দুইটা প্রশ্ন উত্তরহীন: node-১ জানল node-৭ মরেছে — বাকি ৮৯৮জন জানবে কীভাবে? আর দুই replica-তে concurrent write — leader ছাড়া কে মীমাংসা করবে? আজকের লেসন এই দুই প্রশ্নের। প্রথমটার উত্তর gossip protocol — খবর rumor-এর মতো: প্রতিটা node মাঝে মাঝে যাদৃচ্ছিক peer-কে ‘শুনেছিস?’ জিজ্ঞেস করে, আর অবিশ্বাস্যভাবে দ্রুত পুরো cluster জেনে যায়। দ্বিতীয়টার উত্তর CRDT — data-structure এমনভাবে গড়া যায় যে concurrent update মেলানোটাই merge-rule; coordination লাগে না, তবু সব replica এক state-এ পৌঁছায়।

প্রতিশ্রুতি সৎ: gossip-এ খবর অবশেষে পৌঁছায় — staleness-window লুকাব না, মাপব। CRDT-কে magic ভাবতে দেব না — merge-এর algebra খুলে দেখব, কোন structure কোন intent রক্ষা করে, কোনটা চুপচাপ data ফেলে দেয়।

মূল ধারণা

Rumor কীভাবে ছড়ায় — push, pull, push-pull

নিয়ম সরল: প্রতিটা node periodic tick-এ যাদৃচ্ছিক peer বেছে state বিনিময় করে; fanout k মানে প্রতি tick-এ kজন। বিনিময়ের তিন স্বাদ:

Styleকে পাঠায়ভালো কখনদুর্বল কখন
Pushযার খবর আছে সে ঠেলে দেয়শুরুতে — informed কমশেষে — receiver ইতিমধ্যেই জানে
Pullযার খবর নেই সে চেয়ে নেয়শেষে — uninformed কমশুরুতে — কার কাছে চাইব অজানা
Push-pullদুজনে যার-যার নেই নেয়সব ধাপেmessage দ্বিগুণ

Probability-sketch: cluster-এর u ভগ্নাংশ এখনো অজ্ঞ হলে push-round-এ কোনো অজ্ঞ node কারো নজরে না পড়ার সম্ভাবনা —

ur+1≈ur⋅e−k⋅(1−ur)u_{r+1} \approx u_r \cdot e^{-k \cdot (1 - u_r)}

শুরুতে প্রতি round-এ অজ্ঞ-সংখ্যা প্রায় e^-k গুণ কমে — exponential collapse, খবর পৌঁছায় log N-মাপের round-এ; k=1-এও হাজার-node cluster ডজনখানেক round-এ জানে। শেষ ধাপে বদনাম: মাত্র কয়েকজন অজ্ঞ থাকলে random- peer hit দুষ্কর — ওই long tail কাটে pull বা push-pull।

Variant-গুলোর নামকরণ

Variantচালনাকখন থামেব্যবহার
Anti-entropyনিয়মিত digest-compareকখনো না — backgroundReplication repair
Rumor-mongeringউত্তেজনায় ছড়ায়k blind-push-পরে muteHot news spread
SWIM-styleDetection + dissemination একসাথেContinuousMembership

SWIM-এর naming: random peer-কে PING; reply না এলে তৃতীয় কারো মাধ্যমে Ping-Req (indirect probe); fail → SUSPECT ঘোষণা — সন্দেহ-খবরটাই gossip-এ ছড়ায়; refutation না এলে timeout-এ DEAD। Detection-decision আর dissemination-vehicle এক protocol-এ জোড়া।

Staleness window — সৎ স্বীকারোক্তি

Eventual convergence read-guarantee না — write-এর পরে অন্য replica-তে পুরনো মান দেখার window থাকে, মোটামুটি log(N)/log(k) × tick × delay। Bailis-দের PBS paper এটাকে মাপযোগ্য promise বানায় — ‘p% সময় t-সেকেন্ডের বেশি stale না’ জাতীয় claim। মূল কথা: eventual consistency quantitative হতে পারে, ‘কোনোদিন এক হবে’-র দুর্বল শব্দ না।

Strong eventual consistency — আর merge-এর তিন শর্ত

Gossip delivery দেয়, order দেয় না — replica-রা concurrent update ভিন্ন ক্রমে দেখতেই পারে; ad-hoc LWW-ধাঁচের মীমাংসা তখন replica-ভেদে ভিন্ন ফল — divergence। CRDT-র চুক্তি strong eventual consistency (SEC): (১) eventual delivery — যে update এক replica পেয়েছে সবাই অবশেষে পাবে; (২) convergence — যারা একই update-set পেয়েছে তাদের state অবশ্যই এক, order/timing/retry কিছুই ফল বদলাতে পারবে না। State-based CRDT-তে replicas state বিনিময় করে merge করে; convergence-নিশ্চিত করতে merge-এর তিন গুণ লাগে — প্রতিটা শর্ত gossip- বাস্তবতার প্রতিধ্বনি:

শর্তমানেকোন বাস্তবতা থেকে
Commutativea ⊔ b = b ⊔ aখবর যেকোনো order-এ পৌঁছায়
Associative(a ⊔ b) ⊔ c = a ⊔ (b ⊔ c)Relay-তে grouping এলোমেলো
Idempotenta ⊔ a = aDuplicate delivery অনিবার্য

তিনটা মিলে join semilattice: partial order-এ জোড়ার least upper bound-ই ⊔; merge মানে lattice-এ ঊর্ধ্বমুখী হাঁটা — পথ যা-ই হোক, destination এক।

চার concrete CRDT

G-Counter। সরাসরি integer-sum merge idempotence ভাঙে (duplicate-এ দ্বিগুণ); slot-wise max-এ আলাদা node-এর increment overwrite হয়। সমাধান per-node slot — node শুধু নিজের ঘর লেখে, max-ই সঠিক union:

node-A: {A:3, B:2, C:5}   node-B: {A:2, B:4, C:5}
merge = slot-max {A:3, B:4, C:5}   value = sum = 12

PN-Counter: decrement-এর জন্য positive-negative দুই G-Counter-এর জুটি; value = P − N।

LWW-Register। Write বহন করে timestamp; merge রাখে বড়টা, tie-তে node-id — deterministic-ই দরকার, fairness না। সৎ স্বীকারোক্তি: clock-skew জগতে চুপচাপ একটা write ফেলে দিতে পারে — wall-clock-এ আমরা অবিশ্বস্তই দেখেছি।

OR-Set। Add নেয় unique tag; remove শুধু দেখা tag tombstone করে — concurrent add+remove-এ add wins:

R1: add(x,t1)          R2: remove(x) — চেনে শুধু পুরনো tag-set
merge: add-set ∋ t1, removed ∌ t1  →  x বেঁচে যায়

ভেতরে কী ঘটছে

Semilattice-প্রমাণ — convergence কেন অমোঘ

Local update = state এক ধাপ ঊর্ধ্বে (monotonic); merge-ও upper bound-এ নেয়। একই update-set পেলে সবার final state = সেই update-গুলোর join — এক point; order, duplication, delay ফল বদলাতে পারে না। G-Counter- এর lattice slot-vector-দের coordinate-wise order; OR-set-এরটা subset order।

Push-pull crossover

Push শেষে মরে: m informed হলে hit-probability u=(N−m)/N। Pull শুরুতে মরে: খবর-পাওয়ার সম্ভাবনা m/N। Cross roughly m ≈ N/ln N — আগে push, পরে pull সেরা; push-pull দুই দিক একসাথে বলে propagation দ্বিগুণ দ্রুত, tail-ও কাটে। Rumor-mongering-এ deaf-node-gap থেকে যেতে পারে — তাই rumor- এর সাথে anti-entropy চলেই: rumor speed, anti-entropy completeness।

Payload-এর অর্থনীতি — op-based আর delta

Op-based শুধু operation পাঠায় — payload O(op), দাম causal-delivery channel: happens-before order-এ delivery না হলে add-wins-ও উল্টে যেতে পারে। Delta-state middle-path — শেষ sync-এর diff, gossip-sahishnu আর ছোট payload একসাথে। Production-এ মিশ্রণ: hot-path delta, periodic full-state safety-net।

Tombstone GC — চিরকালের ঋণ

Remove মনে রাখতে হয় নইলে resurrection: পুরনো delete-খবর দেরিতে পৌঁছালে পুরনো add আবার বেঁচে যায়। কিন্তু tombstone কখন মুছব — global knowledge ছাড়া কেউ নিশ্চিত না সবাই দেখেছে। শিল্প-উত্তর সব compromise: grace-period (Cassandra gc_grace_seconds), version-watermark, checkpointed compaction। ভুল tune মানে ghost-data — এই লেসনের সবচেয়ে নীরব production-bug।

উদাহরণ

Offline shopping-cart — দুই phone, এক cart

Offline-first app: phone আর tablet, sync মাঝে মাঝে।

t1 phone: add('coffee') {c1}    t2 tablet: add('tea') {t7}
t3 phone: add('milk') {m3}      t4 tablet: remove('coffee') rem{c1}

SYNC merge: adds {c1,t7,m3}, rems {c1}
  tea   — concurrent add, remove তার tag চেনে না → বাঁচে ✓
  milk  — remove-এর সমসাময়িক add → বাঁচে ✓
  coffee— remove tag সরাসরি দেখে মেরেছে → মরে ✓

তিনটা ফলই intuition-মেলা: remove কেবল যা দেখেছিল তা-ই মেরে। এবার একই list LWW-Register-এ? Phone ts=103, tablet ts=104 (clock ২s এগিয়ে) — tablet জেতে, milk হাওয়া; crash নেই, bug নেই, শুধু clock-ফারাক। Data-type choice- ই intent-policy — এই লেসনের দামি এক লাইন। View-count G-Counter-এ? ৩+২ বার দেখা — slot-max ৫ ঠিক; naive sum ১০ ভুল।

ছবিতে

EPIDEMIC (k=3)                    MERGE FUNNEL
R0 ●○○○○○○○  informed=1           writes: inc(A,+2) inc(B,+1)
R1 ●●●●○○○○            =4                 add(x,t9) remove(y,t2)
R2 ●●●●●●●●●●●         =12
R3 ●●●●●●●●●●●●●●●●    =27        R1 ─┐              ┌─ R1'
R4 ●●●●●●●●●●●●●●●●●●●● =28        ├─ gossip-merge ──┤   সবাই:
message-cost ≈ k×Σ(informed)      R2 ─┤  slot-max,      ├── cnt{A:2,B:1}
boom-phase-এই বেশিরভাগ                │  set-union      │  set{adds:{t9},
                                  R3 ─┘                └─  rems:{t2}}
arrival-order যা-ই হোক — converged
Epidemic propagation আর CRDT merge-funnel — বাঁয়ে খবর-ছড়ানোর boom-tail আকৃতি, ডানে তিন replica-র merge একই lattice-point-এ: পথ তিনটা, destination একটা।

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

EXPERIMENT

Gossip speedometer + OR-set truth-table — দুই theory-claim চোখে হাতে

macOS / Linux (Python 3.10+, কোনো dependency নেই)· ২৫ মিনিট
"""gossip_lab.py — epidemic rounds + OR-set concurrency matrix"""
import random


def simulate_gossip(n=200, k=2, trials=30):
    """full-propagation round-count-র distribution"""
    out = []
    for _ in range(trials):
        informed, rounds = {0}, 0
        while len(informed) != n:
            new = set()
            for node in informed:                       # push-phase
                new.update(random.sample(range(n), k))
            for u in range(n):                          # pull-phase
                if (u not in informed and random.random() < 0.5
                        and random.choice(range(n)) in informed):
                    new.add(u)
            informed |= new
            rounds += 1
        out.append(rounds)
    return out


print('=== gossip: rounds-to-full-propagation (30 trials) ===')
for n in (5, 200):
    print(f'-- N={n}')
    for k in (1, 2, 3):
        rs = sorted(simulate_gossip(n=n, k=k))
        print(f'   k={k}: median={rs[len(rs)//2]} '
              f'min={rs[0]} max={rs[-1]}')


class ORSet:
    """add-wins set — tagged adds; remove tombstones seen tags"""

    def __init__(self):
        self.adds = {}          # tag -> element
        self.rems = set()       # tombstoned tags

    def add(self, elem, tag):
        self.adds[tag] = elem

    def remove(self, elem):
        self.rems |= {t for t, e in self.adds.items() if e == elem}

    def elements(self):
        return {e for t, e in self.adds.items() if t not in self.rems}


def merge(a, b):
    m = ORSet()
    m.adds = {**a.adds, **b.adds}
    m.rems = a.rems | b.rems
    return m


print('\n=== OR-set: concurrent add/remove final-state matrix ===')
# case 1: R2 কিছু দেখেনি → add-wins
r1, r2 = ORSet(), ORSet()
r1.add('x', 't1'); r2.remove('x')               # R2-র চেনা tag শূন্য
ok1 = 'x' in merge(r1, r2).elements()
# case 2: R2 তার tag দেখেছিল → observed-remove works
r1b, r2b = ORSet(), ORSet()
r1b.add('x', 't1'); r2b.adds = dict(r1b.adds)   # R2-এর copy
r2b.remove('x')                                 # tombstones t1
ok2 = 'x' not in merge(r1b, r2b).elements()
# case 3: y-এর দুই concurrent add — element once
r1c, r2c = ORSet(), ORSet()
r1c.add('y', 'ta'); r2c.add('y', 'tb')
w3 = merge(r1c, r2c)
ok3 = w3.elements() == {'y'} and len(w3.adds) == 2

for name, expect, ok in [
    ('add(x) || remove(x-unseen)', 'add-wins -> x survives', ok1),
    ('remove after seeing tag', 'observed-remove -> x dies', ok2),
    ('two concurrent adds of y', 'union -> y once (2 tags)', ok3),
]:
    print(f'{name:<28} expect: {expect:<26} got-ok={ok}')
print(f'\nALL-THEORY-MATCHED = {all([ok1, ok2, ok3])}')

নমুনা output (round-সংখ্যা trial-ভেদে ওঠানামা করবে):

=== gossip: rounds-to-full-propagation (30 trials) ===
-- N=5
   k=1: median=3 min=2 max=6
   k=2: median=3 min=2 max=4
   k=3: median=2 min=1 max=3
-- N=200
   k=1: median=10 min=8 max=12
   k=2: median=7 min=6 max=8
   k=3: median=6 min=5 max=7

=== OR-set: concurrent add/remove final-state matrix ===
add(x) || remove(x-unseen)   expect: add-wins -> x survives     got-ok=True
remove after seeing tag      expect: observed-remove -> x dies  got-ok=True
two concurrent adds of y     expect: union -> y once (2 tags)   got-ok=True

ALL-THEORY-MATCHED = True

তিনটা পাঠ:

  • N=200-তে log-scaling স্পষ্ট: k=1 ~১০ round, k=3 ৫–৬; min-max spread-ই probabilistic nature-এর সাক্ষ্য, আর log₂200 ≈ 7.6-এর সাথে k=2-এর ৭–৮ হাতে-হাতে।
  • OR-set-এর তিন case-ই intent-অনুযায়ী — merge বিচার করে না, tag-bookkeeping-ই বিচার করে।
এটা কী প্রমাণ করে

Fanout k=1,2,3-এ full-propagation round-সংখ্যা log-scale-এ কমে, k বাড়লে tail কাটে — O(log N)-sketch-এর live প্রমাণ; আর OR-set-এর প্রতিটা concurrent add/remove interleaving যে final-state দেয় সেটা add-wins theory-র matrix-এর সাথে হুবহু মেলে।

নিজে বানান

BUILD IT

CRDT Suite — চার data-type, merge-algebra, property-fuzz

Python · ●●●●○
  1. GCounter — per-node slot dict, slot-wise max merge, value=sum
  2. PNCounter — positive-negative দুই GCounter-এর জুটি
  3. LWWRegister — timestamp write, merge বড় ts, tie-এ node-id
  4. ORSet — tagged adds + tombstone rems, add-wins merge
  5. Fuzz harness — random schedules, random sync, convergence assert
"""crdts.py — four CRDTs + property-based fuzz (~120 line)"""
import random
from dataclasses import dataclass, field


@dataclass
class GCounter:
    slots: dict = field(default_factory=dict)   # node -> count

    def inc(self, node, by=1):
        self.slots[node] = self.slots.get(node, 0) + by   # নিজের slot-ই

    def value(self):
        return sum(self.slots.values())

    def merge(self, other):
        for node, c in other.slots.items():
            self.slots[node] = max(self.slots.get(node, 0), c)


@dataclass
class PNCounter:
    pos: GCounter = field(default_factory=GCounter)
    neg: GCounter = field(default_factory=GCounter)

    def inc(self, node, by=1):
        (self.pos if by >= 0 else self.neg).inc(node, abs(by))

    def value(self):
        return self.pos.value() - self.neg.value()

    def merge(self, other):
        self.pos.merge(other.pos)
        self.neg.merge(other.neg)


@dataclass
class LWWRegister:
    value=None; ts=0; writer=''
    def set(self, value, ts, writer):   # strict > — equal-ts-এ পুরনো জেতে
        if (ts, writer) > (self.ts, self.writer):
            self.value, self.ts, self.writer = value, ts, writer
    def merge(self, other):
        self.set(other.value, other.ts, other.writer)


@dataclass
class ORSet:
    adds: dict = field(default_factory=dict)    # tag -> elem
    rems: set = field(default_factory=set)      # tombstoned tags

    def add(self, elem, tag):
        self.adds[tag] = elem

    def remove(self, elem):
        self.rems |= {t for t, e in self.adds.items() if e == elem}

    def elements(self):
        return {e for t, e in self.adds.items() if t not in self.rems}

    def merge(self, other):
        merged = dict(self.adds); merged.update(other.adds)
        self.adds = merged
        self.rems |= other.rems


def random_schedule(rng, ops_per_replica=12):
    nodes = ['n1', 'n2', 'n3']
    g = {n: GCounter() for n in nodes}
    pn = {n: PNCounter() for n in nodes}
    lw = {n: LWWRegister() for n in nodes}
    os_ = {n: ORSet() for n in nodes}
    for i in range(ops_per_replica):
        n = rng.choice(nodes)
        g[n].inc(n)
        pn[n].inc(n, rng.choice([1, 1, -1]))
        lw[n].set(f'v{i}', rng.randrange(100), n)
        if rng.random() < 0.6:
            os_[n].add(f'e{rng.randrange(4)}', f'tag{n}-{i}')
        else:
            os_[n].remove(f'e{rng.randrange(4)}')

    # random sync-graph: এলোমেলো pair-merge — order ইচ্ছামতোই থাক
    for _ in range(40):
        a, b = rng.sample(nodes, 2)
        g[a].merge(g[b]);   pn[a].merge(pn[b])
        lw[a].merge(lw[b]); os_[a].merge(os_[b])
        g[b].merge(g[a]);   pn[b].merge(pn[a])
        lw[b].merge(lw[a]); os_[b].merge(os_[a])

    assert len({g[n].value() for n in nodes}) == 1, 'G diverged!'
    assert len({pn[n].value() for n in nodes}) == 1, 'PN diverged!'
    assert len({(lw[n].value, lw[n].ts, lw[n].writer)
                for n in nodes}) == 1, 'LWW diverged!'
    assert len({frozenset(os_[n].elements())
                for n in nodes}) == 1, 'ORSet diverged!'


def intent_cases():                    # documented intent-preservation cases
    a, b = GCounter(), GCounter()      # duplicate delivery harmless?
    a.inc('n1'); a.inc('n1'); b.merge(a); b.merge(a)
    assert b.value() == 2, 'idempotent merge broken'

    p = PNCounter(); p.inc('n1', 5); p.inc('n2', -3)
    assert p.value() == 2, 'PN arithmetic broken'

    r1, r2 = LWWRegister(), LWWRegister()
    r1.set('late', 10, 'n1'); r2.set('early', 5, 'n2')
    r1.merge(r2)
    assert r1.value == 'late', 'LWW ordering broken'

    s1, s2 = ORSet(), ORSet()          # add-wins under concurrency?
    s1.add('x', 't1'); s2.remove('x')
    s1.merge(s2); s2.merge(s1)
    assert 'x' in s1.elements() and 'x' in s2.elements(), 'add-wins broken'
    print('intent cases: PASS (idempotency, PN-arithmetic, LWW-order, add-wins)')


rng = random.Random(42)
for i in range(300):                        # property-based fuzz
    random_schedule(rng)
print('fuzz: 300 random schedules — convergence holds everywhere')
intent_cases()

নমুনা output:

fuzz: 300 random schedules — convergence holds everywhere
intent cases: PASS (idempotency, PN-arithmetic, LWW-order, add-wins)

তিনটা পাঠ:

  • Fuzz specific schedule না, property test করে: যেকোনো interleaving-এ convergence — ৩০০ schedule মানে ৩০০ বিশ্ব, সবখানে একই শেষ। Intent-case আরও কঠিন: ‘মিলেছে’ না, ঠিকটা মিলেছে কি। LWW-র strict >-এ equal-ts- এ পুরনো জেতে — production-এ incarnation-number দিয়ে refine হয়।

নিজে বাড়ান:

  1. MV-Register — concurrent write দুটোই রেখে client-কে resolve করতে দেয়
  2. Delta-mode — last-sync watermark রেখে শুধু delta; bandwidth-সাশ্রয় মাপুন
  3. Fuzz-এ fault-model — half-merged sync drop-পরবর্তী recovery converge করে?

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

বাস্তব জগতে

Cassandra/Dynamo-ঘরানার ring। Membership gossip-এ — coordinator নেই, হাজার-node ring নিজেকে সাজায়; conflict-resolution-এ LWW আর CRDT দুই-ই।

Consul/Serf আর Redis Cluster bus। SWIM-derived gossip-pool-এ service-discovery; Redis-এ node-রা bus-port-এ slot-map আর liveness ছড়ায়।

Riak shopping-cart। সবচেয়ে পরিচিত production OR-set — concurrent cart-edit-এ add-wins customer-পছন্দ বাঁচায়।

Automerge/Yjs/Figma multiplayer। Concurrent document-edit-এর মিলন sequence-CRDT — shared-editor-এর নিচে commutative-merge-ই চলছে।

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

“Gossip-এ খবর ঠিক log N round-এই সবার কাছে পৌঁছাবেই।”

Log N expectation — boundary না; simulation-এর min-max spread দেখুন, rumor-mongering-এ deaf-node-gap-ও আছে। Production-এর জবাব push-pull, fanout-tuning, anti-entropy safety-net। ‘Usually fast’ ≠ ‘always bounded’।

“CRDT মানে conflict-এ কিছুই হারায় না — সব data বাঁচে।”

Guaranteed টা convergence — preservation না: LWW পুরনো write ফেলে, G-Counter decrement জানে না, OR-set remove concurrent add-এ হার মানে। আসল দাবি: merge deterministic + intent-compatible; data-type বাছাই = conflict-policy বাছাই।

“Strong eventual consistency মানে strong consistency।”

SEC শুধু বলে একই update-set-দেখা replica এক state-এ থাকবে; read-freshness ওয়াদা নেই — write-পরবর্তী stale-read স্বাভাবিক (staleness-window!)। Linearizability- এর সাথে পার্থক্য ওই window-টাই; read-after-write-এ session-hint আলাদা লাগে।

“Gossip-traffic negligible — সব state বারবার সবাইকে পাঠাই।”

Message-সংখ্যা মিতব্যয়ী, payload না: full-state exchange O(cluster-state) byte — node/state বড় হলে bandwidth bottleneck; এজন্যই digest-exchange, delta-sync, merkle-tree repair। Traffic-design = count আর payload দুই হিসাব।

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

1

Merge-এর তিন algebraic শর্ত আর প্রতিটা ঠিক কোন gossip-বাস্তবতা থেকে বাধ্য?

স্মরণ

Commutative — খবর যেকোনো order-এ আসে; associative — relay-grouping এলোমেলো; idempotent — retry/relay-এ duplicate অনিবার্য। তিনটা মিলে join-semilattice: ঊর্ধ্বমুখী হাঁটার destination এক।

2

G-Counter plain integer হলে sum-merge আর max-merge ঠিক কী ভাঙে? Slot-vector কীভাবে বাঁচায়?

যুক্তি

Sum-merge: idempotence ভাঙে — duplicate-এ দ্বিগুণ, divergence। Max-merge: algebra ঠিক কিন্তু আলাদা node-এর increment overwrite হয়ে হারায়। Per-node slot-এ node শুধু নিজের ঘর লেখে — max-ই সঠিক union; value = slot-sum।

3

৫০০-node, tick ১s, fanout 2, delay উপেক্ষণীয় — worst-case staleness-window মোটামুটি কত? k=1-এ?

প্রয়োগ

Push-pull-সহ rounds ≈ log₂500 ≈ ৮–৯ → window ≈ ৯ সেকেন্ড। k=1-এ ~১১–১৩ round → ~১২ সেকেন্ড, tail লম্বা। Fanout-ই freshness-latency knob।

4

User item remove করল, পরের sync-এ item ফিরে এলো — কখন বৈধ, কখন bug?

যুক্তি

বৈধ: sync-এর মাঝে অন্য replica নতুন tag-এ re-add করেছে — add-wins সঠিক। Bug কোনো re-add ছাড়া ফিরলে: tombstone আগেই GC হয়েছে বা merge-এ rems-union missing — resurrection: delete-খবরের আগে পুরনো add-খবর জিতে গেছে।

5

Shopping-cart, view-counter, user-status (last-write-wins ঠিক), collaborative checklist — চারটায় কোন CRDT, কেন?

ডিজাইন

Cart → OR-Set: concurrent add বাঁচুক। Counter → G/PN-Counter: arithmetic- merge-ই intent। Status → LWW-Register: latest-state-ই অর্থ। Checklist → OR-Set/sequence-CRDT: item-wise add/remove + ordering। চার জায়গায় চার বাছাই — intent-ই driver।

এরপর কী

এরপর কী

Gossip দিল delivery, CRDT দিল coordination-free মীমাংসা। Production-এ প্রশ্ন এক ধাপ পরে: data কোন node-এ বসবে, request পৌঁছাবে কোথায়, node join/leave-এ কী নড়বে? সেই operational স্তরই পরের লেসন — sharding at scale; আজকের gossip-membership সেখানে node-map-এর vehicle হয়ে ফিরবে।

আরও পড়ুন

  • A Comprehensive Study of Conventional and Universal CRDTs — Martin Kleppmann, Alastair R. Beresford · CRDT design-space-র systematic survey — প্রতিটা structure-এর intent-trade-off-এর মানচিত্র
  • Probabilistically Bounded Staleness for Practical Partial Quorums — Peter Bailis, Shivaram Venkataraman, Michael J. Franklin, Joseph M. Hellerstein, Ion Stoica · Staleness window মাপার কাঠামো — staleness-honesty-র পেছনের research
  • Designing Data-Intensive Applications, Chapter 9 — Consistency, Consensus and the Consensus Problem — Martin Kleppmann · Eventual consistency আর anti-entropy-র ভূমিকা — gossip-কে বড় ছবিতে বসানোর reference