Distributed Message Queue (Kafka) ডিজাইন
- ●Kafka হলো একটি distributed, append-only commit log যেখানে message গুলো topic এবং partition-এ সাজানো থাকে।
- ●প্রতিটি partition-এর leader/follower replication থাকে, আর ordering শুধু একটি partition-এর ভেতরেই নিশ্চিত হয়।
- ●Consumer group, offset commit এবং retention মিলে at-least-once delivery দেয় বিশাল throughput-এ।
ধরো তোমাদের কোম্পানিতে একটা e-commerce platform আছে — order, payment, notification, analytics সব আলাদা service। এখন প্রশ্ন হলো, একটা order হলে এই সব service-কে কীভাবে জানাবে? সরাসরি একে অন্যকে কল করলে coupling বাড়ে, একটা service down হলে পুরো চেইন ভাঙে। এই জায়গায় আসে message queue — আর scale-এ সবচেয়ে জনপ্রিয় হলো Kafka। চলো এটা ধাপে ধাপে ডিজাইন করি।
১. সমস্যা বোঝা (Requirements)
আগে ঠিক করি আমরা কী বানাচ্ছি।
Functional requirements:
- Producer message publish করবে একটা topic-এ।
- Consumer সেই topic থেকে message subscribe করে পড়বে।
- একই message একাধিক consumer group পড়তে পারবে (যেমন analytics আর notification আলাদা আলাদা পড়বে)।
- Message কিছু সময় retain থাকবে, পড়া হয়ে গেলেও সঙ্গে সঙ্গে মুছবে না।
- একটা logical key-এর সব message order বজায় রাখবে।
Non-functional requirements:
- High throughput — সেকেন্ডে লক্ষ লক্ষ message।
- Durability — broker crash করলেও message হারাবে না।
- Horizontal scalability — node যোগ করলেই capacity বাড়বে।
- Low latency — কয়েক millisecond-এর মধ্যে delivery।
Kafka-কে ভাবো একটা সংবাদপত্রের পত্রিকা হিসেবে। Producer হলো সাংবাদিক যারা খবর (message) লেখে, topic হলো একটা নির্দিষ্ট পত্রিকা (যেমন "খেলার পাতা"), আর consumer হলো পাঠক। মজার ব্যাপার — পত্রিকা পড়লেও সেটা মুছে যায় না, একই কপি অনেকে পড়তে পারে, আর প্রত্যেক পাঠক নিজের bookmark (offset) রাখে কোন পেজ পর্যন্ত পড়েছে।
২. স্কেল আন্দাজ (Estimation)
ধরি: 1 million message/second peak
গড় message size: 1 KB
=> ingest throughput = 1,000,000 * 1 KB = 1 GB/second
Retention: 7 দিন
দৈনিক data = 1 GB/s * 86,400 s = ~86 TB/day
7 দিনে = ~600 TB
Replication factor 3 ধরলে disk প্রয়োজন:
600 TB * 3 = ~1.8 PB
Partition planning:
একটি partition ~10 MB/s handle করলে,
1 GB/s ingest-এর জন্য দরকার ~100 partition (নিরাপত্তার জন্য 200+)
মূল শিক্ষা: এই scale একটা single machine-এ অসম্ভব। তাই data কে ভাগ করতেই হবে — সেটাই partition।
৩. API ডিজাইন
# Producer
produce(topic, key, value, headers) -> {partition, offset}
- key থাকলে hash(key) % numPartitions দিয়ে partition নির্ধারণ
- key না থাকলে round-robin / sticky partitioner
# Consumer
subscribe(topics[], groupId)
poll(timeoutMs) -> [records] # batch of records
commitSync(offsets) # offset persist করা
seek(topic, partition, offset) # নির্দিষ্ট offset-এ যাওয়া
# Admin
createTopic(name, numPartitions, replicationFactor)
describeTopic(name) -> partitions, leaders, ISR
লক্ষ্য করো — produce-এর response-এ partition আর offset ফেরত আসে। এই দুটো মিলেই একটা message-এর unique ঠিকানা।
৪. ডেটা মডেল
একটা topic হলো কয়েকটা partition-এর সমষ্টি। প্রতিটি partition একটা ordered, immutable, append-only log। নিচে মূল entity:
| Entity | মূল field | বর্ণনা |
|---|---|---|
| Topic | name, numPartitions, replicationFactor | logical channel |
| Partition | partitionId, leaderBroker, replicas[], ISR[] | parallelism + ordering unit |
| Record | offset, key, value, timestamp, headers | একটি message |
| ConsumerGroupOffset | groupId, topic, partition, offset | কোন group কতদূর পড়েছে |
Storage choice: Kafka নিজে disk-এ segment file হিসেবে log রাখে (sequential write)। কেন disk? কারণ sequential disk write আসলে random memory access-এর চেয়েও fast হতে পারে, আর OS page cache পড়ার সময় কাজে লাগে। Offset তাই কোনো DB নয় — এটা log-এর ভেতর position।
মনে রাখার মতো: Kafka message কে database row হিসেবে নয়, log entry হিসেবে treat করে। তাই random update নেই, শুধু append আছে।
৫. হাই-লেভেল ডিজাইন
মূল components:
- Producer client — partition নির্বাচন, batching, compression করে broker-এ পাঠায়।
- Broker — partition leader/follower host করে, disk-এ log লেখে, consumer-কে serve করে।
- Controller / Metadata layer — কোন partition-এর leader কে, কে cluster-এ আছে — এসব track করে (পুরোনো Kafka-তে ZooKeeper, নতুন version-এ KRaft)।
- Consumer group coordinator — group-এর consumer গুলোর মধ্যে partition assign (rebalance) করে।
Flow (একটা message-এর জীবন):
- Producer
keyদিয়ে hash করে partition বের করে। - সেই partition-এর leader broker-এ message পাঠায়।
- Leader নিজের log-এ append করে, তারপর follower-রা replicate করে।
- যথেষ্ট replica (acks অনুযায়ী) লিখলে producer-কে ack দেয়।
- Consumer group-এর একটা consumer সেই partition assign পায়,
poll()করে batch নেয়। - প্রক্রিয়া শেষে consumer offset commit করে — পরের poll এর পর থেকে শুরু হবে।
৬. গভীরে (Deep Dive)
ক. Partition ও Ordering
Partition হলো Kafka-র parallelism-এর একক। একটা topic-এ যত partition, তত বেশি consumer parallel-এ পড়তে পারে। কিন্তু trade-off: ordering শুধু একটা partition-এর ভেতরেই গ্যারান্টিড। তাই যে message গুলোর order জরুরি (যেমন এক user-এর সব event), সেগুলোকে একই key দিয়ে পাঠাতে হবে যাতে একই partition-এ যায়।
hash("user_42") % 100 = 37 -> সবসময় partition 37
=> user_42-এর সব event order বজায় থাকবে
খ. Replication (Leader/Follower per partition)
প্রতিটি partition-এর একটা leader আর কয়েকটা follower থাকে আলাদা broker-এ। সব read/write leader দিয়ে যায়; follower শুধু leader থেকে copy করে। যে follower গুলো leader-এর কাছাকাছি sync থাকে তাদের বলে ISR (In-Sync Replicas)।
- Leader crash করলে controller ISR থেকে নতুন leader নির্বাচন করে।
acks=allদিলে producer তখনই ack পায় যখন সব ISR লিখে ফেলে — সবচেয়ে durable কিন্তু একটু slow।acks=1দিলে শুধু leader লিখলেই ack — fast কিন্তু leader crash হলে data loss সম্ভব।
acks=1 ব্যবহার করলে leader ack দেওয়ার পর কিন্তু follower replicate করার আগেই leader crash করলে সেই message হারিয়ে যাবে। Durability জরুরি হলে acks=all আর min.insync.replicas=2 সেট করো।
গ. Offset ও Delivery Guarantee
Consumer প্রতিটি partition-এ নিজের offset রাখে — কতদূর পড়েছে। এই offset একটা special internal topic (__consumer_offsets)-এ commit হয়।
- যদি আগে process, পরে commit করো → at-least-once (process-এর পর crash হলে redelivery, কিন্তু hারায় না)। এটাই default এবং সবচেয়ে ব্যবহৃত।
- যদি আগে commit, পরে process করো → at-most-once (process-এর আগে crash হলে message হারাতে পারে)।
- Exactly-once দরকার হলে Kafka-র idempotent producer + transactions ব্যবহার করতে হয়, যা read-process-write cycle কে atomic করে।
যেহেতু at-least-once-এ duplicate আসতে পারে, consumer-কে idempotent বানানো ভালো — যেমন message-এর (partition, offset) বা business key দেখে duplicate skip করা।
৭. বটলনেক ও স্কেলিং
- Hot partition: কোনো একটা key যদি অসম্ভব জনপ্রিয় হয় (যেমন এক বিশাল seller-এর সব order), সেই partition overloaded হবে। সমাধান — key-তে suffix যোগ করে spread করা, অথবা partition সংখ্যা সঠিকভাবে পরিকল্পনা করা।
- Consumer lag: Producer-এর গতি consumer-এর চেয়ে বেশি হলে lag বাড়ে। Monitor করো
consumer lagmetric, দরকারে group-এ consumer যোগ করো (তবে partition সংখ্যার বেশি consumer যোগ করে লাভ নেই — extra consumer idle থাকবে)। - Rebalance storm: Consumer যোগ/বাদ হলে rebalance হয়, এ সময় consumption থামে। Cooperative/sticky rebalancing দিয়ে এর প্রভাব কমানো যায়।
- Disk I/O: Retention বেশি হলে disk দ্রুত ভরে। Time-based বা size-based retention আর compression (snappy/zstd) দিয়ে নিয়ন্ত্রণ করো।
- Partition বাড়ানো: পরে partition বাড়ানো যায় কিন্তু এতে key-to-partition mapping বদলে যায়, ফলে ordering গ্যারান্টি ভাঙতে পারে। তাই শুরুতেই উদার partition planning ভালো।
৮. সারসংক্ষেপ
Kafka মূলত একটা distributed append-only log। Topic কে partition-এ ভাগ করে throughput পাওয়া যায়, প্রতিটি partition-এর leader/follower replication দিয়ে durability পাওয়া যায়, আর offset-based consumption দিয়ে একই data বহু consumer group স্বাধীনভাবে পড়তে পারে। মূল trade-off গুলো মনে রাখো — ordering বনাম parallelism (partition), latency বনাম durability (acks), আর simplicity বনাম exactly-once (transactions)।
Interview-এ Kafka design করলে প্রথমেই partition আর key-এর সম্পর্ক স্পষ্ট করো — এটাই ordering, parallelism আর hot-spot সব সমস্যার কেন্দ্রবিন্দু। তারপর acks দিয়ে durability trade-off, আর offset commit দিয়ে delivery semantics বুঝিয়ে দিলে interviewer খুশি।
মিনি কুইজ
1. Kafka-তে message ordering কোন স্কোপে গ্যারান্টিড?
2. একটি consumer group-এ একই partition একসাথে কয়টি consumer পড়তে পারে?
3. Default-ভাবে Kafka কোন delivery guarantee দেয়?