Retry Logic

ডিস্ট্রিবিউটেড সিস্টেমে ফেইলিউর বা ত্রুটি একটি সাধারণ ঘটনা। তাই সিস্টেম ফেইল করবে কি না, সেটি প্রশ্ন নয়; বরং ফেইল করলে সিস্টেম তা কীভাবে সামলাবে, সেটিই আসল বিষয়।

রিট্রাই লজিক হলো এমন একটি মেকানিজম, যা কোনো ট্রানজিয়েন্ট ফেইলিউর (Transient Failure) বা সাময়িক সমস্যা দেখা দিলে ফেইল হওয়া অপারেশনটি নিজে থেকেই পুনরায় চেষ্টা করে। সঠিকভাবে ইমপ্লিমেন্ট করা হলে এটি সিস্টেমের নির্ভরযোগ্যতা (Reliability) বাড়ায় এবং ডাউনটাইম কমায়। তবে ভুলভাবে তৈরি করা রিট্রাই লজিক একটি ছোট সমস্যাকে পুরো সিস্টেমের আউটেজে (Outage) পরিণত করতে পারে।

১. রিট্রাই লজিক কেন দরকার?

মাইক্রোসার্ভিস আর্কিটেকচারে একটি রিকোয়েস্টকে অনেকগুলো নেটওয়ার্ক হপ (Network Hop) পার হতে হয়। এর মধ্যে যেকোনো ধাপে নিচের সাময়িক সমস্যাগুলো হতে পারে:

  • নেটওয়ার্ক ফেইলিউর: সাময়িকভাবে কানেক্টিভিটি চলে যাওয়া, প্যাকেট লস (Packet loss) বা DNS রেজোলিউশন ব্যর্থ হওয়া।

  • সার্ভিস আন-অ্যাভেইলেবিলিটি (Service Unavailability): অতিরিক্ত লোড, ডিপ্লয়মেন্ট চলাকালীন বা অটো-স্কেলিংয়ের (Auto-scaling) সময় সার্ভিস সাময়িকভাবে সাড়া না দেওয়া।

  • টাইমআউট (Timeout): সার্ভার নির্দিষ্ট সময়ের মধ্যে রেসপন্স দিতে ব্যর্থ হওয়া।

  • রেট লিমিটিং (Rate Limiting): API নির্দিষ্ট সীমার বেশি রিকোয়েস্ট নিতে না পারা এবং 429 স্ট্যাটাস কোড ফেরত দেওয়া।

  • কনকারেন্সি কনফ্লিক্ট (Concurrency Conflict): ডেটাবেস ডেডলক (Deadlock), লক টাইমআউট বা ভার্সন মিসম্যাচ।

  • ফেইলওভার (Failover): ডেটাবেস বা ক্লাস্টারে প্রাইমারি নোড বদলানোর (Leader election) মুহূর্তে কিছু রিকোয়েস্ট ব্যর্থ হওয়া।

যেহেতু এই সমস্যাগুলো সাময়িক, তাই একটু পর আবার চেষ্টা করলে সাধারণত রিকোয়েস্টটি সফল হয়।

২. কোন এররে রিট্রাই করবে এবং কোনটায় করবে না?

রিট্রাই লজিকের সবচেয়ে গুরুত্বপূর্ণ সিদ্ধান্ত হলো—ত্রুটির ধরন চিহ্নিত করা। নিয়মটি খুব সহজ: যে রিকোয়েস্টটি এখন ভুল, সেটি ১০ বার পাঠালেও ভুলই থাকবে। পারমানেন্ট এররে রিট্রাই করলে শুধু সার্ভারের রিসোর্স নষ্ট হয়।

রিট্রাই করা উচিত (Transient Error)

রিট্রাই করা উচিত নয় (Permanent Error)

408 Request Timeout

400 Bad Request

429 Too Many Requests (Retry-After মেনে)

401 Unauthorized / 403 Forbidden

502 Bad Gateway

404 Not Found

503 Service Unavailable

409 Conflict (বিজনেস লজিকের কারণে হলে)

504 Gateway Timeout

422 Validation Error

ডেটাবেস ডেডলক বা লক টাইমআউট

স্কিমা (Schema) বা সিরিয়ালাইজেশন এরর

কানেকশন রিসেট বা DNS ফেইলিউর

ডোমেইন এরর (যেমন: Insufficient balance)

৩. রিট্রাই স্ট্র্যাটেজি (Retry Strategies)

৩.১ Immediate Retry:

ফেইলিউর হওয়ার সঙ্গে সঙ্গে আবার চেষ্টা করা। এটি শুধুমাত্র খুব ছোট এবং বিচ্ছিন্ন গ্লিচে (যেমন: প্যাকেট ড্রপ) ব্যবহার করা উচিত। তবে সমস্যা দীর্ঘ হলে এটি সার্ভারের ওপর মারাত্মক চাপ বাড়ায়।

৩.২ Fixed Interval Retry:

প্রতিবার নির্দিষ্ট সময় (যেমন: ২ সেকেন্ড) পর পর চেষ্টা করা। এটি ইমপ্লিমেন্ট করা সহজ হলেও, একাধিক ক্লায়েন্ট একসঙ্গে রিট্রাই করলে সিস্টেমে লোড স্পাইক (Load spike) তৈরি হয়।

৩.৩ Exponential Backoff:

প্রতিবার অপেক্ষার সময় দ্বিগুণ করা হয় (যেমন: 1s → 2s → 4s → 8s)। এর ফলে ওভারলোডেড সার্ভার রিকভার করার পর্যাপ্ত সময় পায়।

  • সতর্কতা: এর সাথে অবশ্যই একটি সর্বোচ্চ সীমা বা Cap রাখা জরুরি। তা না হলে ১০ম চেষ্টায় অপেক্ষার সময় প্রায় ১৭ মিনিট হয়ে যেতে পারে।

৩.৪ Jitter (Randomized Backoff):

এক্সপোনেনশিয়াল ব্যাকঅফের সঙ্গে র‍্যান্ডম সময় (Jitter) যোগ করা হয়। একটি ডাউন হওয়া সার্ভিস পুনরায় চালু হলে হাজার হাজার ক্লায়েন্ট যদি ঠিক একই মুহূর্তে রিট্রাই করে, তবে সার্ভারটি আবারও ক্র্যাশ করতে পারে। একে থান্ডারিং হার্ড প্রবলেম (Thundering herd problem) বলে। Jitter এই সমস্যা প্রতিরোধ করে রিকোয়েস্টগুলোকে বিভিন্ন সময়ে ছড়িয়ে দেয়। AWS-এর মতে 'Full Jitter' হলো সবচেয়ে আদর্শ উপায়।

৩.৫ Server-Guided Retry (Retry-After):

অনেক সময় সার্ভার 429 বা 503 রেসপন্সের সঙ্গে একটি Retry-After হেডার পাঠিয়ে দেয়, যেখানে বলে দেওয়া থাকে কতক্ষণ পর আবার রিকোয়েস্ট করতে হবে। ক্লায়েন্টের নিজস্ব হিসাবের বদলে সর্বদা এই হেডারটিকে অগ্রাধিকার দেওয়া উচিত।

৪. রিট্রাইয়ের পরিপূরক রেজিলিয়েন্স প্যাটার্ন

রিট্রাই লজিক একা কাজ করতে পারে না। সিস্টেমকে আরও স্থিতিশীল করতে এর সাথে নিচের প্যাটার্নগুলো ব্যবহার করা জরুরি:

  • সার্কিট ব্রেকার (Circuit Breaker): একটানা ব্যর্থতা দেখলে এটি নির্দিষ্ট সময়ের জন্য রিকোয়েস্ট পাঠানো পুরোপুরি বন্ধ রাখে (Fail fast)। ডাউন হয়ে থাকা সার্ভিসে বারবার রিকোয়েস্ট পাঠিয়ে ক্যাসকেডিং ফেইলিউর (Cascading failure) ঠেকানোর এটিই প্রধান উপায়।

  • টাইমআউট এবং ডেডলাইন (Timeout & Deadline): প্রতিটি চেষ্টার (Attempt) জন্য একটি লোকাল টাইমআউট এবং পুরো অপারেশনের জন্য একটি গ্লোবাল ডেডলাইন থাকা উচিত। আপস্ট্রিম সার্ভিসের ডেডলাইন শেষ হয়ে গেলে ডাউনস্ট্রিমে রিট্রাই চালানোর কোনো মানে নেই।

  • ফলব্যাক (Fallback): সব রিট্রাই ব্যর্থ হলে সিস্টেম কী করবে তা আগে থেকে নির্ধারণ করা। যেমন: ক্যাশ (Cached) থেকে পুরনো ডেটা দেখানো বা একটি ডিফল্ট ভ্যালু রিটার্ন করা।

  • ডেড লেটার কিউ (DLQ - Dead Letter Queue): অ্যাসিনক্রোনাস সিস্টেমে সর্বোচ্চ রিট্রাই শেষ হয়ে গেলে রিকোয়েস্টটি মুছে না ফেলে DLQ-তে সংরক্ষণ করতে হয়, যেন পরে তা অ্যানালাইজ বা ম্যানুয়ালি প্রসেস করা যায়।

  • বাল্কহেড (Bulkhead): প্রতিটি এক্সটার্নাল ডিপেনডেন্সির জন্য আলাদা থ্রেড পুল রাখা। এতে একটি সার্ভিস স্লো হলে অন্য সার্ভিসগুলো প্রভাবিত হয় না।

৫. যে ভুলগুলো অবশ্যই এড়িয়ে চলতে হবে

৫.১ রিট্রাই স্টর্ম বা অ্যামপ্লিফিকেশন (Retry Storm):

ধরো, A → B → C → D আর্কিটেকচারে প্রতিটি লেয়ার ৩ বার করে রিট্রাই করে। D সার্ভিসটি ব্যর্থ হলে সে মোট ৩ × ৩ × ৩ = ২৭ বার রিকোয়েস্ট পাবে। চেইন যত বড় হবে, লোড তত গুণিতক হারে বাড়বে।

  • সমাধান: চেইনের যেকোনো একটি লেয়ারে (সাধারণত একদম বাইরের বা ক্লায়েন্ট-সাইড লেয়ারে) রিট্রাই কনফিগার করো।

৫.২ নন-আইডেমপোটেন্ট (Non-Idempotent) অপারেশনে রিট্রাই:

পেমেন্ট রিকোয়েস্ট টাইমআউট হয়েছে, কিন্তু সার্ভারে আসলে টাকা কেটে নেওয়া হয়ে গেছে। এখন রিট্রাই করলে ডাবল চার্জ হবে।

  • সমাধান: ক্লায়েন্ট প্রতিটি রিকোয়েস্টের সঙ্গে একটি ইউনিক Idempotency-Key পাঠাবে। সার্ভার সেই কী (Key) চেক করে দেখবে এটি আগে প্রসেস করা হয়েছে কি না। HTTP মেথডের ক্ষেত্রে GET, PUT, DELETE সাধারণত আইডেমপোটেন্ট হলেও POST এর ক্ষেত্রে বাড়তি সতর্কতা প্রয়োজন।

৬. প্রোডাকশন-রেডি রিট্রাই লজিক চেকলিস্ট

  1. ম্যাক্সিমাম লিমিট: রিট্রাই সীমা ৩ থেকে ৫ বারের মধ্যে রাখা। ইনফিনিট (Infinite) রিট্রাই কখনো ব্যবহার না করা।

  2. Backoff + Jitter + Cap: এই তিনটি উপাদান একসাথে ব্যবহার করে লজিক ডিজাইন করা।

  3. Error Filtering: শুধুমাত্র রিট্রাইএবল (Transient) এররেই যেন রিকোয়েস্ট পাঠানো হয় তা নিশ্চিত করা।

  4. Idempotency: ডুপ্লিকেট ডেটা বা ট্রানজ্যাকশন এড়াতে আইডেমপোটেন্সি মেকানিজম রাখা।

  5. Single-layer Retry: রিট্রাই স্টর্ম ঠেকাতে মাল্টিপল লেয়ারে রিট্রাই না করা।

  6. Observability ও Tracing: প্রতিটি রিকোয়েস্টের সাথে Correlation-ID পাঠানো। রিট্রাই রেট, সার্কিট ব্রেকারের স্টেট এবং ফেইল হওয়ার কারণ লগ করা এবং মনিটরিং ড্যাশবোর্ডে অ্যালার্ট সেট করা।

৭. রিকমেন্ডেড লাইব্রেরি

বাস্তব প্রোজেক্টে এই জটিল লজিকগুলো নিজে স্ক্র্যাচ থেকে লেখার চেয়ে পরীক্ষিত লাইব্রেরি ব্যবহার করা অনেক বেশি নিরাপদ:

  • Java: Resilience4j

  • Python: Tenacity

  • .NET: Polly

  • Go: Hashicorp go-retryablehttp

রিট্রাই লজিক মানে শুধু "আবার চেষ্টা করো" নয়। কখন, কতবার, কত বিরতিতে এবং কখন থামতে হবে—এই সিদ্ধান্তগুলোই আসল। খারাপভাবে বানানো রিট্রাই লজিক, রিট্রাই না থাকার চেয়েও সিস্টেমের জন্য বেশি বিপজ্জনক।