সেশন ০৮

Message Queue - দ্রুত রেসপন্স, পরে প্রসেস

এই সেশনের লক্ষ্য: কাজটা এখনই নিয়ে নেওয়া আর পরে করার কৌশল (asynchronous processing) বোঝা। Producer, queue আর consumer কে কী করে, crash এর পরও কাজ কীভাবে টিকে থাকে, duplicate কেন আসে আর idempotency দিয়ে কীভাবে সামলাতে হয়, আর টানা চাপে backpressure কেন দরকার।

০০ যে প্রশ্নগুলোর উত্তর খুঁজবো

এখন পর্যন্ত আমাদের 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 upload এ click করলো ৪টা resolution এ encode ৩ মিনিট thumbnail বানানো ২০ সেকেন্ড copyright scan ৪০ সেকেন্ড caption বের করা ৩০ সেকেন্ড respond: "done!" user ~৫ মিনিট spinner দেখলো
Synchronous model এ একটাই request সব ধীর কাজ একে একে করে, আর user পুরোটা সময় বসে অপেক্ষা করে। মাঝপথে fail করলে সব কাজ নষ্ট, আর web server এতক্ষণ আটকে থাকে।

এতে চারটা সমস্যা তৈরি হয়:

  1. User ধৈর্য হারায়: ৫ মিনিট ধরে spinner দেখলে মানুষ ভাবে কিছু একটা নষ্ট হয়ে গেছে। অনেকে page বন্ধ করে চলে যায়, আর ফিরে আসে না।
  2. Server এর thread আটকে থাকে: Thread হলো server এর ভেতরের একজন কর্মীর মতো, যে একবারে একটা request সামলায়। রাফির upload যে thread নিয়েছে, সেটা পুরো ৫ মিনিট encode এর কাজেই ব্যস্ত থাকে। এই সময়ে সে আর কাউকে serve করতে পারে না। একসাথে ১০০টা upload এলে ১০০টা thread এভাবে আটকে যায়।
  3. একটা failure এ সব কাজ নষ্ট হয়: ৪ মিনিটের মাথায় কোনো error হলে আগের সব কাজ বৃথা যায়। রাফিকে আবার শুরু থেকে upload করতে হয়।
  4. 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 এ হয়:

User video upload করলো upload শুরু ১ raw video save করে "processing, ID #47" সাথে সাথে ফেরত ২ process #47 job QUEUE এ কাজের স্তূপে জমা ৩ Worker job তুলে process করে কাজটা পরে background এ হয় ৪ user কে জানানো "video #47 ready" ৫
ধাপ ২ এ user সাথে সাথে একটা ID পেয়ে চলে যায় (highlighted)। আসল ভারী কাজ (ধাপ ৪) queue থেকে worker পরে তুলে নিয়ে background এ করে। User কখনো ধীর কাজের জন্য অপেক্ষা করে না।

এই পদ্ধতির নাম asynchronous processing। রাফি এখন দুই সেকেন্ডের মধ্যে উত্তর পায়: “আপনার video পেয়েছি, processing চলছে।” তারপর সে নিশ্চিন্তে ঘুমাতে যায়। সকালে video ready হলে system তাকে জানিয়ে দেয়।

Synchronous বনাম Asynchronous

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 এ তিনটা চরিত্র থাকে:

PRODUCERS CONSUMERS Web 1 producer Web 2 producer Web 3 producer QUEUE waiting line · FIFO Worker 1 consumer Worker 2 consumer Worker 3 consumer
Producer দ্রুত আর অনেকগুলো; তারা job ফেলে queue তে। Consumer ধীর আর load অনুযায়ী scale করা হয়; তারা job তুলে নেয়। Producer আর consumer সরাসরি কথা বলে না, শুধু queue এর মাধ্যমে যুক্ত।

Producer আর consumer কখনো সরাসরি কথা বলে না। এমনকি দুজনের একই সময়ে চালু থাকারও দরকার নেই। Producer job টা queue তে রেখে নিজের কাজে চলে যায়। পরে কোনো একজন worker ফাঁকা হলে job টা তুলে নেয়।

এর একটা বড় লাভ আছে। Processing ধীর হওয়ায় queue লম্বা হতে থাকলে আপনি শুধু worker বাড়াবেন, producer এ হাত দিতে হবে না। পুরোনো synchronous model এ কাজ নেওয়া আর কাজ করা একই server এ হতো। তাই শুধু ধীর অংশটা আলাদা করে বড় করার কোনো উপায় ছিলো না। এই সুবিধাকে বলে independent scaling।

Mental model

Producer হলো কাপড় জমা দেওয়া customer। Queue হলো তাকে সাজানো কাপড়ের সারি। Consumer হলো দোকানের কর্মী, যিনি একটা একটা করে কাপড় ধোন। তিনজনই নিজের গতিতে চলেন। তাদের মধ্যে একমাত্র যোগাযোগ হলো ঐ সারি।

Load leveling: হঠাৎ ভিড় সামলানো

ধরুন, ঈদের রাতে এক মিনিটে ১,০০০টা video upload হলো। আপনার আছে ১০টা worker।

Load leveling

হঠাৎ ভিড় এলে queue সেটা জমিয়ে রাখে। Worker রা কিন্তু নিজেদের স্বাভাবিক গতিতেই কাজ করে যায়। গাড়ির shock absorber যেমন রাস্তার ঝাঁকুনি সামলায়, queue তেমনি traffic এর হঠাৎ ধাক্কা সামলায়। এই কৌশলের নাম load leveling।

Overload এর HTTP code (পাশের কথা)

Server যখন আর কাজ নিতে পারে না, তখন সে একটা HTTP status code দিয়ে সেটা জানায়। তিনটা 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

১. Worker job #47 নেয় Queue invisible mark করে, timer চালু করে ২. Worker video process করে (timer চলছে) সফল Crash ৩ক. SUCCESS: ACK পাঠায় Queue এখন #47 delete করে ৩খ. CRASH: ACK এর আগে down timer expire; #47 আবার visible, retry
Worker job নিলে queue সেটা delete করে না, শুধু invisible করে timer চালায়। ACK এলে তবেই delete হয়। Crash এ ACK আসে না, timer expire করে, আর job আবার visible হয়ে retry হয়। তাই crash এ কাজ হারায় না।

পুরো ব্যবস্থাটা দুটো জিনিসের উপর দাঁড়িয়ে আছে:

মূল safety নিয়ম

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-onceMessage বড়জোর একবার পৌঁছায় (০ বা ১ বার)Message হারিয়ে যেতে পারে, কিন্তু duplicate হয় না
At-least-onceMessage অন্তত একবার পৌঁছায় (১ বা তার বেশি বার)Message হারায় না, কিন্তু duplicate হতে পারে
Exactly-onceMessage ঠিক একবার পৌঁছায়শুনতে আদর্শ, কিন্তু 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 এর উদাহরণ দেওয়া হলো:

Worker: "#888 charge $50" DB check #888 কি আগে charge হয়েছে? হ্যাঁ না হ্যাঁ - record আছে Duplicate, তাই skip করে শুধু ACK, আবার charge নয় না - record নেই charge করে + record লেখে এক atomic transaction, পরে ACK
কাজ করার আগে unique ID (idempotency key) দিয়ে check করা হয় কাজটা আগে হয়েছে কিনা। হয়ে থাকলে skip; না হলে কাজ আর record-write এক atomic transaction এ। তাই duplicate message এলেও দুইবার charge হয় না।

এখানে একটা সূক্ষ্ম বিষয় আছে। টাকা কাটা আর “কাজ হয়েছে” 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।

১০০০/min আসছে QUEUE backlog বাড়ছে ৬০০/min drain নেট: +৪০০/min জমছে ১ ঘণ্টায় ~২৪,০০০ · ১ দিনে ~৫ লাখ backlog
আসার rate (১০০০/min) drain rate (৬০০/min) এর চেয়ে বেশি হলে queue প্রতি মিনিটে ৪০০ করে বাড়ে। এটা সাময়িক spike নয়, টানা overload; worker যোগ না করলে queue unbounded বাড়তে থাকে আর শেষমেশ collapse করে।

প্রতি মিনিটে ৪০০টা job বাড়তি জমছে। এক দিনে জমে যায় প্রায় পাঁচ লাখ। এর ফল তিনটা:

  1. অপেক্ষা বাড়তেই থাকে: নতুন একটা job কে পাঁচ লাখ job এর পেছনে দাঁড়াতে হয়। রাফির video কয়েক মিনিটের বদলে কয়েক দিন পরে ready হয়। “পরে হবে” কথাটা আস্তে আস্তে “কখনো হবে না” তে পরিণত হয়।
  2. Queue এর জায়গা ফুরিয়ে যায়: Queue এর memory আর disk সীমিত। জায়গা ফুরালে queue নতুন job reject করে, বা নিজেই crash করে। দুই ক্ষেত্রেই কাজ হারায়।
  3. আসল সমস্যা চোখের আড়ালে থাকে: Queue সব job নিতে থাকে, তাই বাইরে থেকে system ঠিকঠাক মনে হয়। Worker যে যথেষ্ট নেই, সেটা কেউ টের পায় না। তারপর একদিন হঠাৎ পুরো system unavailable হয়ে যায়।

Worker বাড়ানো তখনই কাজে দেয়, যখন বাড়তি worker রা জমে থাকা কাজ শেষ পর্যন্ত ধরে ফেলতে পারে। কাজ আসার হার স্থায়ীভাবে worker দের ক্ষমতার চেয়ে বেশি হলে queue শুধু collapse টা কিছুদিন পিছিয়ে দেয়। ঠেকাতে পারে না।

টানা চাপ সামলানোর চারটা উপায়

  1. Backpressure: Queue প্রায় ভরে গেলে system পেছনের দিকে, মানে producer এর দিকে, একটা সংকেত পাঠায়: “এখন আর নিতে পারছি না।” Producer তখন নতুন কাজ নেওয়া থামায়, আর user কে 503 দিয়ে একটু পরে চেষ্টা করতে বলে। শুনতে খারাপ লাগলেও এটাই সৎ পথ। যে কাজ কখনো শেষ হবে না, সেটা নিয়ে user কে আশায় রাখার চেয়ে শুরুতেই “না” বলা ভালো।
  2. Autoscaling consumer: System সবসময় queue এর দৈর্ঘ্য মাপে। লাইন লম্বা হলে নিজে থেকেই নতুন worker চালু করে। লাইন ছোট হলে বাড়তি worker বন্ধ করে দেয়। কখন worker বাড়াতে হবে, queue এর দৈর্ঘ্যই সেটা পরিষ্কার বলে দেয়।
  3. Priority queue: সব কাজের গুরুত্ব সমান নয়। টাকা দেওয়া customer এর video আগে process হয়। Free account এর batch কাজ পরে হয়। এতে চাপের মধ্যেও জরুরি কাজ থেমে থাকে না।
  4. Load shedding: চাপ খুব বেশি হলে কম জরুরি কাজ ইচ্ছা করে বাদ দেওয়া হয়। যেমন, analytics event drop করা হয়, যাতে payment এর মতো জরুরি কাজ চলতে থাকে।
শিক্ষা

Queue হঠাৎ আসা ভিড় সামলাতে পারে, কিন্তু টানা overload ঠিক করতে পারে না। টানা চাপে queue শুধু লম্বা হতে থাকে, আর collapse পিছিয়ে যায়। তখন লাগে backpressure, autoscaling, priority আর load shedding। Queue একটা shock absorber, অসীম জায়গা নয়।

০৮ সব মিলিয়ে: queue কী দেয়, আর downside গুলো কী?

System এ queue এর জায়গা

User request পাঠায় Web Server / Producer request নেয়, সাথে সাথে ফেরত দেয় QUEUE অপেক্ষমাণ job এর buffer Workers / Consumers background এ ধীর কাজ DB / Cache / Notify ফলাফল লেখা, user কে জানানো
Producer request নিয়ে সাথে সাথে ফেরত দেয়; কাজটা queue তে অপেক্ষা করে; consumer পরে সেটা তুলে নিয়ে ধীর কাজ background এ করে। Queue হলো accept আর process এর মাঝের buffer।

কোথায় queue ব্যবহার হয়

Video আর ছবি process করা, email, SMS বা push notification পাঠানো, order process করা, analytics আর log এর pipeline চালানো: এই সব জায়গায় queue ব্যবহার হয়। Third-party কোনো API এর ধীর call ও এর মধ্যে পড়ে। মূল নিয়ম একটাই। কোনো ধীর কাজের জন্য user কে বসিয়ে রাখতে না চাইলে কাজটা queue তে দিন।

যে technology গুলোর নাম জানা দরকার

বাস্তবে আপনি নিজে queue বানাবেন না, এগুলোর একটা ব্যবহার করবেন। সেশন ০৬ এ CDN এর ক্ষেত্রেও আমরা নিজে বানাইনি, ভাড়া নিয়েছিলাম।

Queue যা দেয়

Downside গুলো

  1. Complexity বাড়ে: আরেকটা system চালাতে হয়, monitor করতে হয়, আর বুঝতে হয়।
  2. Eventual consistency: কাজ সাথে সাথে হয় না, একটু পরে হয়। তাই user কে “processing” অবস্থা দেখাতে হয়। সেশন ০৩ এ replication lag এর সময় এই ধারণাটা প্রথম দেখেছিলেন।
  3. Duplicate সামলাতে হয়: At-least-once এর কারণে consumer কে idempotent বানাতে হয়। এতে বাড়তি code আর পরিশ্রম লাগে।
  4. ক্রম ঠিক রাখা কঠিন: একাধিক worker থাকলে message গুলো সবসময় আসার ক্রমে process হয় না।
  5. Debug করা কঠিন: একটা কাজ কয়েকটা জায়গা ঘুরে, অনেক পরে শেষ হয়। Producer থেকে queue হয়ে worker পর্যন্ত একটা bug খুঁজে বের করা synchronous request এর চেয়ে অনেক কঠিন।
সবচেয়ে গুরুত্বপূর্ণ শিক্ষা

Message queue কাজ নেওয়া আর কাজ করাকে আলাদা করে দেয়। এতে system দ্রুত উত্তর দেয়, crash এর পরও কাজ হারায় না, আর হঠাৎ ভিড় সামলাতে পারে। বিনিময়ে complexity বাড়ে, ফলাফল একটু দেরিতে আসে, আর idempotency নিয়ে ভাবতে হয়।

কাজ ধীর হলে, আর user এর সাথে সাথে ফলাফল দরকার না হলে queue ব্যবহার করুন। User এর এখনই ফলাফল দরকার হলে queue ব্যবহার করবেন না। তখন queue শুধু একটা বাড়তি ধাপ যোগ করে, user কে সেই অপেক্ষাই করতে হয়।

০৯ মগজে প্রেশার দিন

আগে নিজে ভাবুন। তারপর উত্তর খুলে দেখুন: বেশিরভাগ মানুষ কীভাবে ভাবে, সেই চিন্তায় কী বাদ পড়ে, আর আসল কথাটা কোথায়।

প্রশ্ন ১: Synchronous আর message queue এর মূল পার্থক্য কী, আর প্রধান সুবিধা কোনটা?
উত্তর দেখুন আগে নিজে ভাবুন

সবাই যেভাবে ভাবে

“Synchronous এ handler busy থাকলে request আটকে যায় বা হারিয়ে যায়। Queue হলো post box এ চিঠি ফেলে রাখার মতো। Worker ফাঁকা হলে চিঠি খুলে উত্তর দেয়, তাই চিঠি হারায় না।”

Analogy টা ভালো, কিন্তু একটা গুরুত্বপূর্ণ দিক বাদ পড়েছে।

যে জায়গাটা যোগ করা দরকার

প্রধান সুবিধা শুধু “message হারায় না” নয়। আসল সুবিধা হলো, যে পাঠায়, তাকে অপেক্ষা করতে হয় না। চিঠিটা post box এ ফেলে আপনি সাথে সাথে বাড়ি ফিরে যান। উত্তর কখন লেখা হবে, সেজন্য আপনাকে post box এর সামনে দাঁড়িয়ে থাকতে হয় না। এটাই responsiveness আর decoupling।

সঠিক ভাষায়

Synchronous model এ কাজ নেওয়া আর কাজ করা একসাথে বাঁধা থাকে। Queue দুটোকে আলাদা করে দেয়। Producer দ্রুত কাজ নিয়ে চলে যায়, আর consumer পরে নিজের গতিতে কাজটা করে।

প্রশ্ন ২: Signup এর পর welcome email: queue তে দেবেন, নাকি synchronous রাখবেন? Failure হলে কী হবে?
উত্তর দেখুন আগে নিজে ভাবুন

সঠিক চিন্তা

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 টা শুধু দশ মিনিট পরে পৌঁছায়।

মূল pattern

ধীর আর মাঝে মাঝে fail করা কাজ (email, SMS, third-party API call) queue তে দিন। এতে মূল request দ্রুত থাকে। আর সাময়িক failure (transient failure) retry দিয়েই ঠিক হয়ে যায়।

প্রশ্ন ৩: "Queue exactly-once দেয়, তাই duplicate নিয়ে ভাবতে হবে না।" এই দাবির বিপক্ষে যুক্তি দিন।
উত্তর দেখুন আগে নিজে ভাবুন

সবাই যেভাবে ভাবে

“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 তুলে আসল কাজটা করে।
DecouplingProducer আর consumer আলাদাভাবে চলে, আর আলাদাভাবে scale করা যায়।
Load levelingQueue হঠাৎ আসা ভিড় জমিয়ে রাখে, যাতে worker রা স্থির গতিতে কাজ করতে পারে।
Acknowledgment (ACK)Worker এর পাঠানো “কাজ শেষ” বার্তা। ACK পেলেই queue message টা মুছে ফেলে।
Visibility timeoutWorker কাজ করার সময় message টা যতক্ষণ লুকানো থাকে। এর মধ্যে ACK না এলে message আবার লাইনে ফিরে আসে।
At-most-onceMessage বড়জোর একবার পৌঁছায়। হারাতে পারে, কিন্তু duplicate হয় না।
At-least-onceMessage অন্তত একবার পৌঁছায়। হারায় না, কিন্তু duplicate হতে পারে। বেশিরভাগ queue এটাই দেয়।
Exactly-onceMessage ঠিক একবার পৌঁছায়। 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 / RedisQueue বা event streaming এর জন্য প্রচলিত technology।

১১ শর্ট সামারি

  1. Message queue কাজ নেওয়া আর কাজ করাকে আলাদা করে দেয়। Producer queue তে job রেখে user কে সাথে সাথে উত্তর দেয়, আর consumer রা ধীর কাজটা পরে background এ করে।
  2. Worker ACK না পাঠানো পর্যন্ত queue job টা মোছে না, তাই crash এর পরও কাজ হারায় না। এর ফলে queue at-least-once delivery দেয়, মানে duplicate আসবেই। তাই consumer কে idempotent বানাতে হয়।
  3. Queue হঠাৎ আসা ভিড় সামলাতে পারে, কিন্তু টানা overload ঠিক করতে পারে না। টানা চাপের জন্য backpressure, autoscaling, priority আর load shedding দরকার।

১২ মনে রাখুন