Circuit Breaker & Bulkhead
- ●একটা সেবা slow বা down হলে তার ব্যর্থতা পুরো সিস্টেমে ছড়িয়ে cascading failure ঘটাতে পারে।
- ●Circuit Breaker ব্যর্থ সেবায় call থামিয়ে দ্রুত fail করে; এর তিন অবস্থা — Closed, Open, Half-Open।
- ●Timeout, backoff+jitter সহ retry, Bulkhead isolation আর fallback মিলে সিস্টেমকে resilient রাখে।
সমস্যাটা কী?
ধরো তোমার e-commerce অ্যাপের checkout page একটা recommendation সেবা ডাকে "তোমার জন্য আরও পণ্য" দেখাতে। একদিন সেই recommendation সেবা slow হয়ে গেল — প্রতিটি call-এ ৩০ সেকেন্ড লাগছে। এখন checkout page প্রতিটি request-এ ৩০ সেকেন্ড অপেক্ষা করছে, thread আটকে থাকছে।
কিছুক্ষণের মধ্যে checkout-এর সব thread আটকে গেল, সে আর নতুন request নিতে পারছে না — মানে checkout-ও ডাউন। এর পরে যারা checkout-কে ডাকে, তারাও আটকে যাচ্ছে। একটা ছোট, অগুরুত্বপূর্ণ সেবার সমস্যা ডমিনোর মতো পুরো সিস্টেম ফেলে দিল। একে বলে Cascading Failure — distributed system-এর সবচেয়ে ভয়ংকর দুঃস্বপ্ন।
মূল ধারণা
Circuit Breaker হলো এমন একটা প্যাটার্ন যা একটা সেবা বারবার ব্যর্থ হলে তার দিকে call পাঠানো সাময়িকভাবে বন্ধ করে দেয় এবং দ্রুত fail করে — ঠিক যেমন বিদ্যুতের সার্কিট ব্রেকার শর্ট সার্কিটে লাইন কেটে দেয় যাতে পুরো বাড়ি না পোড়ে।
মূল ভাবনা: একটা ব্যর্থ সেবাকে বারবার ডেকে নিজের thread নষ্ট করার চেয়ে, দ্রুত হাল ছেড়ে দাও (fail fast), আর সেবাটাকে সেরে ওঠার সুযোগ দাও।
এর সঙ্গী প্যাটার্ন Bulkhead — জাহাজের মতো, যেখানে দেয়াল দিয়ে আলাদা কম্পার্টমেন্ট থাকে; এক ঘরে পানি ঢুকলেও পুরো জাহাজ ডোবে না। সিস্টেমে এটা মানে resource (thread, connection) আলাদা ভাগে রাখা।
কীভাবে কাজ করে
Circuit Breaker-এর তিন অবস্থা
| অবস্থা | আচরণ | কখন বদলায় |
|---|---|---|
| Closed | সব call স্বাভাবিক যায়, ব্যর্থতা গোনা হয় | ব্যর্থতা সীমা ছাড়ালে → Open |
| Open | call একদম পাঠায় না, সঙ্গে সঙ্গে fail/fallback | নির্দিষ্ট সময় পর → Half-Open |
| Half-Open | অল্প কিছু পরীক্ষামূলক call ছাড়ে | সফল হলে → Closed, ব্যর্থ হলে → Open |
ধাপে ধাপে: শুরুতে Closed — সব ঠিক। ব্যর্থতা বেড়ে সীমা (যেমন ৫০% call ব্যর্থ) ছাড়ালে breaker Open হয়ে call বন্ধ করে দেয়। কিছু সময় (যেমন ৩০ সেকেন্ড) পর Half-Open হয়ে কয়েকটা টেস্ট call ছাড়ে — সেবা সেরে উঠেছে কিনা দেখতে। সফল হলে আবার Closed, না হলে আবার Open।
Timeout, Retry, Backoff + Jitter
Circuit breaker একা যথেষ্ট নয়। সাথে দরকার:
- Timeout: কোনো call অনির্দিষ্টকাল ঝুলে থাকতে দিও না; নির্দিষ্ট সময়ে কেটে দাও।
- Retry: ক্ষণস্থায়ী সমস্যায় আবার চেষ্টা — কিন্তু সাবধানে।
- Exponential Backoff: প্রতি retry-তে অপেক্ষা বাড়াও — ১s, ২s, ৪s, ৮s...।
- Jitter: এর সাথে এলোমেলো একটু সময় যোগ করো।
| কৌশল | retry বিলম্ব |
|---|---|
| Fixed | ১s, ১s, ১s (সবাই একসাথে → herd) |
| Exponential | ১s, ২s, ৪s |
| Exponential + Jitter | ১.৩s, ১.৮s, ৩.৭s (এলোমেলো) |
Jitter কেন? সব client যদি ঠিক একই মুহূর্তে retry করে, তারা একসাথে আবার সেবাটা ডুবিয়ে দেবে — একে বলে "thundering herd"। jitter এই ঢেউ ছড়িয়ে দেয়।
Bulkhead Isolation ও Fallback
Bulkhead-এ প্রতিটি downstream সেবার জন্য আলাদা thread pool/connection pool রাখো। তাহলে recommendation সেবার জন্য বরাদ্দ ১০টা thread আটকে গেলেও, payment সেবার আলাদা thread গুলো অক্ষত থাকে — সমস্যা এক ঘরেই আটকে থাকে।
আর Fallback হলো plan-B: সেবা ব্যর্থ হলে কী দেখাবে? recommendation না পেলে একটা default জনপ্রিয় পণ্যের তালিকা দেখাও — checkout তবু চলবে।
পুরোনো ঢাকার বাড়ির বৈদ্যুতিক সার্কিট ব্রেকার ভাবো। কোনো একটা ঘরে শর্ট সার্কিট হলে ব্রেকার "ট্রিপ" করে শুধু ওই লাইন কেটে দেয় (Open অবস্থা) — পুরো বাড়ি অন্ধকার হয় না, আগুনও লাগে না। কিছুক্ষণ পর তুমি ব্রেকার তুলে দেখো সমস্যা ঠিক হয়েছে কিনা (Half-Open) — হলে চালু রাখো (Closed), না হলে আবার নেমে যায়। আর Bulkhead হলো জাহাজের জলরোধী কামরা — এক কামরায় ফুটো হলে দরজা বন্ধ, পানি ছড়ায় না, জাহাজ ভাসমান থাকে।
সুবিধা ও অসুবিধা
সুবিধা:
- Cascading failure ঠেকায়: এক সেবার পতন পুরো সিস্টেমে ছড়ায় না।
- Fail fast: ব্যর্থ সেবায় thread নষ্ট না করে দ্রুত হাল ছাড়ে।
- Self-healing: Half-Open দিয়ে স্বয়ংক্রিয়ভাবে পুনরুদ্ধার পরীক্ষা।
- ভালো user experience: fallback দিয়ে অ্যাপ পুরোপুরি না ভেঙে চলতে থাকে।
- Resource রক্ষা (bulkhead): এক সমস্যা পুরো resource খেয়ে ফেলে না।
অসুবিধা:
- Tuning কঠিন: threshold, timeout, backoff-এর মান ভুল হলে হিতে বিপরীত।
- জটিলতা: আরও state ও কনফিগ সামলাতে হয়।
- ভুল fallback বিভ্রান্তিকর data দিতে পারে — পুরনো বা ভুল তথ্য দেখানোর ঝুঁকি।
- Testing দরকার: failure simulate করে যাচাই না করলে আস্থা থাকে না।
কখন ব্যবহার করবে / করবে না
ব্যবহার করবে যখন: তোমার সেবা অন্য remote সেবা বা third-party API-র উপর নির্ভর করে যা ব্যর্থ বা slow হতে পারে — অর্থাৎ প্রায় সব microservices সিস্টেমে।
এড়িয়ে যাবে যখন: call সম্পূর্ণ local ও নির্ভরযোগ্য (in-memory), কিংবা সেবা এমন critical যে fallback বা দ্রুত fail কোনোটাই গ্রহণযোগ্য নয় (তখন breaker নয়, redundancy লাগে)।
সবচেয়ে কমন ভুল: চোখ বন্ধ করে সব জায়গায় retry লাগানো, কিন্তু backoff/jitter ছাড়া। সেবা slow হলে এই retry গুলো আরও চাপ বাড়িয়ে সেবাটাকে পুরোপুরি ডুবিয়ে দেয় — এটাকে বলে "retry storm"। মনে রাখো: retry সবসময় exponential backoff + jitter সহ, একটা সর্বোচ্চ সীমা সহ, এবং কেবল ক্ষণস্থায়ী (transient) ত্রুটির জন্য। স্থায়ী ত্রুটিতে (যেমন 400 Bad Request) retry অর্থহীন।
বাস্তব উদাহরণ
Netflix এই ক্ষেত্রে পথপ্রদর্শক। তারা Hystrix নামের একটা library বানায় যা circuit breaker, bulkhead, timeout আর fallback একসাথে দিত। Netflix-এ প্রতিটি downstream call Hystrix-এ মোড়া থাকত — কোনো সেবা slow হলে breaker খুলে যেত, আর fallback (যেমন default recommendation) দেখানো হতো, ফলে পুরো অ্যাপ কখনো একটা সেবার জন্য পড়ে যেত না।
আজকাল Hystrix maintenance mode-এ; ইন্ডাস্ট্রি ব্যবহার করে Resilience4j (Java), যেটা হালকা ও functional — এতে circuit breaker, rate limiter, retry, bulkhead আলাদা মডিউল হিসেবে পাওয়া যায়। Service mesh যেমন Istio/Envoy-ও এখন infrastructure স্তরেই circuit breaking ও bulkhead দিয়ে দেয়, কোড না বদলেই।
ইন্টারভিউতে resilience নিয়ে কথা উঠলে তিনটা শব্দ একসাথে বলো: "timeout + circuit breaker + bulkhead, সাথে exponential backoff ও jitter সহ retry, আর একটা graceful fallback।" এই combo বললে তুমি দেখাও যে একক টুল নয়, একটা সম্পূর্ণ resilience strategy বোঝো — যেটাই সিনিয়র ইঞ্জিনিয়ারের চিন্তা।
মূল শব্দ (Key Terms)
মিনি কুইজ
1. Circuit Breaker-এর 'Open' অবস্থায় কী ঘটে?
2. Retry-তে jitter যোগ করার কারণ কী?
3. Bulkhead pattern-এর মূল লক্ষ্য কী?