মূল কনটেন্টে যান
📝
ডক•14

Asynchronism

এই পথে যেখানে আসে

  • দিন ০১৫ · queue কেন
    একটা ফিচার যেখানে ব্যবহারকারীকে অপেক্ষা করানো ভুল — ইমেইল, রিপোর্ট, বড় import; queue ছাড়া আর queue দিয়ে — ইংরেজিতে দুই কলামে, ৩ বুলেট করে (ডক ১৪)
  • দিন ০৩৯ · file upload
    "Design a file upload" — ছয় ধাপে বুলেট: অগ্রগতি দেখানো, মাঝপথে বাতিল, ব্যর্থ হলে আবার চেষ্টা, বড় ফাইল টুকরো করে, একই টুকরো দুবার গেলে কী (ডক ১৪ · ডক ১৬)

Synchronous সিস্টেমে caller অপেক্ষা করে থাকে যতক্ষণ না কাজটা শেষ হয়। এটা সহজ, কিন্তু দুটো বড় দুর্বলতা আছে — ধীর কাজ user-কে আটকে রাখে, আর downstream service ধীর বা ডাউন হলে সেই ব্যর্থতা সরাসরি user পর্যন্ত ছড়ায়।

Asynchronism এই সংযোগটা ছিঁড়ে দেয়। কাজটা একটা queue-তে রেখে সাথে সাথে user-কে উত্তর দেওয়া হয়, আর প্রকৃত কাজ পেছনে চলে। এতে তিনটা জিনিস পাওয়া যায়: কম latency (user অপেক্ষা করে না), স্থিতিস্থাপকতা (consumer ডাউন থাকলে message queue-তে জমা থাকে, হারায় না), এবং load leveling (হঠাৎ ট্রাফিক বাড়লে queue শুষে নেয়, backend স্থির গতিতে কাজ করে যায়)।

খরচও আছে — সিস্টেম আর সরল থাকে না। "কাজটা হয়েছে কি না" জানা কঠিন হয়, eventual consistency মেনে নিতে হয়, message duplicate বা ভুল ক্রমে আসতে পারে, এবং debugging কঠিন হয়ে যায়।

Message Queues

Message queue হলো producer ও consumer-এর মাঝখানে বসা একটা buffer। Producer message পাঠায়, queue সেটা টিকিয়ে রাখে, consumer নিজের সুবিধামতো তুলে নেয়। মূল অবদান — decoupling: producer জানে না consumer কে, কোথায়, বা আদৌ এখন চালু আছে কি না।

দুটো মৌলিক আকৃতি:

  • Point-to-point (queue) — প্রতিটা message ঠিক একজন consumer পায়। কাজ ভাগ করে নেওয়ার (work distribution) জন্য। যেমন RabbitMQ-র queue, AWS SQS।
  • Publish/Subscribe (topic) — প্রতিটা message সব subscriber পায়। একই ঘটনায় একাধিক সিস্টেমের প্রতিক্রিয়া দরকার হলে। যেমন Kafka topic, AWS SNS।

Delivery guarantee নিয়ে সচেতন থাকুন:

  • At-most-once — হারাতে পারে, ডুপ্লিকেট হবে না।
  • At-least-once — হারাবে না, কিন্তু ডুপ্লিকেট হতে পারে। এটাই বাস্তবে সবচেয়ে প্রচলিত, তাই consumer-কে idempotent হতে হবে (Idempotent Operations)।
  • Exactly-once — আকাঙ্ক্ষিত, কিন্তু প্রকৃতপক্ষে ব্যয়বহুল ও সীমিত পরিসরে সম্ভব। বেশিরভাগ সিস্টেমে "at-least-once + idempotency" দিয়েই কার্যত exactly-once অর্জন করা হয়।

আরও দুটো জরুরি বিষয়: Dead Letter Queue — বারবার ব্যর্থ হওয়া message আলাদা queue-তে সরিয়ে রাখুন, নাহলে একটা বিষাক্ত message পুরো pipeline আটকে দেবে। আর ordering — বেশিরভাগ queue globally ক্রম রক্ষা করে না; দরকার হলে partition/ordering key ব্যবহার করুন (একই key-র message একই partition-এ যাবে)।

Task Queues

Task queue হলো message queue-র উপর তৈরি একটা উচ্চস্তরের বিমূর্ততা, যা বিশেষভাবে "background-এ কোনো function চালাও" এই কাজের জন্য বানানো। এটা serialization, worker pool, retry policy, scheduling, ও ফলাফল সংরক্ষণ — সবই সামলে দেয়।

উদাহরণ: Celery (Python), Sidekiq (Ruby), BullMQ (Node), Hangfire (.NET)।

ভালো task queue ব্যবহারের কিছু নিয়ম:

  • Task ছোট ও idempotent রাখুন — retry অনিবার্য।
  • Task-এ পুরো object নয়, ID পাঠান — worker চলার সময় সর্বশেষ ডেটা নিজে পড়ে নেবে; payload ছোট থাকবে এবং বাসি ডেটা এড়ানো যাবে।
  • অগ্রাধিকার অনুযায়ী আলাদা queue — ধীর ব্যাচের কাজ যেন জরুরি ইমেইল আটকে না দেয়।
  • Retry-তে exponential backoff + jitter ব্যবহার করুন, নাহলে ব্যর্থ downstream-এ retry storm হবে।

Back Pressure

Producer যখন consumer-এর প্রক্রিয়াকরণের ক্ষমতার চেয়ে দ্রুত কাজ পাঠায়, তখন queue বাড়তেই থাকে। এর পরিণতি ভয়াবহ: memory শেষ হয়ে যায়, queue-তে অপেক্ষার সময় এত বাড়ে যে message গুলো পৌঁছানোর আগেই অপ্রাসঙ্গিক হয়ে যায়, এবং শেষে পুরো সিস্টেম ধসে পড়ে।

Back pressure হলো সেই ব্যবস্থা যেখানে সিস্টেম পেছনের দিকে সংকেত পাঠায় — "আস্তে!"। কৌশলগুলো:

  • Queue-র সীমা বেঁধে দিন এবং পূর্ণ হলে নতুন কাজ স্পষ্টভাবে প্রত্যাখ্যান করুন (503 Service Unavailable বা 429 Too Many Requests + Retry-After)। ধীরে ধীরে মরে যাওয়ার চেয়ে দ্রুত ও সৎভাবে "না" বলা অনেক ভালো।
  • Rate limiting / throttling — প্রবেশমুখেই আগমনের হার সীমিত করুন।
  • Load shedding — চাপের সময় কম গুরুত্বপূর্ণ কাজ (যেমন analytics, recommendation) ফেলে দিয়ে মূল কাজটা বাঁচান।
  • Autoscaling — queue depth-এর ভিত্তিতে consumer সংখ্যা বাড়ান। কার্যকর, কিন্তু তাৎক্ষণিক নয় — তাই এটাই একমাত্র প্রতিরক্ষা হতে পারে না।

নীতি হিসেবে মনে রাখুন: সীমাহীন queue একটা লুকানো বোমা। প্রতিটা queue-র একটা সর্বোচ্চ আকার ও পূর্ণ হলে কী হবে তার একটা সুস্পষ্ট উত্তর থাকা উচিত। আর queue-র দৈর্ঘ্য ও অপেক্ষার সময় (queue age) সবসময় নিরীক্ষণে রাখুন — এগুলোই সিস্টেমের অসুস্থতার সবচেয়ে আগাম সংকেত।


পূর্ববর্তী: Caching · পরবর্তী: Communication