০০ যে প্রশ্নগুলোর উত্তর খুঁজবো
এখন পর্যন্ত আমাদের system এর প্রতিটা request একই নিয়মে চলেছে। Request আসে, server পুরো কাজটা শেষ করে, তারপর উত্তর দেয়। এই পুরো সময়টা user অপেক্ষা করে। এই নিয়মের নাম synchronous।
কাজ ছোট হলে এতে কোনো সমস্যা নেই। কিন্তু কাজটা কয়েক মিনিটের হলে কী হবে? এই সেশনে আমরা একটা ভিন্ন পথ শিখবো: কাজটা এখনই নিয়ে নেওয়া, কিন্তু করা পরে। শেষে আপনি বুঝবেন, একটা queue কীভাবে system কে দ্রুত আর নির্ভরযোগ্য করে তোলে।
- ১ User একটা video upload করলো। পুরো process শেষ হওয়া পর্যন্ত তাকে অপেক্ষা করানো কেন সমস্যা?
- ২ সাথে সাথে "পেয়েছি" বলে কাজটা পরে করলে কেমন হয়? (asynchronous processing)
- ৩ "পরে করার কাজ" গুলো কোথায় অপেক্ষা করে? (queue)
- ৪ এই system এর অংশগুলো কী কী? (producer, queue, consumer)
- ৫ কাজের মাঝখানে worker crash করলে job টার কী হয়? (ACK, visibility timeout)
- ৬ একই message দুইবার process হলে কী হবে? (at-least-once, idempotency)
- ৭ Worker রা যত দ্রুত কাজ শেষ করে, তার চেয়ে দ্রুত কাজ এলে কী হয়? (backpressure)
- ৮ সব মিলিয়ে: queue কী দেয়, আর downside গুলো কী?
০১ পুরো process শেষ হওয়া পর্যন্ত user কে অপেক্ষা করানো কেন সমস্যা?
রাত এগারোটা। রাফি তার বোনের বিয়ের video upload করলো। Screen এ একটা spinner ঘুরছে। এক মিনিট গেলো, দুই মিনিট গেলো। রাফি বুঝতে পারছে না, upload আটকে গেছে নাকি চলছে। Page টা refresh করবে কিনা, সেটাও ভাবছে। Refresh করলে যদি সব আবার শুরু থেকে করতে হয়?
পর্দার আড়ালে তখন অনেক কাজ চলছে। Video টা দেখার উপযোগী হওয়ার আগে system কে কয়েকটা কাজ করতে হয়। Video কে কয়েকটা resolution এ encode করতে হয়। Thumbnail বানাতে হয়। Copyright scan করতে হয়। Caption বের করতে হয়। প্রতিটা কাজই ধীর, সব মিলিয়ে প্রায় ৫ মিনিট লাগে।
Synchronous model এ upload এর request টা নিজেই এই সব কাজ একটার পর একটা করে। আর পুরো সময়টা রাফি spinner এর দিকে তাকিয়ে থাকে:
এতে চারটা সমস্যা তৈরি হয়:
- User ধৈর্য হারায়: ৫ মিনিট ধরে spinner দেখলে মানুষ ভাবে কিছু একটা নষ্ট হয়ে গেছে। অনেকে page বন্ধ করে চলে যায়, আর ফিরে আসে না।
- Server এর thread আটকে থাকে: Thread হলো server এর ভেতরের একজন কর্মীর মতো, যে একবারে একটা request সামলায়। রাফির upload যে thread নিয়েছে, সেটা পুরো ৫ মিনিট encode এর কাজেই ব্যস্ত থাকে। এই সময়ে সে আর কাউকে serve করতে পারে না। একসাথে ১০০টা upload এলে ১০০টা thread এভাবে আটকে যায়।
- একটা failure এ সব কাজ নষ্ট হয়: ৪ মিনিটের মাথায় কোনো error হলে আগের সব কাজ বৃথা যায়। রাফিকে আবার শুরু থেকে upload করতে হয়।
- Timeout হয়: Browser, load balancer আর proxy একটা request এর জন্য অনন্তকাল অপেক্ষা করে না। নির্দিষ্ট সময় পার হলে তারা নিজেরাই connection কেটে দেয়।
রাফির আসলে পুরো process শেষ হওয়া পর্যন্ত বসে থাকার দরকার নেই। তার শুধু জানা দরকার যে video টা নিরাপদে পৌঁছেছে। কিন্তু synchronous model এ “এখন নিলাম, পরে করবো” বলার কোনো উপায় নেই।
০২ সাথে সাথে “পেয়েছি” বলে কাজটা পরে করলে কেমন হয়?
পাড়ার Dry Cleaner এর দোকান
Dry cleaner এর দোকানে কাপড় দিতে গেলে কী হয়, একবার মনে করে দেখুন। দোকানদার আপনাকে তিন ঘণ্টা counter এ দাঁড় করিয়ে রাখেন না। তিনি কাপড়গুলো নেন। আপনার হাতে একটা receipt ধরিয়ে দেন: “Ticket #47, কাল বিকেলে নেবেন।” দশ সেকেন্ডের মধ্যে আপনি দোকান থেকে বেরিয়ে আসেন।
আপনার কাপড় তখন পেছনের তাকে অন্য কাপড়ের সাথে সারিতে অপেক্ষা করে। কর্মীরা নিজেদের গতিতে একটা একটা করে কাপড় ধোন। কাজ শেষ হলে কাপড় rack এ ঝুলে থাকে, যতক্ষণ না আপনি receipt নিয়ে ফেরেন।
খেয়াল করুন, এখানে দুটো কাজ আলাদা হয়ে গেছে। কাপড় নেওয়া হলো দশ সেকেন্ডে। ধোয়া হলো পরে, আপনার চোখের আড়ালে।
Video upload এ একই কৌশল (asynchronous processing)
রাফির upload এও এখন একই ঘটনা ঘটে। Server raw video টা save করে। রাফিকে সাথে সাথে একটা ID দেয়, ঠিক dry cleaner এর ticket এর মতো। ভারী কাজটা পরে background এ হয়:
এই পদ্ধতির নাম asynchronous processing। রাফি এখন দুই সেকেন্ডের মধ্যে উত্তর পায়: “আপনার video পেয়েছি, processing চলছে।” তারপর সে নিশ্চিন্তে ঘুমাতে যায়। সকালে video ready হলে system তাকে জানিয়ে দেয়।
Synchronous: কাজটা এখনই হয়, আর user পুরো সময়টা অপেক্ষা করে।
Asynchronous: System সাথে সাথে জানিয়ে দেয় যে কাজটা নেওয়া হয়েছে। আসল কাজটা পরে background এ হয়। ধীর কাজের জন্য user কে আর অপেক্ষা করতে হয় না।
এবার আগের চারটা সমস্যার কী হলো, দেখুন। রাফি সাথে সাথে উত্তর পায়, তাই অপেক্ষার কষ্ট নেই। Server এর thread কয়েক millisecond এর মধ্যে মুক্ত হয়ে যায়। Job টা নিরাপদে জমা থাকে, তাই একটা error এ সব কাজ নষ্ট হয় না। আর request দ্রুত শেষ হয়, তাই timeout এর প্রশ্নই ওঠে না।
Queue দুটো কাজকে আলাদা করে দেয়: কাজ নেওয়া আর কাজ করা। এই আলাদা করাকে বলে decoupling। Web server দ্রুত কাজ নেয়। Worker রা ভারী কাজটা পরে করে, নিজেদের গতিতে।
০৩ “পরে করার কাজ” গুলো কোথায় অপেক্ষা করে?
কাজগুলো অপেক্ষা করে একটা queue তে। Queue মানে লাইন, ঠিক বাসের টিকিট কাউন্টারের লাইনের মতো। প্রতিটা কাজকে বলা হয় একটা job বা message। Job গুলো লাইনে দাঁড়িয়ে থাকে, আর নিজের পালা এলে process হয়। Dry cleaner এর পেছনের তাকে সাজানো কাপড়ের সারিটাই হলো queue।
Queue টা web server এর ভেতরে থাকে না। এটা একটা আলাদা system, নিজের machine এ চলে। তাই web server job টা queue তে রেখেই নিজের কাজে ফিরে যেতে পারে। Web server restart হলেও queue এর job গুলো হারায় না।
০৪ এই system এর অংশগুলো কী কী? (producer, queue, consumer)
প্রতিটা message queue system এ তিনটা চরিত্র থাকে:
- Producer: যে queue তে job রাখে। আমাদের উদাহরণে upload নেওয়া web server টাই producer। Producer সাধারণত দ্রুত কাজ করে, আর সংখ্যায় অনেক থাকে।
- Queue: যেখানে job গুলো process হওয়ার আগ পর্যন্ত নিরাপদে অপেক্ষা করে। বেশিরভাগ queue FIFO (First In, First Out) নিয়মে চলে। অর্থাৎ যে job আগে আসে, সে আগে বের হয়।
- Consumer (worker): যে queue থেকে job তুলে নিয়ে আসল কাজটা করে। আমাদের উদাহরণে video encoder গুলোই consumer। এরাই সাধারণত ধীর অংশ, তাই load বাড়লে এদের সংখ্যা বাড়ানো হয়।
Producer আর consumer কখনো সরাসরি কথা বলে না। এমনকি দুজনের একই সময়ে চালু থাকারও দরকার নেই। Producer job টা queue তে রেখে নিজের কাজে চলে যায়। পরে কোনো একজন worker ফাঁকা হলে job টা তুলে নেয়।
এর একটা বড় লাভ আছে। Processing ধীর হওয়ায় queue লম্বা হতে থাকলে আপনি শুধু worker বাড়াবেন, producer এ হাত দিতে হবে না। পুরোনো synchronous model এ কাজ নেওয়া আর কাজ করা একই server এ হতো। তাই শুধু ধীর অংশটা আলাদা করে বড় করার কোনো উপায় ছিলো না। এই সুবিধাকে বলে independent scaling।
Producer হলো কাপড় জমা দেওয়া customer। Queue হলো তাকে সাজানো কাপড়ের সারি। Consumer হলো দোকানের কর্মী, যিনি একটা একটা করে কাপড় ধোন। তিনজনই নিজের গতিতে চলেন। তাদের মধ্যে একমাত্র যোগাযোগ হলো ঐ সারি।
Load leveling: হঠাৎ ভিড় সামলানো
ধরুন, ঈদের রাতে এক মিনিটে ১,০০০টা video upload হলো। আপনার আছে ১০টা worker।
- Queue ছাড়া (synchronous): ধরুন server একসাথে ২০০টা request সামলাতে পারে। বাকি ৮০০ জন error পায় (503), বা তাদের request timeout হয়। চাপ বেশি হলে server crash ও করতে পারে।
- Queue থাকলে: ১,০০০টা upload ই সাথে সাথে accept হয়, কারণ প্রতিটা request শুধু একটা job queue তে রাখে। ১০টা worker ধীরে ধীরে জমে থাকা কাজ শেষ করে। কারো request timeout হয় না। Job গুলো queue তে নিজের পালার জন্য অপেক্ষা করে।
হঠাৎ ভিড় এলে queue সেটা জমিয়ে রাখে। Worker রা কিন্তু নিজেদের স্বাভাবিক গতিতেই কাজ করে যায়। গাড়ির shock absorber যেমন রাস্তার ঝাঁকুনি সামলায়, queue তেমনি traffic এর হঠাৎ ধাক্কা সামলায়। এই কৌশলের নাম load leveling।
Overload এর HTTP code (পাশের কথা)
Server যখন আর কাজ নিতে পারে না, তখন সে একটা HTTP status code দিয়ে সেটা জানায়। তিনটা code প্রায়ই গুলিয়ে যায়:
- 503 Service Unavailable: Server এই মুহূর্তে overloaded। অর্থাৎ “এখন পারছি না, একটু পরে আবার চেষ্টা করুন।” Overload বোঝাতে এটাই সবচেয়ে প্রচলিত code।
- 429 Too Many Requests: একটা নির্দিষ্ট client তার জন্য বরাদ্দ সীমার (rate limit) চেয়ে বেশি request পাঠিয়েছে।
- 500 Internal Server Error: Server এর ভেতরে কোনো একটা error হয়েছে। এটা overload বোঝানোর code নয়।
০৫ কাজের মাঝখানে worker crash করলে job টার কী হয়?
Worker 3 queue থেকে video #47 তুলে নিলো। Encode শুরু করলো। তিন মিনিট পর server এর power চলে গেলো। (Crash অন্য কারণেও হতে পারে: code এর bug, নতুন version deploy, বা hardware নষ্ট হওয়া।)
Job টা queue থেকে মুছে গিয়ে থাকলে রাফির video আর কখনো process হবে না। কেউ টেরও পাবে না। রাফি শুধু দেখবে, তার video দিনের পর দিন “processing” অবস্থায় আটকে আছে।
ভালো queue এটা হতে দেয় না। নিয়মটা সহজ: worker job তুলে নিলেই queue সেটা মুছে ফেলে না। Worker যখন জানায় যে কাজ শেষ, শুধু তখনই মোছে।
ধাপে ধাপে: ACK আর visibility timeout
পুরো ব্যবস্থাটা দুটো জিনিসের উপর দাঁড়িয়ে আছে:
- Acknowledgment (ACK): কাজ শেষ হলে worker queue কে একটা ছোট বার্তা পাঠায়: “কাজ শেষ।” এই বার্তার নাম ACK। ACK পেলেই queue job টা মুছে ফেলে।
- Visibility timeout: Worker job তুলে নিলে queue সেটা অন্য worker দের কাছ থেকে কিছুক্ষণ লুকিয়ে রাখে, আর একটা timer চালু করে। এই সময়ের মধ্যে ACK না এলে queue ধরে নেয় যে worker টা crash করেছে। তখন job টা আবার লাইনে ফিরে আসে, আর অন্য কোনো worker সেটা তুলে নেয়।
Job সফলভাবে শেষ না হওয়া পর্যন্ত queue সেটা ছাড়ে না। Worker crash করলেও কাজ হারায় না, অন্য কেউ আবার চেষ্টা করে। এই কারণেই queue শুধু asynchronous নয়, reliable ও। Synchronous model এর সাথে তুলনা করুন। সেখানে ৪ মিনিটের মাথায় crash হলে পুরো কাজ হারিয়ে যেতো।
০৬ একই message দুইবার process হলে কী হবে?
ACK পাঠানোর ঠিক আগে crash
এবার একটা কঠিন case দেখুন। Worker 3 video #47 এর encode সফলভাবে শেষ করলো। ACK পাঠাতে যাবে, ঠিক সেই মুহূর্তে crash করলো।
Queue কোনো ACK পেলো না। Timer শেষ হলো। Queue ভাবলো কাজটা হয়নি, তাই #47 কে Worker 5 এর হাতে দিলো। ফলে video টা দুইবার encode হলো।
Video দুইবার encode হলে শুধু কিছু CPU সময় নষ্ট হয়। কিন্তু একই ঘটনা payment এ ঘটলে customer এর card থেকে দুইবার টাকা কাটা যাবে। Email এ ঘটলে user একই email দুইবার পাবে। Database এ ঘটলে একই order দুইবার save হবে।
কাছাকাছি আরেকটা সমস্যা: poison message
কিছু job কখনোই সফল হয় না। যেমন, upload করা file টাই নষ্ট (corrupt)। যে worker ই এটা তুলে নেয়, ACK পাঠানোর আগেই fail করে। Job টা আবার লাইনে ফিরে আসে, আবার fail করে, আর এভাবে চলতেই থাকে। এমন job কে বলে poison message।
এর সমাধান হলো Dead Letter Queue (DLQ)। একটা job নির্দিষ্ট সংখ্যক বার (ধরুন ৫ বার) fail করলে queue সেটাকে একটা আলাদা queue তে সরিয়ে রাখে। পরে একজন engineer সেখানে দেখে নেন, file টায় আসলে কী সমস্যা। এতে একটা নষ্ট job বাকি সব কাজের পথ আটকে রাখতে পারে না।
Delivery guarantee: queue কী প্রতিশ্রুতি দেয়
একটা message কতবার পৌঁছাবে, সেই প্রতিশ্রুতিকে বলে delivery guarantee। তিন রকম guarantee আছে:
| Guarantee | মানে | কী ঝুঁকি থাকে |
|---|---|---|
| At-most-once | Message বড়জোর একবার পৌঁছায় (০ বা ১ বার) | Message হারিয়ে যেতে পারে, কিন্তু duplicate হয় না |
| At-least-once | Message অন্তত একবার পৌঁছায় (১ বা তার বেশি বার) | Message হারায় না, কিন্তু duplicate হতে পারে |
| Exactly-once | Message ঠিক একবার পৌঁছায় | শুনতে আদর্শ, কিন্তু infrastructure level এ পাওয়া প্রায় অসম্ভব |
বেশিরভাগ বাস্তব queue at-least-once দেয়। অর্থাৎ message হারাবে না, কিন্তু মাঝে মাঝে দুইবার আসবে। একটু আগে দেখা “ACK এর ঠিক আগে crash” এর ঘটনাটাই এর কারণ।
নামের শব্দটাই বলে দেয় ঝুঁকি কোন দিকে। At-least-once মানে অন্তত একবার, তাই বেশিবার আসতে পারে: duplicate। At-most-once মানে বড়জোর একবার, তাই একবারও না আসতে পারে: message হারিয়ে যাওয়া।
তাহলে duplicate আসবেই। এটা পুরোপুরি ঠেকানোর চেষ্টা বৃথা। তার বদলে system কে এমনভাবে বানাতে হয়, যাতে duplicate এলেও কোনো ক্ষতি না হয়। এই কৌশলের নাম idempotency।
সমাধান: idempotency
Idempotency: একটা কাজ একবার করলে যে ফল হয়, একাধিকবার করলেও ঠিক একই ফল হলে কাজটা idempotent।
ঘরের light switch এর কথা ভাবুন। Switch টা “on” দিকে চাপলে বাতি জ্বলে। আরো পাঁচবার “on” দিকে চাপলেও বাতি জ্বলেই থাকে। এটা idempotent। কিন্তু অনেক বাতিতে একটাই button থাকে, প্রতিবার চাপলে অবস্থা উল্টে যায় (toggle)। দুইবার চাপলে বাতি আবার নিভে যায়। এটা idempotent নয়।
Worker এর কাজ idempotent হলে video #47 দুইবার এলেও কোনো সমস্যা নেই। দ্বিতীয়বার worker দেখবে কাজটা আগেই হয়ে গেছে, আর কাজটা skip করবে।
কাজকে idempotent বানানোর উপায়: idempotency key
প্রতিটা job এর একটা unique ID থাকে। কাজ শুরুর আগে worker database এ দেখে নেয়, এই ID এর কাজ আগে হয়েছে কিনা। এই ID কে বলে idempotency key। নিচে একটা payment এর উদাহরণ দেওয়া হলো:
এখানে একটা সূক্ষ্ম বিষয় আছে। টাকা কাটা আর “কাজ হয়েছে” record লেখা, এই দুটো একসাথে হতে হবে। হয় দুটোই হবে, নয়তো কোনোটাই হবে না। Database এর এই ব্যবস্থাকে বলে transaction, আর “হয় সব, নয়তো কিছুই না” বৈশিষ্ট্যকে বলে atomic।
কেন এটা দরকার, একটা উদাহরণে দেখুন। Worker টাকা কাটলো, কিন্তু record লেখার আগেই crash করলো। পরের worker database এ কোনো record খুঁজে পাবে না। ফলে সে আবার টাকা কাটবে। Transaction থাকলে এমন হয় না, কারণ crash হলে টাকা কাটাও বাতিল হয়ে যায়।
বাস্তবেও ঠিক এটাই হয়। Stripe একটা জনপ্রিয় online payment service। এর API তে প্রতিটা payment request এর সাথে একটা idempotency key পাঠানো যায়। একই key দিয়ে দ্বিতীয়বার request এলে Stripe আবার টাকা কাটে না। আগের ফলাফলটাই ফেরত দেয়।
Queue at-least-once delivery দেয়, তাই duplicate আসবেই। Consumer কে idempotent বানান, যাতে একই job দুইবার এলেও কোনো ক্ষতি না হয়। এর জন্য লাগে একটা unique ID আর একটা stored record। মনে আছে, upload এর সাথে সাথে রাফি একটা ID পেয়েছিলো? ঐ ID শুধু রাফির receipt নয়। Duplicate ধরার কাজেও ঐ ID ই ব্যবহার হয়।
০৭ Worker রা যত দ্রুত কাজ শেষ করে, তার চেয়ে দ্রুত কাজ এলে কী হয়?
এক মিনিটের হঠাৎ ভিড় queue ভালোভাবেই সামলায়। কিন্তু ভিড় না থামলে কী হবে? ধরুন, প্রতি মিনিটে ১,০০০টা job আসছে, আর worker রা মিনিটে শেষ করতে পারে ৬০০টা। এমন টানা চাপকে বলে sustained overload।
প্রতি মিনিটে ৪০০টা job বাড়তি জমছে। এক দিনে জমে যায় প্রায় পাঁচ লাখ। এর ফল তিনটা:
- অপেক্ষা বাড়তেই থাকে: নতুন একটা job কে পাঁচ লাখ job এর পেছনে দাঁড়াতে হয়। রাফির video কয়েক মিনিটের বদলে কয়েক দিন পরে ready হয়। “পরে হবে” কথাটা আস্তে আস্তে “কখনো হবে না” তে পরিণত হয়।
- Queue এর জায়গা ফুরিয়ে যায়: Queue এর memory আর disk সীমিত। জায়গা ফুরালে queue নতুন job reject করে, বা নিজেই crash করে। দুই ক্ষেত্রেই কাজ হারায়।
- আসল সমস্যা চোখের আড়ালে থাকে: Queue সব job নিতে থাকে, তাই বাইরে থেকে system ঠিকঠাক মনে হয়। Worker যে যথেষ্ট নেই, সেটা কেউ টের পায় না। তারপর একদিন হঠাৎ পুরো system unavailable হয়ে যায়।
Worker বাড়ানো তখনই কাজে দেয়, যখন বাড়তি worker রা জমে থাকা কাজ শেষ পর্যন্ত ধরে ফেলতে পারে। কাজ আসার হার স্থায়ীভাবে worker দের ক্ষমতার চেয়ে বেশি হলে queue শুধু collapse টা কিছুদিন পিছিয়ে দেয়। ঠেকাতে পারে না।
টানা চাপ সামলানোর চারটা উপায়
- Backpressure: Queue প্রায় ভরে গেলে system পেছনের দিকে, মানে producer এর দিকে, একটা সংকেত পাঠায়: “এখন আর নিতে পারছি না।” Producer তখন নতুন কাজ নেওয়া থামায়, আর user কে 503 দিয়ে একটু পরে চেষ্টা করতে বলে। শুনতে খারাপ লাগলেও এটাই সৎ পথ। যে কাজ কখনো শেষ হবে না, সেটা নিয়ে user কে আশায় রাখার চেয়ে শুরুতেই “না” বলা ভালো।
- Autoscaling consumer: System সবসময় queue এর দৈর্ঘ্য মাপে। লাইন লম্বা হলে নিজে থেকেই নতুন worker চালু করে। লাইন ছোট হলে বাড়তি worker বন্ধ করে দেয়। কখন worker বাড়াতে হবে, queue এর দৈর্ঘ্যই সেটা পরিষ্কার বলে দেয়।
- Priority queue: সব কাজের গুরুত্ব সমান নয়। টাকা দেওয়া customer এর video আগে process হয়। Free account এর batch কাজ পরে হয়। এতে চাপের মধ্যেও জরুরি কাজ থেমে থাকে না।
- Load shedding: চাপ খুব বেশি হলে কম জরুরি কাজ ইচ্ছা করে বাদ দেওয়া হয়। যেমন, analytics event drop করা হয়, যাতে payment এর মতো জরুরি কাজ চলতে থাকে।
Queue হঠাৎ আসা ভিড় সামলাতে পারে, কিন্তু টানা overload ঠিক করতে পারে না। টানা চাপে queue শুধু লম্বা হতে থাকে, আর collapse পিছিয়ে যায়। তখন লাগে backpressure, autoscaling, priority আর load shedding। Queue একটা shock absorber, অসীম জায়গা নয়।
০৮ সব মিলিয়ে: queue কী দেয়, আর downside গুলো কী?
System এ queue এর জায়গা
কোথায় queue ব্যবহার হয়
Video আর ছবি process করা, email, SMS বা push notification পাঠানো, order process করা, analytics আর log এর pipeline চালানো: এই সব জায়গায় queue ব্যবহার হয়। Third-party কোনো API এর ধীর call ও এর মধ্যে পড়ে। মূল নিয়ম একটাই। কোনো ধীর কাজের জন্য user কে বসিয়ে রাখতে না চাইলে কাজটা queue তে দিন।
যে technology গুলোর নাম জানা দরকার
- RabbitMQ: পুরোনো আর জনপ্রিয় একটা message broker, মানে queue চালানোর software। কোন message কোন queue তে যাবে, তা নানা নিয়মে ঠিক করে দেওয়া যায়।
- Amazon SQS: AWS এর managed queue। Server চালানোর ঝামেলা AWS নেয়, আপনি শুধু ব্যবহার করেন।
- Apache Kafka: এটা ঠিক queue নয়, বরং একটা distributed log। Message পড়ার পরও মুছে যায় না, ক্রম অনুযায়ী জমা থাকে। প্রতি সেকেন্ডে লাখ লাখ event সামলানোর জন্য Kafka বানানো হয়েছে।
- Redis (streams/lists): হালকা কাজের জন্য সহজ queue। Redis কে আপনি cache হিসেবে আগেই চিনেছেন (সেশন ০৪)।
বাস্তবে আপনি নিজে queue বানাবেন না, এগুলোর একটা ব্যবহার করবেন। সেশন ০৬ এ CDN এর ক্ষেত্রেও আমরা নিজে বানাইনি, ভাড়া নিয়েছিলাম।
Queue যা দেয়
- Decoupling: Producer আর consumer আলাদাভাবে চলে, আর আলাদাভাবে scale করা যায়।
- দ্রুত response: User সাথে সাথে উত্তর পায়, আর ধীর কাজ পেছনে চলতে থাকে।
- Load leveling: হঠাৎ ভিড় queue তে জমা থাকে, আর worker রা স্থির গতিতে কাজ করে।
- Reliability: Worker crash করলেও কাজ হারায় না, কারণ ACK না পাওয়া পর্যন্ত queue job ধরে রাখে।
- Resilience: সব worker কিছুক্ষণ down থাকলেও job গুলো queue তে নিরাপদে অপেক্ষা করে।
Downside গুলো
- Complexity বাড়ে: আরেকটা system চালাতে হয়, monitor করতে হয়, আর বুঝতে হয়।
- Eventual consistency: কাজ সাথে সাথে হয় না, একটু পরে হয়। তাই user কে “processing” অবস্থা দেখাতে হয়। সেশন ০৩ এ replication lag এর সময় এই ধারণাটা প্রথম দেখেছিলেন।
- Duplicate সামলাতে হয়: At-least-once এর কারণে consumer কে idempotent বানাতে হয়। এতে বাড়তি code আর পরিশ্রম লাগে।
- ক্রম ঠিক রাখা কঠিন: একাধিক worker থাকলে message গুলো সবসময় আসার ক্রমে process হয় না।
- Debug করা কঠিন: একটা কাজ কয়েকটা জায়গা ঘুরে, অনেক পরে শেষ হয়। Producer থেকে queue হয়ে worker পর্যন্ত একটা bug খুঁজে বের করা synchronous request এর চেয়ে অনেক কঠিন।
Message queue কাজ নেওয়া আর কাজ করাকে আলাদা করে দেয়। এতে system দ্রুত উত্তর দেয়, crash এর পরও কাজ হারায় না, আর হঠাৎ ভিড় সামলাতে পারে। বিনিময়ে complexity বাড়ে, ফলাফল একটু দেরিতে আসে, আর idempotency নিয়ে ভাবতে হয়।
কাজ ধীর হলে, আর user এর সাথে সাথে ফলাফল দরকার না হলে queue ব্যবহার করুন। User এর এখনই ফলাফল দরকার হলে queue ব্যবহার করবেন না। তখন queue শুধু একটা বাড়তি ধাপ যোগ করে, user কে সেই অপেক্ষাই করতে হয়।
০৯ মগজে প্রেশার দিন
আগে নিজে ভাবুন। তারপর উত্তর খুলে দেখুন: বেশিরভাগ মানুষ কীভাবে ভাবে, সেই চিন্তায় কী বাদ পড়ে, আর আসল কথাটা কোথায়।
উত্তর দেখুন আগে নিজে ভাবুন
সবাই যেভাবে ভাবে
“Synchronous এ handler busy থাকলে request আটকে যায় বা হারিয়ে যায়। Queue হলো post box এ চিঠি ফেলে রাখার মতো। Worker ফাঁকা হলে চিঠি খুলে উত্তর দেয়, তাই চিঠি হারায় না।”
Analogy টা ভালো, কিন্তু একটা গুরুত্বপূর্ণ দিক বাদ পড়েছে।
যে জায়গাটা যোগ করা দরকার
প্রধান সুবিধা শুধু “message হারায় না” নয়। আসল সুবিধা হলো, যে পাঠায়, তাকে অপেক্ষা করতে হয় না। চিঠিটা post box এ ফেলে আপনি সাথে সাথে বাড়ি ফিরে যান। উত্তর কখন লেখা হবে, সেজন্য আপনাকে post box এর সামনে দাঁড়িয়ে থাকতে হয় না। এটাই responsiveness আর decoupling।
Synchronous model এ কাজ নেওয়া আর কাজ করা একসাথে বাঁধা থাকে। Queue দুটোকে আলাদা করে দেয়। Producer দ্রুত কাজ নিয়ে চলে যায়, আর consumer পরে নিজের গতিতে কাজটা করে।
উত্তর দেখুন আগে নিজে ভাবুন
সঠিক চিন্তা
Email পাঠানো ধীর কাজ, আর প্রায়ই fail করে। তাই এটা queue এর জন্য একদম উপযুক্ত। Synchronous model এ email server down থাকলে দুটোর একটা হবে। হয় user email পাবেই না, নয়তো signup নিজেই fail করবে। Queue থাকলে job টা অপেক্ষা করে। Email server ফিরে এলে retry হয়, আর email চলে যায়।
আরো যা বলা যায়
Signup নিজে দ্রুত থাকে। Email server দশ মিনিট down থাকলেও নতুন user রা ঠিকঠাক signup করতে পারে। Email টা শুধু দশ মিনিট পরে পৌঁছায়।
ধীর আর মাঝে মাঝে fail করা কাজ (email, SMS, third-party API call) queue তে দিন। এতে মূল request দ্রুত থাকে। আর সাময়িক failure (transient failure) retry দিয়েই ঠিক হয়ে যায়।
উত্তর দেখুন আগে নিজে ভাবুন
সবাই যেভাবে ভাবে
“Exactly-once পাওয়া কঠিন, তাই সমাধান হলো idempotent processing। একাধিকবার process করলেও ফলাফল একই থাকে।”
সমাধানটা ঠিক আছে। কিন্তু guarantee এর নামগুলো প্রায়ই উল্টে যায়।
যে জায়গাটা ঠিক করতে হয়
প্রথমত, দাবিটাই ভুল। বাস্তবের queue সাধারণত exactly-once দেয় না, দেয় at-least-once। Message হারায় না, কিন্তু ACK এর ঠিক আগে crash হলে একই message দুইবার আসে। তাই duplicate নিয়ে ভাবতেই হবে, আর এর সমাধান idempotency।
দ্বিতীয়ত, নাম দুটো গুলিয়ে ফেলবেন না। At-least-once এ ঝুঁকি হলো duplicate। At-most-once এ ঝুঁকি হলো message হারিয়ে যাওয়া।
কোনো queue exactly-once দেয়, এমন দাবি শুনলে সন্দেহ করুন। ধরে নিন duplicate আসবে, আর consumer কে idempotent বানান।
১০ যে টার্মগুলো শিখলাম
| টার্ম | মানে |
|---|---|
| Synchronous | কাজটা এখনই হয়, আর যে কাজ চেয়েছে সে ফলাফলের জন্য অপেক্ষা করে। |
| Asynchronous | কাজটা নেওয়ার কথা এখনই জানানো হয়, আর আসল কাজটা পরে background এ হয়। |
| Message queue | যেখানে job বা message গুলো worker এর হাতে যাওয়ার আগ পর্যন্ত অপেক্ষা করে। |
| Producer | যে queue তে message রাখে। |
| Consumer / Worker | যে queue থেকে message তুলে আসল কাজটা করে। |
| Decoupling | Producer আর consumer আলাদাভাবে চলে, আর আলাদাভাবে scale করা যায়। |
| Load leveling | Queue হঠাৎ আসা ভিড় জমিয়ে রাখে, যাতে worker রা স্থির গতিতে কাজ করতে পারে। |
| Acknowledgment (ACK) | Worker এর পাঠানো “কাজ শেষ” বার্তা। ACK পেলেই queue message টা মুছে ফেলে। |
| Visibility timeout | Worker কাজ করার সময় message টা যতক্ষণ লুকানো থাকে। এর মধ্যে ACK না এলে message আবার লাইনে ফিরে আসে। |
| At-most-once | Message বড়জোর একবার পৌঁছায়। হারাতে পারে, কিন্তু duplicate হয় না। |
| At-least-once | Message অন্তত একবার পৌঁছায়। হারায় না, কিন্তু duplicate হতে পারে। বেশিরভাগ queue এটাই দেয়। |
| Exactly-once | Message ঠিক একবার পৌঁছায়। Infrastructure level এ পাওয়া প্রায় অসম্ভব। |
| Idempotency | একটা কাজ একাধিকবার করলেও একবার করার মতোই ফল হয়। |
| Idempotency key | একটা unique ID, যেটা দেখে worker বোঝে কাজটা আগে হয়েছে কিনা। |
| Atomic transaction | কয়েকটা কাজ একসাথে হয়: হয় সবগুলো হয়, নয়তো একটাও হয় না। |
| Poison message | যে message প্রতিবার process করতে গেলেই fail করে। |
| Dead Letter Queue (DLQ) | কয়েকবার fail করার পর poison message যে আলাদা queue তে যায়, যাতে পরে কেউ সেটা পরীক্ষা করতে পারে। |
| Backpressure | চাপ বেশি হলে producer কে “এখন আর নিতে পারছি না” সংকেত পাঠানো। |
| Load shedding | চাপ খুব বেশি হলে কম জরুরি কাজ ইচ্ছা করে বাদ দেওয়া। |
| RabbitMQ / SQS / Kafka / Redis | Queue বা event streaming এর জন্য প্রচলিত technology। |
১১ শর্ট সামারি
- Message queue কাজ নেওয়া আর কাজ করাকে আলাদা করে দেয়। Producer queue তে job রেখে user কে সাথে সাথে উত্তর দেয়, আর consumer রা ধীর কাজটা পরে background এ করে।
- Worker ACK না পাঠানো পর্যন্ত queue job টা মোছে না, তাই crash এর পরও কাজ হারায় না। এর ফলে queue at-least-once delivery দেয়, মানে duplicate আসবেই। তাই consumer কে idempotent বানাতে হয়।
- Queue হঠাৎ আসা ভিড় সামলাতে পারে, কিন্তু টানা overload ঠিক করতে পারে না। টানা চাপের জন্য backpressure, autoscaling, priority আর load shedding দরকার।
১২ মনে রাখুন
- কাজ নেওয়া আর কাজ করাকে আলাদা করুন। User এর সাথে সাথে ফলাফল দরকার না হলে কাজটা queue তে দিন।
- ACK না আসা পর্যন্ত job মোছা হয় না। তাই worker crash করলেও কাজ হারায় না।
- At-least-once মানে duplicate, at-most-once মানে হারিয়ে যাওয়া। বেশিরভাগ queue at-least-once দেয়।
- Duplicate ঠেকানোর চেষ্টা না করে consumer কে idempotent বানান। Unique ID, stored record আর atomic transaction দিয়ে এটা করা যায়।
- Queue হঠাৎ ভিড় সামলায়, টানা overload নয়। টানা চাপে backpressure, autoscaling, priority আর load shedding ব্যবহার করুন।
- Queue এর downside আছে। Complexity বাড়ে, আর ফলাফল দেরিতে আসে। তাই শুধু ধীর আর পরে করা যায় এমন কাজেই queue ব্যবহার করুন।