بناء أنظمة مرنة ضد انهيار الخدمات الخارجية: قواطع الدوائر والتراجع الأسي
حماية النظام من الانهيار المتتابع عند تعطل الخدمات الخارجية باستخدام نمط قاطع الدائرة، إعادة المحاولة الذكية، وعزل الموارد.
🌐 External Service Failure: لما API خارجية توقع السيستم كله
سؤال انترفيو شكله بسيط...
</div>عندك API بتجيب سعر الدولار من Service خارجية، وفجأة الخدمة بقت ترد بعد 10 ثواني بدل 200ms. هتعمل إيه؟
📚 Table of Contents
- المشكلة
- Cascading Failure
- ليه Timeout مهم
- Retry مش دايمًا حل
- Exponential Backoff
- Jitter
- Retry Budget
- Circuit Breaker
- Fallback
- Stale Data
- Bulkhead Isolation
- Connection Pool Isolation
- Idempotency
- Rate Limiting
- Timeouts على كل Network Boundary
- الترتيب الصحيح للـResilience
- Architecture
- Failure Scenarios
- Monitoring
- Common Wrong Answers
- Interview Answer
- Production Checklist
- Official Sources
🎤 السؤال
عندك API بتسحب سعر الدولار من Service خارجية:
Your API
↓
Exchange Rate Service
وكل مرة المستخدم يفتح الصفحة:
GET /exchange-rate
السيستم يعمل:
Your API
↓
External Exchange Rate API
↓
USD Rate
في البداية:
External API = 200ms
وبعدين فجأة:
External API = 10 seconds
إيه اللي هيحصل؟
💥 المشكلة
وصل:
Request #1
↓
Wait 10s
بعده:
Request #2
↓
Wait 10s
بعده:
Request #3
↓
Wait 10s
وبعد شوية:
Hundreds of requests
↓
Waiting
↓
Connections / Threads / Memory occupied
↓
Your API becomes slower
↓
More requests pile up
↓
System starts failing
والمشكلة الأصلية أصلًا مش عندك.
المشكلة في:
External Dependency
لكن بسبب إنك مربوط بيها synchronous وبلا حدود، المشكلة بدأت تنتشر عندك.
وده مثال واضح على:
🔥 Cascading Failure
Microsoft تشرح إن dependency غير مستجيبة ممكن تستهلك موارد الـcaller مثل threads والـconnections لفترة طويلة، ومع استمرار الطلبات قد يحصل resource exhaustion ويؤثر ذلك على أجزاء أخرى من النظام. citeturn0search1turn0search9
🧠 أول قاعدة
External services are unreliable dependencies.
حتى لو:
Google
AWS
Stripe
Exchange Rate API
Payment Provider
Internal Microservice
مش معنى إنها شغالة دلوقتي إنها هتفضل شغالة بنفس السرعة دائمًا.
لازم تصميمك يفترض:
Timeout
Failure
Slow response
Rate limit
Partial outage
Network failure
⏱️ Timeout
أول سؤال:
هل هفضل مستني الـservice الخارجية للأبد؟
طبعًا لا.
نعمل:
Timeout
مثلاً:
External API timeout = 3s
يعني:
Request
↓
Call External Service
↓
Wait
↓
3 seconds
↓
Timeout
بدل:
10s
20s
30s
...
الـtimeout مش معناه إن الخدمة فشلت نهائيًا؛ معناه إن طلبك الحالي لم يعد يستحق الانتظار أكثر من الحد الذي حددته.
Microsoft توصي بوضع timeouts على كل outbound call قبل إضافة retry logic، لأن timeouts الطويلة تسمح بتراكم threads/connections، بينما القصيرة جدًا قد تسبب failures غير ضرورية. citeturn0search1
⚠️ Timeout لوحده مش كفاية
تخيل:
1000 requests
وكل واحد يعمل:
3 seconds timeout
لسه عندك:
1000 outbound attempts
فأنت منعت الانتظار للأبد، لكن لسه ممكن يكون عندك load ضخم.
هنا ندخل على:
Retry
Circuit Breaker
Bulkhead
Fallback
🔁 Retry مش دايمًا حل
أول ما request تفشل:
Retry
ثم:
Retry
ثم:
Retry
شكله منطقي.
لكن لو الـexternal service واقعة:
1000 users
×
3 retries
=
3000 additional attempts
يعني أنت بتعمل:
Failure
↓
Retry Storm
↓
More Load
↓
More Failure
↓
More Retries
↓
Service becomes even worse
Microsoft تحذر من retry strategies العدوانية، وتوضح أن retries من عدة requests بشكل متزامن قد تخلق load إضافيًا يمنع dependency من التعافي. citeturn0search1turn0search10
🧠 إمتى Retry ينفع؟
Retry مناسب أكثر عندما يكون الخطأ:
Transient
يعني مشكلة مؤقتة ممكن تختفي.
أمثلة محتملة:
503 Service Unavailable
429 Too Many Requests
Temporary network failure
Transient I/O failure
لكن:
400 Bad Request
401 Unauthorized
403 Forbidden
404 Not Found
غالبًا retry مش هيحل المشكلة.
Microsoft توصي بإعادة المحاولة فقط عندما يكون failure transient ومن المحتمل أن تنجح العملية عند إعادتها. citeturn0search1
📈 Exponential Backoff
بدل:
Retry immediately
Retry immediately
Retry immediately
نعمل:
Attempt 1
↓
wait 1s
Attempt 2
↓
wait 2s
Attempt 3
↓
wait 4s
يعني تقريبًا:
delay = base × 2^attempt
وده:
Exponential Backoff
الفكرة:
متهاجمش dependency وهي بتحاول تتعافى.
Microsoft توصي باستخدام exponential backoff في retry strategies بدل retry intervals الثابتة، خصوصًا مع أعداد كبيرة من الطلبات. citeturn0search1turn0search10
🎲 Jitter
لكن فيه مشكلة تانية.
تخيل مليون request فشلوا في نفس اللحظة.
وكلهم عملوا:
wait 1s
بعد ثانية:
💥 1,000,000 requests
وده ممكن يعمل:
Thundering Herd
عشان كده بنضيف:
Jitter
يعني randomization للـdelay:
Request A → 0.8s
Request B → 1.3s
Request C → 1.7s
Request D → 2.1s
بدل ما كلهم يرجعوا في نفس اللحظة.
مثال conceptually:
delay = exponentialBackoff + randomJitter
وفي الأنظمة الكبيرة، jitter مهم جدًا لتوزيع retries بدل تجميعها في موجات.
💰 Retry Budget
فيه غلطة تانية:
كل request عنده:
3 retries
فتقول:
خلاص أنا مأمن نفسي.
لا.
لو عندك:
10,000 requests
×
3 retries
ممكن يكون عندك:
30,000 retry attempts
حتى لو كل request ملتزم بالـpolicy.
عشان كده ممكن تحط:
Retry Budget
مثلاً conceptually:
Maximum retries against dependency:
60 / minute
لو الـbudget خلص:
Fail fast
بدل:
Keep retrying
Microsoft تذكر أن per-request retry limits وحدها لا تمنع aggregate retry overload، وتقترح retry budget على مستوى العملية/service للحد من إجمالي retry load. citeturn0search1
⚡ Circuit Breaker
دلوقتي لو الـexternal service واقعة تمامًا:
هل كل request جديد يجرب؟
لا.
هنا يظهر:
🔌 Circuit Breaker
الفكرة شبه circuit breaker في الكهرباء.
بدل ما تفضل تضغط على dependency:
Failure
Failure
Failure
Failure
Failure
النظام يقول:
واضح إن الخدمة واقعة، خلينا نوقف المحاولات مؤقتًا.
🟢 Closed
الحالة الطبيعية:
Your API
↓
External Service
الطلبات تعدي.
🔴 Open
بعد threshold معين:
Failures
↓
Threshold exceeded
↓
Circuit OPEN
دلوقتي:
Request
↓
Circuit Breaker
↓
FAIL FAST
من غير ما تكلم الـexternal service أصلًا.
Microsoft توضح أن Circuit Breaker يمنع الاستمرار في استدعاء dependency يُرجح أن تكون فاشلة، وبالتالي يقلل هدر الموارد والضغط على dependency. citeturn0search9
🟡 Half-Open
بعد فترة:
30s
النظام يسمح بعدد محدود جدًا من requests للتجربة.
Circuit
↓
Half-Open
↓
Probe Request
لو نجح:
Half-Open
↓
Closed
لو فشل:
Half-Open
↓
Open
فكرة الـHalf-Open مهمة لأنها تمنع إن service بدأت تتعافى من إنها تتفاجئ بكمية ضخمة من requests مرة واحدة. citeturn0search9
🧩 Circuit Breaker ≠ Retry
مهم جدًا:
Retry
يقول:
العملية ممكن تنجح لو جربنا تاني.
أما:
Circuit Breaker
فيقول:
واضح إن dependency غالبًا واقعة، متحاولش دلوقتي.
وممكن تستخدم الاثنين معًا.
Microsoft توضح أن الاثنين مختلفان ويمكن دمجهما: retry للتعامل مع transient failures، وcircuit breaker لمنع الاستمرار في استدعاء dependency عندما تكون فرص النجاح منخفضة. citeturn0search9
🛟 Fallback
طيب لو الـexchange service واقعة؟
هل لازم المستخدم يشوف:
500 Internal Server Error
مش دائمًا.
ممكن تعمل:
Fallback
مثلاً:
External API
↓
Failed
↓
Cached USD rate
فتعرض:
USD = 48.20
Last updated:
2 minutes ago
بدل ما الصفحة تقع بالكامل.
🗃️ Stale Data
دي من أقوى أفكار resilience في الـread-heavy systems.
لو البيانات:
Exchange Rate
Weather
Product Recommendations
News
Reviews
Analytics
مش لازم تكون لحظية 100%.
ممكن تستخدم:
Fresh Data
↓
Cache
↓
External API
ولو external service وقعت:
External API ❌
↓
Cached value ✅
وده يسمى أحيانًا:
Stale-While-Revalidate
أو graceful degradation حسب الـimplementation.
Microsoft تشير إلى أن cache يمكن في بعض السيناريوهات أن يساعد في الحفاظ على availability للبيانات المتكررة القراءة أثناء تعطل origin مؤقتًا. citeturn0search2
🚢 Bulkhead Isolation
دلوقتي نصل لأخطر سيناريو.
عندك:
100 Threads
والـExchange API علقت.
كلهم:
WAITING
وبالتالي:
Login
Profile
Checkout
Orders
Search
كلهم ممكن يتأثروا لو الموارد مشتركة.
الحل:
🚢 Bulkhead Pattern
الفكرة جاية من السفن.
السفينة فيها compartments.
لو جزء دخل فيه مياه:
Compartment A ❌
Compartment B ✅
Compartment C ✅
نفس الفكرة في السيستم.
نعزل الموارد:
Exchange API
↓
Dedicated Connection Pool / Concurrency Limit
Login
↓
Different Resources
Checkout
↓
Different Resources
لو Exchange API وقعت:
Exchange workload ❌
لكن:
Login ✅
Checkout ✅
Profile ✅
Microsoft توصي بالـBulkhead لعزل الموارد ومنع dependency واحدة من استنزاف connection pools أو threads أو CPU الخاص بباقي workloads. citeturn0search0turn0search5
🧱 Bulkhead مش لازم يكون Threads فقط
ممكن يكون:
Connection Pools
Semaphores
Thread Pools
Processes
Containers
Queues
CPU / Memory limits
يعني الفكرة الأساسية:
Resource isolation
مش مجرد:
"أعمل thread pool جديد."
Microsoft تذكر أن bulkheads يمكن تنفيذها باستخدام processes أو thread pools أو semaphores أو connection pools أو حتى separate containers/processes حسب مستوى العزل المطلوب. citeturn0search0
🔒 Idempotency
في نقطة مهمة جدًا مع retries.
تخيل إنك بتعمل:
POST /payments
والـserver نفذ العملية.
لكن response ضاع بسبب network failure.
الclient يقول:
Request failed
↓
Retry
فتعمل payment مرتين.
عشان كده:
Retrying a request is only safe when you understand its side effects.
Microsoft توصي بالنظر إلى idempotency قبل retrying، لأن العملية قد تكون نفذت بالفعل رغم أن response لم يصل، وإعادة إرسالها قد تسبب duplicate side effects. citeturn0search10
مثلاً:
Idempotency-Key: payment_123
والserver يضمن إن:
payment_123
تتنفذ مرة واحدة منطقيًا.
🚦 Rate Limiting
ممكن dependency تقول:
100 requests/sec
وأنت عندك:
1000 requests/sec
حتى لو service شغالة، أنت ممكن تقتلها.
عشان كده:
Rate Limit
+
Concurrency Limit
+
Retry Budget
كلهم أدوات للتحكم في الـload.
Microsoft تضع rate limiting ضمن patterns التي تساعد على منع unbounded retry-on-error وتقليل الضغط على dependencies. citeturn0search2
⏱️ Timeouts على كل Network Boundary
لو عندك:
Frontend
↓
API Gateway
↓
Service A
↓
Service B
↓
Exchange API
مينفعش تعتمد على timeout واحد وخلاص.
كل network boundary محتاجة سياسة واضحة.
مثلاً conceptually:
Gateway → API 5s
API → Service A 3s
Service A → B 2s
B → Exchange API 1s
لكن الأرقام مش قواعد ثابتة.
لازم تراعي:
End-to-end latency budget
+
Expected latency
+
Retries
+
Backoff
Microsoft تنبه إلى أن مجموع timeouts + retry delays قد يجعل overall latency أكبر بكثير من المتوقع، ويجب ضبطها ضمن الـSLO/end-to-end latency requirement. citeturn0search1
🧠 نقطة مهمة جدًا: الـTimeout مش Cancellation دائمًا
لما تعمل:
Timeout = 3 seconds
ده لا يعني بالضرورة إن الـremote server وقف الشغل.
ممكن يحصل:
Your API
↓ request
External Service
↓
Your API timeout
بينما:
External Service
↓
still processing
عشان كده لازم تفهم semantics الخاصة بالclient/library والـprotocol.
وده مهم جدًا في العمليات اللي لها side effects.
🏗️ Architecture
flowchart LR
U["Users"]
API["Your API"]
RL["Rate / Concurrency Limit"]
CB["Circuit Breaker"]
TO["Timeout"]
RETRY["Retry + Backoff + Jitter"]
CACHE[("Cache")]
EXT["External Exchange API"]
FALL["Fallback"]
OBS["Metrics / Logs / Traces"]
U --> API
API --> RL
RL --> CB
CB --> TO
TO --> RETRY
RETRY --> EXT
API --> CACHE
EXT --> CACHE
CB -. "Open / Failure" .-> FALL
TO -. "Timeout" .-> FALL
FALL --> CACHE
API --> OBS
CB --> OBS
RETRY --> OBS
🔄 Flow طبيعي
Request
↓
Rate Limit
↓
Circuit Closed?
↓
Timeout
↓
Call External API
↓
Success
↓
Return Response
🔥 Flow وقت الـFailure
Request
↓
Rate Limit
↓
Circuit Closed?
↓
Timeout
↓
External API ❌
↓
Retry?
↓
Backoff + Jitter
↓
Retry
↓
Repeated failures
↓
Circuit OPEN
↓
Fail Fast / Fallback
📦 Production Example
افترض:
External API p95 = 200ms
وفجأة:
p95 = 10s
ممكن يكون عندك policy مثل:
Timeout:
1-3s
Max Retries:
1-2
Backoff:
Exponential
Jitter:
Enabled
Circuit:
Failure-rate based
Half-Open:
Limited probes
Fallback:
Cached rate
Concurrency:
Bounded
Retry Budget:
Global / dependency-level
الأرقام دي أمثلة وليست configuration جاهزة للإنتاج.
الـcorrect values لازم تتحدد من:
SLO
+
Dependency SLA
+
Traffic
+
Latency distribution
+
Business criticality
📊 Monitoring
لو أنت عامل كل patterns دي ومش بتراقبها:
أنت بتخمن.
لازم تراقب على الأقل:
External Dependency
Latency
Error Rate
Timeout Rate
429 Rate
5xx Rate
Availability
Your Service
RPS
p50
p95
p99
CPU
Memory
Thread Pool
Connection Pool
Queue Depth
Resilience
Retry Count
Retry Rate
Retry Budget
Circuit State
Circuit Open Count
Fallback Rate
Bulkhead Rejections
Timeout Count
والأهم:
Which dependency caused the failure?
🔭 Distributed Tracing
في system كبير:
Request
↓
API
↓
Exchange Service
↓
Database
لو response بقى:
5 seconds
مش كفاية تقول:
API slow
عايز تعرف:
API processing = 50ms
External call = 4.8s
DB = 100ms
هنا عرفت الـbottleneck الحقيقي.
عشان كده:
Logs
+
Metrics
+
Traces
ثلاثة أجزاء أساسية من observability.
⚠️ Failure Scenarios
1. External API Slow
Timeout
+
Bounded concurrency
+
Fallback
2. External API Down
Circuit Breaker
+
Fallback
3. Temporary 503
Limited Retry
+
Exponential Backoff
+
Jitter
4. 429
Respect Retry-After
+
Backoff
+
Rate limiting
Microsoft توصي باتباع Retry-After عندما يكون موجودًا، لأنه يعكس توقيت التعافي الذي يحدده الـserver نفسه. citeturn0search1
5. Retry Storm
Retry Budget
+
Circuit Breaker
+
Jitter
6. Exchange API Consumes All Connections
Bulkhead
+
Connection Pool Isolation
+
Concurrency Limit
7. Non-Critical Feature Depends on External API
Fallback
+
Cached Data
+
Graceful Degradation
❌ Common Wrong Answers
❌ "هعمل Retry 3 مرات"
السؤال:
هل الخطأ transient؟
هل العملية idempotent؟
هل عندك backoff؟
هل عندك retry budget؟
هل عندك circuit breaker؟
❌ "هعمل Timeout وخلاص"
Timeout يمنع الانتظار الطويل، لكنه لا يمنع:
retry storm
resource exhaustion
dependency overload
❌ "هعمل Circuit Breaker وخلاص"
Circuit breaker ممتاز، لكنه مش بديل عن:
Timeout
Retry policy
Fallback
Bulkhead
Monitoring
❌ "هعمل Cache"
ممكن يكون ممتاز.
لكن اسأل:
How stale can the data be?
لو سعر الدولار لازم يكون real-time، stale data ممكن تكون مشكلة business.
❌ "هكبر السيرفر"
لو المشكلة:
External dependency
زيادة CPU عندك مش هتخلي الـexternal service أسرع.
❌ "هستخدم async"
async/await ممتاز لتجنب blocking threads في كثير من I/O workloads، لكن مش resilience strategy لوحده.
يعني:
async
مش معناها:
Timeout
Retry
Circuit Breaker
❌ "هعمل retry لكل errors"
غلط.
مثلاً:
400
401
403
404
غالبًا retry مش هيحل السبب.
🧠 السؤال الأصعب
لو عندك:
Exchange API
Inventory API
Reviews API
Shipping API
Discount API
وصفحة واحدة محتاجة الخمسة.
هل dependency واحدة تقع = الصفحة كلها تقع؟
مش بالضرورة.
ممكن تعمل:
Critical
↓
Must succeed
Non-critical
↓
Fallback / cached / omitted
مثلاً:
Product
↓
Inventory ❌
↓
Show:
"Availability temporarily unavailable"
بدل:
500
وده اسمه:
Graceful Degradation
🎯 Interview Answer
لو الـinterviewer قال:
"عندك API بتعتمد على external service بقت بطيئة جدًا، هتعمل إيه؟"
ممكن تجاوب:
"أول حاجة مش هخلي الـrequest يستنى بلا حدود؛ هحط timeout واضح على الـoutbound call. لو الفشل transient ومناسب للعملية، ممكن أستخدم عدد retries محدود مع exponential backoff وjitter، وأراعي Retry-After لو الـprovider بيرجعه. لكن مش هعتمد على retry فقط، لأن concurrent retries ممكن تعمل retry storm وتزود الضغط على الـdependency. لذلك هستخدم retry budget، ومع استمرار failures هفتح Circuit Breaker عشان أفشل بسرعة وأدي الخدمة فرصة تتعافى. لو البيانات تسمح، هستخدم fallback زي cached/stale value أو graceful degradation. وهعزل الموارد الخاصة بالـdependency باستخدام bulkhead/concurrency أو connection-pool isolation عشان تعطلها ما يستهلكش resources الخاصة بالـlogin أو checkout. وأخيرًا هراقب latency، errors، timeouts، retries، circuit state، connection pools والـfallback rate، وأستخدم tracing عشان أعرف dependency اللي مسببة المشكلة."
لو سألك:
"ليه مش Retry بس؟"
تقول:
"لأن retry من مئات أو آلاف requests ممكن يحول failure محلي إلى retry storm، ويضغط على dependency المتعطلة ويؤدي إلى cascading failure."
لو سألك:
"ليه Circuit Breaker؟"
تقول:
"عشان لما dependency تكون واضحة إنها failing، بدل ما كل request يحاول ويتحمل timeout، أفشل بسرعة وأمنع إرسال traffic إضافي لحد ما الـdependency تبدأ تتعافى."
لو سألك:
"إيه Bulkhead؟"
تقول:
"Resource isolation. أعزل resources المستخدمة في dependency معينة، زي connection pool أو concurrency limit، عشان لو dependency وقعت ما تستهلكش resources الخاصة بباقي النظام."
🧠 Mental Model
احفظها بالشكل ده:
External Dependency
↓
TIMEOUT
↓
Is failure transient?
↓
RETRY
↓
Backoff + Jitter
↓
Retry Budget
↓
Still failing?
↓
CIRCUIT BREAKER
↓
FAIL FAST
↓
FALLBACK / CACHE
↓
GRACEFUL DEGRADATION
وفي نفس الوقت:
BULKHEAD
+
CONCURRENCY LIMIT
+
CONNECTION POOL ISOLATION
عشان المشكلة متطلعش بره الـdependency.
🏆 Production Checklist
Timeouts
- Every outbound call has a timeout
- Timeout fits the latency budget
- Overall request budget considers retries
- Understand cancellation semantics
Retry
- Retry only transient failures
- Bounded retry count
- Exponential backoff
- Jitter
- Respect Retry-After
- Retry budget
- Check idempotency
Circuit Breaker
- Failure threshold
- Open state
- Cooldown
- Half-open probes
- Recovery logic
- Metrics
Bulkhead
- Per-dependency concurrency limits
- Connection pool isolation
- Critical workload isolation
- Resource limits
Fallback
- Cached data where safe
- Stale-data policy
- Graceful degradation
- Clear user experience
Observability
- Latency
- Error rate
- Timeout rate
- Retry rate
- Circuit state
- Pool utilization
- CPU / Memory
- Distributed tracing
📚 Official Sources
Microsoft Azure Architecture Center
-
Bulkhead Pattern
https://learn.microsoft.com/en-us/azure/architecture/patterns/bulkhead -
Circuit Breaker Pattern
https://learn.microsoft.com/en-us/azure/architecture/patterns/circuit-breaker -
Retry Pattern
https://learn.microsoft.com/en-us/azure/architecture/patterns/retry -
Transient Fault Handling
https://learn.microsoft.com/en-us/azure/architecture/best-practices/transient-faults -
Reliability Design Patterns
https://learn.microsoft.com/en-us/azure/well-architected/reliability/design-patterns -
Microservices Design Patterns
https://learn.microsoft.com/en-us/azure/architecture/microservices/design/patterns
<div align="center">
🚀 الخلاصة
External Service Failure مش مشكلة API واحدة.
هي ممكن تتحول إلى:
Slow Dependency
↓
Timeouts
↓
Resource Exhaustion
↓
Retries
↓
Retry Storm
↓
Cascading Failure
↓
System Outage
لكن بالتصميم الصح:
Timeout
+
Bounded Retry
+
Exponential Backoff
+
Jitter
+
Retry Budget
+
Circuit Breaker
+
Fallback
+
Bulkhead
+
Observability
تقدر تخلي:
</div>External service تقع... والسيستم بتاعك يفضل واقف. 💪
منشورات مقترحة
مشاريع ذات صلة

واجهة متجر إلكتروني متجاوبة مبنية بـ Next.js و React لتصفح الأجهزة التقنية وإدارة السلة ومراحل الشراء.