Happens-before ও Causality — কার আগে কার পরে
Happens-Before and Causality
Global clock ছাড়াও cause-effect মাপা যায় — happens-before-এর তিন rule, concurrency-র আসল অর্থ, আর reply-before-parent-জাতীয় user-visible ভুল ঠেকানোর সস্তা কৌশল।
আগে এটা বুঝি
গত লেসনে logical clock বানিয়েছিলাম — Lamport আর vector clock।
মূল ফলাফল দুইটা: Lamport clock একমুখী promise দেয় (a → b হলে
timestamp ছোট, উল্টোটা জানা যায় না); vector clock উল্টোটাও
জানায় — timestamp তুলনায় cause-সম্পর্ক ধরা যায়।
কিন্তু clock শুধু detect করে। আজকের প্রশ্ন: সেই detection দিয়ে কী বানাব? উত্তরটা production-এর প্রতিদিনে। একটা thread খুলুন —
Q: PostgreSQL-এ autovacuum tuning কীভাবে করে?
└─ A: scale_factor 0.01 রাখো, monitor চালাও ← উত্তর!আর কখনো replication-এর জন্য উল্টোটা: উত্তরটা আগে, প্রশ্নটা পরে — বা প্রশ্নটাই ক’মিনিট নিখোঁশ। User screenshot নিয়ে ticket খোলে: “সাইট ভবিষ্যদ্বাণী করছে।” Magic না — causality violation: কারণ-প্রভাবের ধার replica-জগতে হারিয়েছে।
আজ দুইটা কাজ। এক: happens-before relation-টা আনুষ্ঠানিকভাবে দাঁড় করানো — কোন শর্তে দুই event-এর মধ্যে “আগে” বলা যায়। দুই: সেই সংজ্ঞা কাজে লাগিয়ে সস্তা কৌশলে user-visible ক্ষতি ঠেকানো, আর শেষে একটা delivery engine — উল্টো-ক্রমে জিনিস এলে সেটা আটকে রাখে, যাতে কারণের আগে প্রভাব ছাড়া অসম্ভব হয়।
মূল ধারণা
Relation-টা দাঁড় করাই — তিনটা rule
World model গত লেসন থেকেই: ক’টা process, প্রত্যেকটার ভেতরে ঘটছে event — local computation, message send, message receive। Shared clock নেই, shared memory নেই। এই জগতে “আগে”র একমাত্র বৈধ সংজ্ঞা তিনটা rule থেকে আসে:
| Rule | নাম | বলে |
|---|---|---|
| ১ | Program order | একই process-এর ভেতরে a কোডে b-র আগে হলে a → b |
| ২ | Message transit | a যদি m-এর send আর b সেই m-এর receive হয়, a → b |
| ৩ | Transitivity | a → b আর b → c হলে a → c |
Formal ভাষায়: happens-before relation হলো program-order edge আর
message-edge-এর union-এর <Term id="transitive-closure">transitive closure</Term> — (po ∪ msg)-এর closure। এই এক লাইনই Lamport-এর
১৯৭৮ paper-এর হৃদয়।
Relation-টা irreflexive, transitive, asymmetric
(a → b হলে b → a অসম্ভব) — strict
partial order: কিছু pair-এর ক্রম
নির্ধারিত, অনেক pair অনির্ধারিতই থাকে। Total order-এর মতো
সবাইকে সারি করে না — এটাই এর সৌন্দর্য আবার খরচ।
Concurrency — neither-before, simultaneous নয়
Partial order-এ অনির্ধারিত pair-দেরই নাম concurrent:
a ∥ b ⟺ ¬(a → b) এবং ¬(b → a)এবার সবচেয়ে বেশি ভুল হওয়া শব্দটা ঠিক করি: concurrent মানে একই সময়ে ঘটা নয়। মানে শুধু — message-path (সরাসরি বা পরোক্ষ) নেই। Laptop boot আর মঙ্গলগ্রহের rover-ঘোরা — wall- clock-এ বছরখানেকের ফারাক — তবু concurrent, খবর কারো কাছে পৌঁছায়নি। উল্টোটাও সত্য: একই সেকেন্ডের দুই event causal-ভাবে ordered হতে পারে — মাঝে message থাকলে।
Detection tool — vector clock এক লাইনে
গত লেসনের যন্ত্রটা এখানে ঢুকে পড়ে:
a → b ⟺ V(a) <= V(b) componentwise এবং V(a) != V(b)
a ∥ b ⟺ কোনো দিকেই comparable নয়ঘড়ি-তুলনাই relation-এর decision procedure। আজ এটা দুই কাজে: display-order ঠিক করা (experiment), delivery আটকে-রাখা (build)।
Knowledge আর cut — informal ফ্রেম
Process p-র knowledge = যে সব event তার causal past-এ
পৌঁছেছে (নিজেরগুলো + received-message-দের পূর্বপুরুষ)। Happens-
before-এ downward-closed event-সেটকে বলে consistent cut;
snapshot algorithm-গুলো এটাই ধরার চেষ্টা। আমাদের জন্য একটাই নিয়ম
যথেষ্ট — holdback principle: যার causal past এখনো পৌঁছায়নি,
সেটা দেখাও না। বাকি লেসনটা এই এক বাক্যের বাস্তবায়ন।
ভেতরে কী ঘটছে
Violation #1 — reply-before-parent, জন্মে কীভাবে
Setup: parent-comment গেল partition P1-এ, reply গেল partition P2 (thread-key routing ভাঙল, বা multi-region write); display-service দুই partition-ই থেকে async pull করে:
t1 alice -> P1 : parent (version 41)
t2 bob -> P2 : reply (version 87; bob-এর client parent-এর
vector দেখেছিল)
t3 P2 -> display : reply পৌঁছাল (দ্রুত link!)
t4 P1 -> display : parent *এখনো* পথে
t5 user refresh -> [reply] একা ভেসে আছেখেয়াল করুন: clock কেউ মিথ্যা বলেনি, packet হারায়নি — শুধু কেউ causal chain-টা বহন করেনি। Bob-এর client-এর কাছে parent-এর জ্ঞান ছিল; সেটা reply-র সাথে metadata হয়ে travel করেনি।
Violation #2 — replica-switch-এ read-your-writes হারানো
t1 profile-edit -> region-A primary (ack)
t2 পরের request <Term id="load-balancer">load balancer</Term>
region-B lagging replica-তে পাঠাল
t3 B-তে edit এখনো replay হয়নি
t4 user: আমার পরিবর্তন *উধাও*User নিজের কাজের causal past-এ দাঁড়িয়ে, server past অস্বীকার করল। খারাপ সংস্করণ: password-change lagging replica-তে দেরিতে পৌঁছালে পুরনো password-ই জীবিত — security incident।
সস্তা ট্র্যাকিং — দুইটা production-কৌশল
পূর্ণাঙ্গ causal broadcast ব্যবহার না করে বাস্তব system-গুলো সস্তা উপায়ে শুধু user-visible অংশটুকু রক্ষা করে:
কৌশল A — session causality (per-client version vector): client নিজের session-এর vector context ধরে রাখে, প্রতিটা request-এর সাথে পাঠায়; server data-তে context stamp করে, আর read- করা replica check করে — requester-এর context আমার কাছে আছে তো? না থাকলে wait/redirect। COPS-paper এটাকেই scalable করেছিল।
কৌশল B — causal-key partitioning trick: যে operation-গুলো
একে অপরের cause হতে পারে সবগুলো এক key-তে bind — thread-এর
সবকিছু thread_id-এ এক partition-এ। Single-partition FIFO নিজেই
causal order দেয়; vector-এর দরকারই নেই। খরচ: hot-key skew, আর
thread-এর বাইরের causality ধরা যায় না।
| Session causality | Causal-key partition | |
|---|---|---|
| Mechanism | per-client vector + server check | same-key single ordering |
| Cross-entity causality | ধরা যায় | ধরা যায় না |
| Hot-spot risk | নেই | আছে |
| Client complexity | মাঝারি | প্রায় শূন্য |
- event arrivessender-id + vector-clock payload সহ
- merge knowledgedisplay-node-র known-vector max-update করে
- predecessor checkevent-এর vector known-এর সব জায়গায় covered?
- deliver / holdbackcovered হলে render, না হলে holdback queue-তে অপেক্ষা
- queue re-scanপ্রতিটা নতুন delivery-র পরে আটকে-থাকাদের আবার বিচার
উদাহরণ
Crafted log — একই ডেটা, দুই renderer, দুই সত্য
তিন actor-এর interleaved log; arrival order নেটওয়ার্কের মনমতো
উল্টো। bob-এর vector-এ alice: 1 — সে parent দেখে reply লিখেছিল:
| arrival | actor | lamport | vector | text |
|---|---|---|---|---|
| 1 | carol | 1 | {carol: 1} | আমার ফ্যান গরম করছে |
| 2 | bob | 2 | {bob: 1, alice: 1} | reply — version-stamp ব্যবহার করো |
| 3 | alice | 4 | {alice: 1} | parent — cache invalidate কীভাবে? |
Renderer ১ — naive lamport-sort: ছোট timestamp আগে → carol, bob(reply), alice(parent)। Reply প্রশ্নের আগে দেখায় — inversion reproduced: lamport number ক্রস-process তুলনায় অর্থহীন, bob-এর counter ভাগ্যক্রমে ছোট ছিল, এটুকুই।
Renderer ২ — vector-aware topological sort: প্রতিটা pair-এ
vector-compare → edge — alice → bob পাওয়া যায়
({alice:1} <= {alice:1, bob:1}); carol কারো সাথে comparable নয়।
topological sort চালালে
carol, parent, reply — edge যেদিকে, display সেদিকে, arrival
যা-ই হোক। মোড়ের কথা: carol-এর post order-হীন — সে concurrent,
যেকোনো position legal; causality-র দাবি শুধু chain-এর ভেতরে।
ছবিতে
Space-time ছবি — arrow-ই সত্য
P1 P2 P3
│ │ │
a1 ───● │ │
│\ │ │
│ \ m1 │ │
a2 ───● \ b1 ●───────────● c1 m2 (P1->P3)
│ ↘ │ │\
│ b2 ● │ \ r2
a3 ───● ∥ ● │
│ │ c2 ●
chain : a1 → b1 → c1 (message-সেতু + transitivity)
pair : a3 ∥ c2 (কোনো পথ নেই — যে ক্রমই দিন, legal) alice bob
│ │
parent(41) reply(87, vc={bob:1, alice:1})
▼ ▼
P1 ═══╗ async ╔═══ P2 arrival: reply FIRST
▼ ▼
┌──────────────────┐
│ display node │
│ holdback: [reply]│ ← vector-এ alice:1 আছে, known-এ নেই
│ parent এলো → parent, তারপর reply
└──────────────────┘নিজে চালিয়ে দেখুন
Naive timestamp-sort বনাম vector-aware sort — এবং concurrency-র ছড়িয়ে পড়া
# comments_causality.py — naive timestamp sort vs vector-aware causal sort
ARRIVALS = [
# (arrival#, actor, lamport, vector, text)
(1, "carol", 1, {"carol": 1}, "carol: আমার ফ্যান গরম করছে"),
(2, "bob", 2, {"bob": 1, "alice": 1}, "bob: reply — version-stamp"),
(3, "alice", 4, {"alice": 1}, "alice: parent — invalidate?"),
]
def hb(a, b): # a -> b ? va <= vb componentwise এবং va != vb
va, vb = a[3], b[3]
keys = set(va) | set(vb)
return all(va.get(k, 0) <= vb.get(k, 0) for k in keys) and va != vb
def naive_sort(events):
return sorted(events, key=lambda e: e[2]) # lamport-সাজ
def causal_sort(events):
order = []
while len(order) < len(events):
# ready = এখনো-বাকিদের মধ্যে যার undelivered predecessor নেই
ready = [e for e in events if e not in order and
all(not hb(q, e) or q in order for q in events)]
order.append(min(ready, key=lambda e: e[0])) # arrival tie-break
return order
print("— NAIVE: lamport timestamp sort —")
for e in naive_sort(ARRIVALS):
print(f" ts={e[2]} {e[4]}")
print("— CAUSAL: vector-aware topological sort —")
for e in causal_sort(ARRIVALS):
print(f" vc={e[3]} {e[4]}")
naive = naive_sort(ARRIVALS)
inv = any("reply" in x[4] and "parent" in y[4]
for i, x in enumerate(naive) for y in naive[i+1:])
print(f"naive display-এ inversion: {inv}")
# ---------- অংশ ২: k process-এ concurrent pair-এর হিস্যা ----------
def concurrent_ratio(k, m=30):
"""প্রতি process m event; মাঝপথে আগের process-থেকে একটা
message-merge (ring) — বাকি সব independent।"""
clocks = [[0] * k for _ in range(k)]
events = []
for r in range(m):
for proc in range(k):
if r == m // 2:
src = clocks[(proc - 1) % k]
clocks[proc] = [max(a, b)
for a, b in zip(clocks[proc], src)]
clocks[proc][proc] += 1
events.append(tuple(clocks[proc]))
conc = comp = 0
n = len(events)
for i in range(n):
for j in range(i + 1, n):
a, b = events[i], events[j]
le = all(x <= y for x, y in zip(a, b))
ge = all(x >= y for x, y in zip(a, b))
if le or ge: comp += 1
else: conc += 1
return 100 * conc / (comp + conc)
for k in (2, 3, 4, 5, 6):
print(f"k={k}: concurrent pairs = {concurrent_ratio(k):.1f}%")প্রত্যাশিত output (deterministic — হুবহু এটাই আসবে):
— NAIVE: lamport timestamp sort —
ts=1 carol: আমার ফ্যান গরম করছে
ts=2 bob: reply — version-stamp
ts=4 alice: parent — invalidate?
— CAUSAL: vector-aware topological sort —
vc={'carol': 1} carol: আমার ফ্যান গরম করছে
vc={'alice': 1} alice: parent — invalidate?
vc={'bob': 1, 'alice': 1} bob: reply — version-stamp
naive display-এ inversion: True
k=2: concurrent pairs = 24.6%
k=3: concurrent pairs = 38.2%
k=4: concurrent pairs = 46.0%
k=5: concurrent pairs = 51.0%
k=6: concurrent pairs = 54.5%পড়ার সময় তিনটা লক্ষ্য: (১) naive ক্রমে reply আগে — একই ডেটা;
(২) causal ক্রমে edge-এর দিকই final word; (৩) k বাড়ার সাথে
concurrent-হিস্যা বাড়ছে — comparable pair তত বিরল,
asymptotic-ভাবেই। “সবকিছু
order করা” scale-এর হিসেবেই হার মানে।
Lamport timestamp দিয়ে display-সাজালে reply-before-parent inversion হুবহু reproduce হয়, vector-compare + topological sort সেটা deterministic-ভাবে ঠিক করে; আর process-সংখ্যা বাড়লে comparable-pair-এর হিস্যা কমে গিয়ে দেখায় কেন causality শুধু chain-এ দাবি করে, সব pair-এ না।
নিজে বানান
CausalBuffer — holdback queue-সহ causal delivery engine
- প্রতিটা message-এ (sender, vector, payload) — vector-ই তার causal resume
- deliverable শর্ত: sender-এর slot ঠিক পরের সংখ্যা (FIFO-per-sender) এবং বাকি slot-গুলো known-এ covered
- deliverable না হলে holdback queue-তে — মুছবেন না, অপেক্ষা
- প্রতিটা delivery-র পরে গোটা queue re-scan — এক delivery আরোগুলো ছাড় করতে পারে
- state-printer রাখুন — holdback-র বিবর্তনই এই build-এর প্রমাণ
লক্ষ্য: network উল্টো করে জিনিস পাঠাক — engine এমন deliver করবে যে parent কখনো reply-র পরে আসবে না, আর একই sender-এর msg1 কখনো msg2-র পরে না। শেষের দুই assertion এটাই যাচাই করে।
# causal_buffer.py — CausalBuffer: holdback-queue সহ causal delivery
class CausalBuffer:
def __init__(self):
self.known = {} # delivered-সমূহের merged vector clock
self.holdback = [] # এসেছে, কিন্তু deliverable নয়
self.order = [] # চূড়ান্ত delivery sequence
def _deliverable(self, sender, V):
# (১) নিজের slot: sender-এর ঠিক পরের message হতে হবে (FIFO)
if V.get(sender, 0) != self.known.get(sender, 0) + 1:
return False
# (২) বাকি slot: অন্য কার কাজও কি আমি সব দেখে ফেলেছি?
return all(V.get(k, 0) <= self.known.get(k, 0)
for k in V if k != sender)
def receive(self, sender, V, payload):
self.holdback.append((sender, V, payload))
self._show(f"receive {payload!r}")
self._pump()
def _pump(self):
progressed = True
while progressed:
progressed = False
for ev in list(self.holdback):
s, V, p = ev
if self._deliverable(s, V):
self.holdback.remove(ev)
for k, v in V.items(): # knowledge merge
self.known[k] = max(self.known.get(k, 0), v)
self.order.append(p)
self._show(f"DELIVER {p!r}")
progressed = True
def _show(self, tag):
hb = [f"{s}:{p}" for s, _, p in self.holdback]
print(f" {tag:<24} holdback=[{', '.join(hb) or '-'}]")
buf = CausalBuffer()
print("— arrival order: ইচ্ছা করেই উল্টো ফেড —")
buf.receive("bob", {"bob": 1, "alice": 1}, "reply(parent)")
buf.receive("carol", {"carol": 2}, "carol-msg2")
buf.receive("alice", {"alice": 1}, "parent")
buf.receive("carol", {"carol": 1}, "carol-msg1")
print("— চূড়ান্ত delivery order —")
for i, item in enumerate(buf.order, 1):
print(f" {i}. {item}")
assert buf.order.index("parent") < buf.order.index("reply(parent)")
assert buf.order.index("carol-msg1") < buf.order.index("carol-msg2")
print("OK: causality + per-sender FIFO দুই-ই রক্ষা")প্রত্যাশিত output — holdback queue-র শ্বাস-প্রশ্বাসটাই মূল পাঠ:
— arrival order: ইচ্ছা করেই উল্টো ফেড —
receive 'reply(parent)' holdback=[bob:reply(parent)]
receive 'carol-msg2' holdback=[bob:reply(parent), carol:carol-msg2]
receive 'parent' holdback=[bob:reply(parent), carol:carol-msg2, alice:parent]
DELIVER 'parent' holdback=[bob:reply(parent), carol:carol-msg2]
DELIVER 'reply(parent)' holdback=[carol:carol-msg2]
receive 'carol-msg1' holdback=[carol:carol-msg2, carol:carol-msg1]
DELIVER 'carol-msg1' holdback=[carol:carol-msg2]
DELIVER 'carol-msg2' holdback=[-]
— চূড়ান্ত delivery order —
1. parent
2. reply(parent)
3. carol-msg1
4. carol-msg2
OK: causality + per-sender FIFO দুই-ই রক্ষাতিন মুহূর্ত: reply সবার আগে এসেও parent-এর অপেক্ষায় holdback-এ জীবন কাটাল; parent-delivery-র সাথেই cascade হয়ে বেরিয়ে এলো; carol-msg2 ভাইয়ের জন্য FIFO-রক্ষায় অপেক্ষা করল।
নিজে বাড়ান:
- Duplicate delivery inject করুন (একই
(sender, seq)দুইবার) — idempotency guard যোগ করুন - এক sender চিরতরে মরলে holdback-এ কোনগুলো আটকে থাকবে — timeout/flush policy ডিজাইন করুন
- Multi-key version — প্রতি key আলাদা buffer, কোন state share?
বাস্তব সিস্টেমে
বাস্তব জগতে happens-before
Git — relation-টাই explicit। প্রতিটা commit-এর parent-pointer ঘোষিত happens-before edge; history মানেই partial order-এর দলিল। Merge conflict মানে দুই concurrent branch — Git রায় দেয় না, আপনাকে দেয়।
Riak/Dynamo sibling। Vector clock incomparable হলে sibling জন্মায় — system detect করে, resolve আপনার কাজ। “Conflict মানে bug” ভুল: এটা concurrency-র স্বাভাবিক ফসল।
MongoDB causal sessions। Driver-level session context — কৌশল A-ই: client token বহন করে, server chain ধরে; read-your-writes session-boundary-র ভেতরে guaranteed।
Reddit/HN-ধাঁচের threads + COPS। Thread-key partitioning হলো কৌশল B-র mass-market রূপ — পুরো discussion এক shard-এ, reply কখনো question-এর আগে আসে না। COPS (SOSP 2011) প্রমাণ: wide-area store-এ causal-plus eventual-কে ছাড়িয়ে latency দেয়।
যে ভুলগুলো সবাই করে
“Concurrent events মানে একই সময়ে ঘটেছে।”
সংজ্ঞাটা আবার: concurrent মানে কোনো causal path নেই — neither-before। Wall-clock relation-হীন: বছর-দূরের দুই event concurrent হতে পারে, একই মাইক্রোসেকেন্ডের দুই event ordered হতে পারে। “P1-এ t=10ms, P2-তে t=10ms — concurrent?” — উত্তর: path দেখাও, clock নয়।
“Happens-before মানে wall-clock-এ আগে ঘটেছে।”
Relation-টা logical — physical time লাগে না, ব্যবহৃতও হয় না।
Machine-clock sync যা-ই হোক, a → b রায় শুধু program-order আর
message-path দেখে দেয়। উল্টোদিকটাও সতর্ক: clock বলল a আগে —
তবু a ∥ b হলে distributed semantics-এ কেউ আগে না।
“Vector clock ব্যবহার করলে conflict ঠিক হয়েই যায়।”
Vector clock শুধু detect করে — ordered নাকি concurrent। Resolution আলাদা পলিসি: merge-function, LWW, user-prompt, CRDT-math। Riak sibling মানে clock কাজ করেছে; কোন অর্ধেক জিতবে — উত্তর clock-এ নেই। Detection আর resolution আলাদা স্তর।
“সঠিক system মানে সব event-pair-এর order ঠিক করতে হবে।”
Total order পাওয়া যায় — consensus-এর দামে (module-এর পরের
অধ্যায়)। কিন্তু user-visible correctness বেশিরভাগ সময় শুধু
causal subset চায়। Concurrent-pair-এর হিস্যা k-র সাথে বাড়ে —
সব pair-এর order-শ্রম ঢালাই হয় এমন pair-এ, যার ভুল কেউ দেখেই
না। আসল প্রশ্ন: কোন chain user-এর চোখে?
বুঝেছেন কি না দেখুন
1Happens-before relation-এর তিনটা rule এক বাক্যে করে — আর formalভাবে
relation-টা কোন union-এর কী?
স্মরণ
Rule ১: একই process-এ program-order হলে সেটাই →। Rule ২: send-এর
receive উল্টোদিকে →। Rule ৩: transitivity — chain জুড়ে দাও।
Formal: (po ∪ msg)-এর transitive closure।
2Diagram-এ a3 আর c2 — একটার timestamp অন্যটার চেয়ে অনেক বড়
হলেও দুটোকে concurrent বলা হলো। কোন তথ্যের অনুপস্থিতিতে এই রায়?
যুক্তি
a3 আর c2 — একটার timestamp অন্যটার চেয়ে অনেক বড়
হলেও দুটোকে concurrent বলা হলো। কোন তথ্যের অনুপস্থিতিতে এই রায়?Path-এর অনুপস্থিতিতে। a3 → c2 (বা উল্টো) কোনো message-chain
পৌঁছায়নি — vector-compare incomparable। Timestamp-value relation-এর
উৎস নয়; edges-ই উৎস। Clock-ছোট/বড় শুধু detection-tool-এর input।
3Violation #2-তে edit region-A-তে acked, পরের read region-B থেকে
পুরনো মান দিল। Session causality বসলে কোন মুহূর্তে কী বদলাবে?
প্রয়োগ
Edit-ack-এ A session-context vector bump করে response-এ ফেরত দেবে; client সেটা রেখে পরের request-এ বহন করবে; B নিজের applied-vector-এর সাথে তুলনা করে deficit দেখল serve না করে wait/redirect — fix সেই blind-routing মুহূর্তেই।
4CausalBuffer-এ _deliverable-এর শর্ত (১) কেন ঠিক known+1, mere
<= known নয়?
যুক্তি
_deliverable-এর শর্ত (১) কেন ঠিক known+1, mere
<= known নয়?V[sender] == known[sender]+1 মানে sender-এর gap-হীন ঠিক-পরের
message। <= দিলে মাঝের পুরনো message শেষমুহূর্তে deliver হয়ে
ক্রম উল্টাতে পারে; আর gap থাকলে শর্ত (১) fail — পরেরটা অপেক্ষায়
বাধ্য, এটাই মাঝেরটার আসার guarantee।
5Product: প্রতি document-এ collaborative comment-thread + global
activity-feed। কোথায় causal-key partition, কোথায় session causality?
ডিজাইন
Thread: causal-key partition — conversation-local causality single-shard FIFO-তে বিনামূল্যে, client-জটিলতা শূন্য। Feed: cross-thread causality partitioning পারবে না; session-context vector (কৌশল A) যথেষ্ট — user নিজের action-এর order নিজের feed-এ দেখবে, পরের মানুষের feed eventual। Hot-document risk থেকে যায়।
6Experiment-এ k=2 → 24.6% থেকে k=6 → 54.5% concurrent-pair।
এই growth total-order-everything design-এর বিরুদ্ধে কী argument দেয়?
যুক্তি
k=2 → 24.6% থেকে k=6 → 54.5% concurrent-pair।
এই growth total-order-everything design-এর বিরুদ্ধে কী argument দেয়?Process যত বাড়ে comparable-pair তত বিরল — order-enforcement-শ্রম ক্রমবর্ধমানভাবে এমন pair-এ যায় যার ক্রম কারো অভিজ্ঞতা বদলায় না; scale-এ সেই শ্রম consensus-cost, তাই দাবি causality-তেই সীমাবদ্ধ রাখুন।
এরপর কী
এরপর কী
আজ হাতে এসেছে relation — কোন pair আগে-পরে, কোন pair স্বাধীন। কিন্তু user-এর প্রশ্ন বৃহত্তর ভাষায়: “আমি যা লিখলাম তা আমি দেখব? পরশুর লেখা আজ অদৃশ্য হবে না তো?” — এগুলো consistency model-এর ভাষা। পরের লেসনে ladder-টা আনুষ্ঠানিক হবে: linearizability-র real-time recency, sequential-এর mind-bending reorder, causal-এর আজকের relation-ভিত্তিক চুক্তি, eventual-এর ন্যূনতম promise — প্রতিটার জন্য history বানিয়ে বিচারক-কোড চালাব।
আরও পড়ুন
- Time, Clocks, and the Ordering of Events in a Distributed System — Leslie Lamport — Communications of the ACM 21(7), 1978 · Happens-before relation-এর জন্মপত্র — এই লেসনের প্রতিটা সংজ্ঞার মূল উৎস
- Virtual Time and Global States of Distributed Systems — Friedemann Mattern — Parallel and Distributed Algorithms, 1989 · Vector clock আর knowledge-ফ্রেমের পাঠ্যপুস্তক treatment
- Don't Settle for Eventual: Scalable Causal Consistency for Wide-Area Storage with COPS — Wyatt Lloyd, Michael J. Freedman, Michael Kaminsky, David G. Andersen — SOSP 2011 · Session causality-র production-গ্রেড রূপ — hood অংশের ভিত্তি
- Designing Data-Intensive Applications, Chapter 9 — Martin Kleppmann · Causality আর consistency-র সম্পর্কের সেরা practitioner-আলোচনা