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

Message Queues — Kafka আর RabbitMQ-র দুই দর্শন

Message Queues: Kafka and RabbitMQ

Queue শেষ-হওয়া message মুছে দেয়, log চিরকাল রেখে reader-কেই সময় ঘোরাতে দেয় — RabbitMQ আর Kafka-র দুই দর্শন, push-বনাম-pull backpressure, আর partition-parallelism-এর গণিত।

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

  • Broker কীভাবে producer-consumer-কে temporally decouple করে — buffering, load-leveling, failure-isolation — তিনটা ভূমিকা আলাদা করে ব্যাখ্যা করতে পারবেন
  • RabbitMQ-র মডেল trace করতে পারবেন — exchange routing (direct/topic/fanout) → queue → push delivery → consumer ack, আর smart-broker-dumb-consumer দর্শনের মানে
  • Kafka-র মডেল trace করতে পারবেন — append-only partitioned log, consumer PULL with offsets, dumb-broker-smart-consumer, আর offset-reset replay = log ≠ queue-এর মৌলিক পার্থক্য
  • Partition-কে parallelism-unit হিসেবে ব্যাখ্যা করতে পারবেন — key-hash placement, ordering শুধু partition-এর ভেতরে, consumer-group load-balancing আর rebalance-storm
  • Retention (time/size-based) বনাম ack-delete দুই retention-দর্শনের ফল বিশ্লেষণ করে delivery-semantics-কে আগের লেসনের ladder-এ বসাতে পারবেন

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

আগে এটা বুঝি

Checkout-service আর notification-service সরাসরি HTTP-call-এ জুড়লে? Notification down হলে checkout down; Black-Friday spike-এ notification ধীর হলে checkout-ও ধীর; নতুন consumer (fraud-check, analytics) যোগ করতেই checkout-এর code ছোঁয়া লাগে। তিন সমস্যার এক ঔষধ — মাঝে broker: checkout কাজটা message-এ মুড়িয়ে ছুঁড়ে দেয়, কে কখন নেবে তা নিয়ে আর মাথা ঘামায় না।

কিন্তু broker বলতে দুইটা প্রায়-বিপরীত দর্শন। RabbitMQ বলে: আমি বুদ্ধিমান router — message ছাঁটাই করব, ঠেলে দেব, receipt নেব, প্রয়োজনে ফেরত আনব। Kafka বলে: আমি মূর্খ — শুধু append-only log; কে কী কোথা থেকে পড়বে, দু’বার পড়বে কি না, সব তোমার ইচ্ছা। দুই দর্শনের যন্ত্র, trade-off আর ভুল-ব্যবহার — আজকের পাঠ।

মূল ধারণা

Broker-এর তিন কাজ

ভূমিকাযন্ত্রউপকার
Temporal decouplingConsumer অফলাইন থাকলেও message জমে থাকেService-গুলো একসাথে up থাকতে বাধ্য নয়
Load levelingSpike buffer-এ জমে, consumer নিজের গতিতে খায়Burst পেছনের tier-এ propagate হয় না
Failure isolationConsumer crash → message redeliverIn-flight কাজ হারায় না

RabbitMQ — smart broker, dumb consumer

producer ──publish──► EXCHANGE ──route──► QUEUE ──push──► CONSUMER
             direct: exact-key | topic: pattern | fanout: broadcast
                        ◄────────────ack──────────────┘
  • Exchange routing-এর সব বুদ্ধি — consumer শুধু queue-subscribe করে, কোথা থেকে কী এলো তাতে তার মাথা নেই; per-message routing-flexibility অসাধারণ, দাম broker-এর state-heavy জীবন (routing-table, unacked-tracking)।
  • Push delivery: broker message ঠেলে দেয়; prefetch-window-ই backpressure — window full মানে broker ওই consumer-কে আর পাঠায় না।
  • Ack-delete: basic.ack-এ message মুছে যায়; crash/timeout-এ unacked requeue — at-least-once, duplicate-এর দায় consumer-এর (আগের লেসন!)।

Kafka — dumb broker, smart consumer

producer ──produce(key,val)──► PARTITIONED LOG
                                 ├─ p0: [k7][k2][k9]...  ← append-only
                                 ├─ p1: [k1][k4][k3]...
consumer ──PULL(offset)──◄──────┴─ p2: ...
           commit(offset) ──►    group-storage: {p0:41, p1:38}
  • Append-only log: write হয় শেষে, modify/delete নেই। Broker জানেই না message ‘processed’ হয়েছে কি না — concept-টাই ওর ডিকশনারিতে নেই।
  • Offset: consumer নিজের reading-position নিজে commit করে; crash → ফিরে এসে সেই offset থেকে আবার — at-least-once, idempotency-ই রক্ষাকর্তা।
  • Replayability: offset পিছনে ঘুরিয়ে দিলেই history re-process — bug-fix-এর পরে গতকালের data আবার! Queue-তে অসম্ভব (already deleted); এটাই log ≠ queue-এর মূল পার্থক্য — queue communication-mechanism, log system-of-record।
  • Retention time/size-based (retention.ms=7 days) — delete consumer-এর ack-তে নয়, ঘড়িতে।

Consumer groups — sharing আর rebalance-storm

একই topic-এ একাধিক consumer-instance চললে group partition ভাগ করে দেয়: ৩ partition, ৩ instance → একজন একটা করে; instance মরলে তার partition বাকিরা নেয়। Group-এর ভেতরে = load-balancing; ভিন্ন group = সবাই পুরো stream-এর নিজের copy (fanout)।

Rebalance-trigger ছোট: join/leave, heartbeat-miss, session-timeout। আর rebalance মানে stop-the-world — assignment নতুন হওয়া পর্যন্ত consumption থামে। max.poll.interval.ms heavy-processing-এর সাথে মিললে ঘূর্ণি: pause → rebalance → restart → pause — rebalance-storm।

Retention বনাম ack-delete — দুই দর্শনের চুক্তিপত্র

প্রশ্নQueue (RabbitMQ)Log (Kafka)
Message কবে মরে?Ack-এTime/size-limit-এ
Slow consumer-এর দামQueue-depth বাড়ে, memory-pressureDisk-space বাড়ে (সস্তা!), consumer-lag metric
পুরনো data আবার পড়াঅসম্ভবOffset-reset
Storage দর্শনWorking-memory (transit)System-of-record (history)

ভেতরে কী ঘটছে

Kafka কেন এত দ্রুত — io-models-এর প্রতিশ্রুতি পূরণ

আগের module-এ io-models লেসনের মূল শিক্ষা ফিরে আসে:

  • Sequential I/O: append-only log মানে disk-এ সোজা রেখায় লেখা — random-write-এর তুলনায় শতগুণ।
  • Page cache: Kafka OS page-cache-কেই বিশ্বাস করে, নিজের application-cache প্রায় নেই; hot-stream পুরো memory-speed-এ।
  • Zero-copy: consumer-delivery-তে sendfile syscall — data kernel-space থেকে socket-buffer-এ সরাসরি, user-space copy বাদ। তিনটাই mechanical sympathy — OS-এর যন্ত্র যেমন কাজ করে, design তেমনই।

Push বনাম pull — কে গতি ঠিক করে

PUSH (RabbitMQ):   broker message ধরিয়ে দেয়; pace = prefetch-window
                   (window-ই throttle); latency কম

PULL (Kafka):      consumer batch করে টানে; pace = poll-rate — প্রাকৃতিক
                   backpressure, batch-optimize-এর সুযোগ; idle-এ polling-
                   overhead (long-polling-এ মেটানো)

Push-model-এ slow consumer-কে broker আটকে রাখে (window); pull-model- এ consumer নিজেই পিছিয়ে যায় — আর সেই পেছনে-থাকাটা first-class observable metric: consumer lag = log-tip − committed offset; production-Kafka monitoring-এর প্রাণ।

Rebalance ভেতরে কেমন

Group coordinator (একটা broker) member-heartbeat track করে; timeout/ join/leave-এ rebalance-protocol: সবাই consumption থামায়, assignment-generation বাড়ে, নতুন partition→member map broadcast, আবার শুরু। Safe কিন্তু world-stop — heartbeat-interval, session-timeout, max.poll.interval তিন knob production-tuning-এর প্রাণ।

RabbitMQ-র ব্যর্থতা-পথ: nack আর DLX

basic.nack(requeue=true) → message queue-র সামনে ফেরে — সঙ্গে সঙ্গে আবার deliver হয়ে poison-loop (একই bad message infinite redelivery!)। শিল্প-সমাধান dead-letter-exchange: retry শেষ হলে message চলে যায় DLQ-তে — autopsy-র জন্য। Poison-handling broker-feature দিয়ে না, discipline দিয়ে হয়।

উদাহরণ

একই order-events, দুই ব্রোকারে

workload: order-events; consumer processing-মাঝখানে crash; পরে fix-deploy।

RABBITMQ (ack-based):
t0  unacked window (prefetch=100) full → broker পাঠানো বন্ধ
t1  connection dead → ১০০ unacked REQUEUE → queue=৫০০
t2  নতুন consumer → push শুরু, ack-প্রতি delete
    ✔ হারানো নেই   ✕ processed-ও ফিরে এলো (duplicate!)
                    ✕ পুরো history আর দেখা যাবে না

KAFKA (offset-based):
t0  group A committed offset=1200, log-tip=1700
t1  consumer crash — broker কিছুই বোঝে না, log চলতেই থাকে
t2  fix-deploy, consumer ফিরল → poll(1200) → সেখান থেকে আবার
t3  ops: 'গতকালের ২৪ ঘণ্টা re-process করো' → offset reset → replay!
    ✔ history durable   ✔ replay first-class
    ✕ duplicate effects → consumer-side idempotency (আগের লেসন)

দুই path-ই বৈধ — প্রশ্ন হলো workload-এর দাবি: transient-work-distribution (RabbitMQ sweet-spot) না event-history + replay (Kafka sweet-spot)?

ছবিতে

                       ┌──────────────────────────────┐
 order.created ──►  ┌──┴──┐ direct(key=order.created)│
 [producer]         │ EX  ├──binding──────────► ┌─────────┐
                    │     │                     │ Q-orders│──push──► order-svc
                    └──┬──┘ fanout(broadcast)    └─────────┘
                       │                 ┌──────────┐
                       └───────────────► │ Q-audit  │──push──► audit-svc
                       prefetch-window   └──────────┘
                       ◄──────ack──────── (unacked crash → requeue → duplicate)
RabbitMQ topology — exchange-এর routing-rule-ই traffic-এর মালিক; queue হলো transit-buffer, ack-এই জীবন-মৃত্যু।
LOG (append-only, retention 7d):
p0: ▣▣▣▣▣▣▣▣▣▣▣▣▣▣▣▣▣▣▣▣   tip=20
p1: ▣▣▣▣▣▣▣▣▣▣▣▣▣▣▣▣        tip=16
p2: ▣▣▣▣▣▣▣▣▣▣▣▣             tip=12

group A (3 instances, এক-এক partition):      group B (analytics, slow):
  a1 ← p0 (offset 17, lag 3)                   b1 ← p0,p1,p2 (offset 5.., lag বড়)
  a2 ← p1 (offset 16, lag 0)                   ← এক instance সব টানে, pull-pacing
  a3 ← p2 (offset 12, lag 0)
rebalance: a2 dies → coordinator ভাঙে assignment → a1,a3 নতুন map
Kafka consumer-group — partition-ভিত্তিক assignment; group-A আর B আলাদা copy (fanout), ভেতরে load-balancing; lag = tip − offset।

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

EXPERIMENT

Twin mini-brokers — একই workload push-queue আর pull-log-এ

macOS / Linux (Python 3.10+)· ২০ মিনিট
"""mini_brokers.py — push(queue) বনাম pull(log), একই event-stream"""
import collections

class TinyQueue:
    """RabbitMQ-স্বাদ: push + prefetch-window + ack-delete + requeue"""
    def __init__(self, prefetch=2):
        self.q = collections.deque()
        self.unacked = collections.defaultdict(list)
        self.prefetch = prefetch

    def publish(self, msg):
        self.q.append(msg)

    def try_deliver(self, cid):                  # broker-এর push attempt
        if len(self.unacked[cid]) >= self.prefetch:
            return None                          # window-full → block!
        if self.q:
            m = self.q.popleft()
            self.unacked[cid].append(m)
            return m
        return None

    def ack(self, cid, msg):
        self.unacked[cid].remove(msg)            # delete forever

    def crash(self, cid):                        # unacked → requeue
        self.q.extendleft(reversed(self.unacked.pop(cid, [])))

class TinyLog:
    """Kafka-স্বাদ: append-only + pull-offsets + commit + replay"""
    def __init__(self):
        self.log, self.offsets = [], {}

    def produce(self, msg):
        self.log.append(msg)

    def poll(self, group, limit=3):              # consumer pulls
        off = self.offsets.get(group, 0)
        return off, self.log[off:off + limit]

    def commit(self, group, new_offset):
        self.offsets[group] = new_offset

    def seek(self, group, offset):               # replay-API!
        self.offsets[group] = offset


EVENTS = [f'order-{i}' for i in range(50)]

# ── scenario A: consumer crash mid-flight — দুই ব্রোকারের প্রতিক্রিয়া ──
tq = TinyQueue(prefetch=4)
for e in EVENTS:
    tq.publish(e)
got = [tq.try_deliver('c1') for _ in range(4)]
tq.ack('c1', got[0]); tq.ack('c1', got[1])   # প্রথম দুটো process-শেষ
# got[2], got[3] processing-এই ছিল → CRASH, ack হয়নি
tq.crash('c1')                               # unacked → requeue!
print(f'[queue] delivered={len(got)}, backlog-after-crash={len(tq.q)} '
      f'(requeue সহ) — duplicate-redelivery incoming')

tl = TinyLog()
for e in EVENTS:
    tl.produce(e)
tl.commit('g1', 4)
print(f'[log] committed=4, tip={len(tl.log)} — data immutable, replay-ready')

# ── scenario B: slow consumer — কে থামায়, কে জমায় ──
tq2, tl2 = TinyQueue(prefetch=2), TinyLog()
for e in EVENTS:
    tq2.publish(e); tl2.produce(e)
taken = sum(1 for _ in range(5) if tq2.try_deliver('c-slow'))
print(f'[push] window=2: ৫টা deliver-request-এ broker দিল {taken}টা '
      f'(বাকি queue-তে) — throttle broker-এর হাতে')

_, batch = tl2.poll('g-slow', limit=5)
tl2.commit('g-slow', 5)
lag = len(tl2.log) - tl2.offsets.get('g-slow', 0)
print(f'[pull] consumer এক poll-এই নিল {len(batch)}টা; '
      f'pace নিজের — lag={lag} (monitoring-এর metric)')

# ── scenario C: replay — queue-তে অসম্ভব, log-এ এক-line ──
tl2.seek('g-slow', 0)
_, again = tl2.poll('g-slow', limit=3)
print(f'[replay] seek(0)-এর পরে আবার পড়া গেল: {again}')

নমুনা output:

[queue] delivered=4, backlog-after-crash=48 (requeue সহ) — duplicate-redelivery incoming
[log] committed=4, tip=50 — data immutable, replay-ready
[push] window=2: ৫টা deliver-request-এ broker দিল 2টা (বাকি queue-তে) — throttle broker-এর হাতে
[pull] consumer এক poll-এই নিল 5টা; pace নিজের — lag=45 (monitoring-এর metric)
[replay] seek(0)-এর পরে আবার পড়া গেল: ['order-0', 'order-1', 'order-2']

তিনটা পাঠ:

  • Queue requeue duplicate দেয় (processed-ও ফের আসে); log-এ ‘duplicate’ আসে uncommitted-offset re-poll থেকে — mechanism ভিন্ন, ফল এক: idempotent consumer non-negotiable।
  • Push-এ throttle broker-এর, pull-এ consumer-এর; replay queue-তে impossible (deleted), log-এ এক-line API।
এটা কী প্রমাণ করে

Crash-before-ack queue-তে duplicate-redelivery দেয়, log-এ uncommitted-replay দেয়; slow-consumer-এ push prefetch-block করে, pull lag জমায়; আর offset-reset-ই replay-এর দরজা — সব চোখে হাতে।

নিজে বানান

BUILD IT

MiniLogBroker — partitioned log, offsets, groups, lag metrics, replay

Python · ●●●●○
  1. Partitioned append — key-hash placement, per-partition sequence
  2. Group offset-store — consumer-position per partition
  3. Poll + commit — bounded batches, explicit progress
  4. Lag metric — log-tip minus committed offset, per partition
  5. Seek/replay — offset reset API আর same-key ordering proof
"""minilog.py — single-node partitioned log broker (~120 line)"""
import hashlib

def partition_for(key, count):
    h = int(hashlib.md5(str(key).encode()).hexdigest(), 16)
    return h % count

class MiniLogBroker:
    def __init__(self, partitions=3):
        self.logs = [[] for _ in range(partitions)]   # append-only!
        self.groups = {}                              # g -> {pid: offset}

    def produce(self, key, value):
        p = partition_for(key, len(self.logs))
        self.logs[p].append((key, value))
        return p, len(self.logs[p])                   # (partition, next-offset)

    def offsets(self, group):
        return self.groups.setdefault(
            group, {i: 0 for i in range(len(self.logs))})

    def poll(self, group, max_records=4):
        """bounded pull — মোট max_records, partition ঘুরে ঘুরে"""
        offs = dict(self.offsets(group))
        batch, progress = [], True
        while progress and len(batch) != max_records:
            progress = False
            for pid in range(len(self.logs)):
                if len(batch) == max_records:
                    break
                start = offs[pid]
                if start >= len(self.logs[pid]):
                    continue
                batch.append((pid, start, self.logs[pid][start]))
                offs[pid] = start + 1
                progress = True
        return batch

    def commit(self, group, positions):               # consumer-ই progress-
        self.offsets(group).update(positions)         # এর একমাত্র মালিক

    def lag(self, group):
        offs = self.groups.get(group, {})
        return {pid: len(log) - offs.get(pid, 0)
                for pid, log in enumerate(self.logs)}

    def total_lag(self, group):
        return sum(self.lag(group).values())

    def seek(self, group, pid, offset):               # replay-API!
        self.offsets(group)[pid] = max(0, offset)


# ── benchmark: 1 fast + 3 slow consumer-groups — lag dynamics ──
def benchmark():
    broker = MiniLogBroker(partitions=3)
    TICKS, PRODUCE_PER_TICK = 30, 8
    SPEEDS = {'fast': 24, 'slow-1': 4, 'slow-2': 2, 'v-slow': 1}

    print(f"{'tick':>4} {'produced':>8} " +
          " ".join(f"{g:>7}" for g in SPEEDS))
    produced = 0
    for t in range(1, TICKS + 1):
        for i in range(PRODUCE_PER_TICK):             # steady traffic
            broker.produce(f'user-{i % 5}', f'e{t}-{i}')
        produced += PRODUCE_PER_TICK
        row = ''
        for g, speed in SPEEDS.items():
            batch = broker.poll(g, max_records=speed)  # pull, own pace
            pos = dict(broker.offsets(g))
            for pid, off, rec in batch:
                pos[pid] = off + 1                     # process → commit
            broker.commit(g, pos)
            row += f"{broker.total_lag(g):>7} "
        if t == 1 or t % 10 == 0:
            print(f"{t:>4} {produced:>8} {row}")

    p0, _ = broker.produce('user-99', 'a')             # same-key ordering
    ok, prev = True, p0
    for i in range(5):
        p, nxt = broker.produce('user-99', f'step{i}')
        ok = ok and p == p0 and nxt > prev
        prev = nxt
    print(f"\nsame-key -> same-partition, in-order: {ok}")

    before = broker.total_lag('v-slow')
    broker.seek('v-slow', 0, 0)                        # replay demo
    print(f"seek(v-slow, p0->0): lag {before} -> "
          f"{broker.total_lag('v-slow')} (replay-দায় idempotency)")


benchmark()

নমুনা output:

tick  produced    fast  slow-1  slow-2  v-slow
   1        8       0       4       6       7 
  10       80       0      40      60      70 
  20      160       0      80     120     140 
  30      240       0     120     180     210 

same-key -> same-partition, in-order: True
seek(v-slow, p0->0): lag 216 -> 246 (replay-দায় idempotency)

তিনটা পাঠ:

  • Fast lag শূন্য-ই; slow-group-গুলোর lag rate×time-এ linear — production alert-threshold-এর গণিত এখানেই।
  • Same-key test প্রমাণ করে hash-placement-ই ordering-এর মূলধন।
  • Seek-পরবর্তী lag-লাফ = replay-এর দাম — পুরনো record আবার process হবে, effect অবশ্যই idempotent (আগের লেসনের echo)।

নিজে বাড়ান:

  1. Rebalance simulate করুন — v-slow-এর এক partition অন্য group-member-কে দিন (assignment-map যোগ করে)
  2. Compacted-log mode — একই key-র শেষ record ছাড়া বাকি purge; lag-ganit তাতে কেমন বদলায় দেখুন
  3. Dead-letter path — malformed record আলাদা partition-এ quarantine

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

বাস্তব জগতে

LinkedIn-এ Kafka-র জন্ম। Activity-feed pipeline হাতলে দিয়ে যেত না — ২০১১-র এই paper-project-ই আজ finance, ride-sharing, IoT-সব জায়গার event-backbone; log-darshan distributed-systems-এর সবচেয়ে সফল export-গুলোর একটা।

RabbitMQ-র দুর্গ। Task-distribution, RPC-over-message, complex per-message routing — banking payment-fan-out, CI job-dispatch; management-UI আর mature-routing এখনো এর জয়ের জায়গা।

CDC pipelines। Debezium → Kafka → downstream-everything — database-WAL event-stream হয়ে cache-invalidate, search-index, warehouse-feed চালায়; saga-লেসনের outbox-relay-এরই production-scale রূপ।

Consumer-lag আর SRE। Burrow, exporter-metrics, lag-alerting — Kafka-ops- এর প্রথম দিনই lag-dashboard; rebalance-storm postmortem ইন্টারনেটে আলাদা genre। DLQ hygiene। Poison-autopsy, replay-tooling, schema-registry — দুই ecosystem-এই pattern; মাপকাঠি একটাই: routing-flexibility না history-replay।

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

“Kafka মানে আধুনিক, faster RabbitMQ — migration-ই upgrade।”

দুটো ভিন্ন abstraction: RabbitMQ queue (delete-on-ack, per-message routing), Kafka log (retention, partition-keyed streams)। Low-volume complex-routing task-queue Kafka-তে migrate করলে পাবেন extra-ops-burden, routing-ক্ষমতা হারাবেন; high-throughput replayable-event-stream RabbitMQ-তে আটকে যাবেন। Tool-choice = semantic-fit, fashion নয়।

“Kafka সবসময় ordering গ্যারান্টি দেয়।”

Ordering শুধু partition-এর ভেতরে, তাও producer-এর একই key ব্যবহার করলে। Keyless-produce বা retry-জনিত out-of-order batch মানেই ordering গ্যারান্টি শূন্য; global-order চাইলে single-partition — যেটা parallelism হারানো। Design-principle: ordering-need scope করে key-design করুন, default-এ নয়।

“Ack-delete-ছাড়া তো disk ফুলবেই — retention Kafka-র দুর্বলতা।”

উল্টো। Sequential-append + cheap-disk + time-based-retention মানে storage-cost predictable; queue-র ack-delete-এ ভুলে-যাওয়া consumer একটাই পুরো system hostage (আগের slot-lesson-এর WAL-retention-এর পরিচিত ছায়া)। আর ‘disk ফুলবে’-র উত্তর retention-policy — ৭ দিন, ৭ GB, যা দরকার।

“Consumer-group rebalance সাধারণ operation — মাঝে মাঝে হলেই চলবে।”

Rebalance stop-the-world: পুরো group consumption freeze, তারপর catch-up burst। Frequent rebalance মানে effective-throughput ভাঙা আর downstream-latency spike। Session-timeout, max.poll.interval, heartbeat — তিন knob মিলিয়ে tune করা mandatory; storm-mode ধরা পড়লে প্রথমেই ওখানেই তাকান।

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

1

RabbitMQ-র message-journey পাঁচটা স্টেজে — publish থেকে delete।

স্মরণ

Producer publish করে exchange-এ → binding-rule-এ route → queue-তে store → broker consumer-কে push (prefetch-window মেনে) → consumer ack → delete। Nack/crash হলে unacked requeue — at-least-once।

2

‘Log ≠ queue’ — এক বাক্যে মৌলিক পার্থক্য, আর একটা capability যা শুধু log-এ সম্ভব।

যুক্তি

Queue message-কে transit-item মনে করে (delivered=done, মুছে ফেলে); log message-কে immutable-record মনে রাখে, progress reader-এর offset-এ। শুধু log-এ সম্ভব: offset-reset replay — bug-fix-এর পরে পুরনো data পুরোটা re-process।

3

৬ partition, key=user-id। Consumer-group-এ ৮ instance। কয়টা instance কাজ করবে, বাকিরা কী করবে? Ordering-এর ফল কী?

প্রয়োগ

Partition-ই assignment-unit — ৬ instance এক-এক partition, ২টা idle; scaling-cap = partition-count। Ordering: user-এর সব event hash-এ এক partition-এ → প্রতি-user order অটুট; cross-user global-order নেই — সাধারণত দরকারও নেই।

4

Slow consumer-এ push-model আর pull-model কীভাবে আলাদাভাবে ‘চোখে পড়ে’? Metric কী, আর কে থামায়?

যুক্তি

Push: broker prefetch-window full করে পাঠানো বন্ধ — signal = unacked/window। Pull: consumer নিজের গতিতে poll, পিছিয়ে যায় — signal = consumer-lag (tip − committed offset)। Push-এ থামায় broker, pull-এ consumer; lag-ই monitoring-এর প্রাণ।

5

Payment-events: audit-team ৭ দিনের সব event পরে re-process করতে চায়; fraud-check real-time, per-card ordering জরুরি; email-notification fire-and-forget। Topic/partition/broker কীভাবে ভাববেন?

ডিজাইন

Event-stream-এ history-দাবি → Kafka, retention ≥ ৭ দিন; key=card-id → per-card ordering। তিন consumer-group: fraud (real-time, lag-alert), audit (replay-friendly), notification (skip-on-lag policy)। Email fire-and-forget Kafka ছাড়াও চলে — RabbitMQ/SMTP queue-ই যথেষ্ট; choice-per-consumer, এক-size-fits-none।

এরপর কী

এরপর কী

Broker-এ message flow দেখলাম — সেই message-গুলোই যদি system-এর সত্যের উৎস হয়? পরের লেসনে event-driven architecture আর event sourcing: current-state rows-কে append-only event-stream থেকে derive — database-এর WAL ইতিমধ্যেই যে দর্শন মানে; aggregate-replay, snapshot-checkpoint, CQRS-projection, immutable log বনাম GDPR-delete-এর দ্বন্দ্ব।

আরও পড়ুন