Resilence

Resilence

Part 3: Resilience Patterns - Timeout, Retry, Circuit Breaker, Fallback - Complete Guide

Part 3: Resilience Patterns

The Complete Resilience Stack - Timeout, Retry, Circuit Breaker, Fallback

📌 Quick Summary:
The resilience stack has 4 layers applied in order: Timeout (don't wait forever) → Retry (try again if temporary) → Circuit Breaker (stop calling failing services) → Fallback (provide alternative response). Each layer protects against specific failure scenarios.

The Resilience Stack (Order of Protection)

🔷 Level 1: TIMEOUT - "Don't wait forever"

🔷 Level 2: RETRY - "Try again if temporary"

🔷 Level 3: CIRCUIT BREAKER - "Stop calling failing service"

🔷 Level 4: FALLBACK - "Provide alternative response"

1. Timeout Pattern

What is Timeout?

A timeout limits the maximum time a client will wait for a response from a service before giving up.

📖 Real-World Example: You call a restaurant for delivery. If they don't answer after 30 seconds, you hang up and try another restaurant. You don't wait forever.

Types of Timeouts

TypeDescriptionExample
Connection TimeoutTime to establish connection3 seconds to connect to server
Read TimeoutTime to wait for response after connection5 seconds to read response
Request TimeoutTotal time for entire operation10 seconds max for whole call

Code Example

// Java Spring Boot - Setting Timeouts
@Configuration
public class TimeoutConfig {
    
    @Bean
    public RestTemplate restTemplate() {
        return new RestTemplateBuilder()
            .setConnectTimeout(Duration.ofSeconds(3))   // Connection timeout
            .setReadTimeout(Duration.ofSeconds(5))      // Read timeout
            .build();
    }
}

// Using WebClient with timeouts
webClient.get()
    .uri("/api/payment")
    .timeout(Duration.ofSeconds(5))
    .retrieve()
    .bodyToMono(PaymentResponse.class)
    .onErrorResume(TimeoutException.class, e -> {
        log.error("Payment service timeout!");
        return Mono.just(fallbackResponse());
    });

Best Practices

  • Every remote call MUST have a timeout - no exceptions
  • Set timeouts based on Service Level Agreements (SLAs)
  • Use different timeouts for different operations (fast vs slow operations)
  • Monitor timeout occurrences - they indicate problems

2. Retry Pattern

What is Retry?

Automatically retry a failed operation if the failure is likely temporary (network blip, momentary overload).

📖 Real-World Example: You try to call a friend. The line is busy. You wait 2 seconds, try again. Still busy. Wait 4 seconds, try again. Connected!

Retry Algorithms

A. Fixed Delay Retry

Attempt 1: Fail → Wait 1 second
Attempt 2: Fail → Wait 1 second
Attempt 3: Success ✓

B. Exponential Backoff (Recommended)

Attempt 1: Fail → Wait 100ms
Attempt 2: Fail → Wait 200ms (2x)
Attempt 3: Fail → Wait 400ms (4x)
Attempt 4: Fail → Wait 800ms (8x)
Attempt 5: Success ✓

Formula: wait = base * (2 ^ attempt) + jitter

C. Exponential Backoff with Jitter (Best)

Add random variation to prevent "thundering herd" problem

Attempt 1: 100ms + random(0-50ms)
Attempt 2: 200ms + random(0-50ms)
Attempt 3: 400ms + random(0-50ms)

Result: 100 services retrying at slightly different times

When to Retry vs When NOT to Retry

ScenarioRetry?Why
Network timeout✅ YESTemporary network issue
HTTP 503 Service Unavailable✅ YESService temporarily overloaded
HTTP 500 Internal Error✅ MAYBECould be transient or permanent
HTTP 400 Bad Request❌ NOClient error - retry won't fix
HTTP 401 Unauthorized❌ NOAuth issue - retry won't fix
HTTP 404 Not Found❌ NOResource doesn't exist
Non-idempotent operation (POST)⚠️ CAREFULCould create duplicate data
⚠️ Important: Only retry IDEMPOTENT operations (GET, PUT, DELETE) or use idempotency keys for POST requests. Otherwise, you might create duplicate orders or payments!

3. Circuit Breaker Pattern

What is Circuit Breaker?

A circuit breaker monitors failures and "trips" (opens) when failures exceed a threshold, preventing further calls to a failing service. After a timeout, it allows test calls to check recovery.

📖 Real-World Example: Your home circuit breaker trips when there's an electrical fault. You don't keep flipping the switch - you fix the problem first. The circuit breaker protects your house from electrical fires.

Three States of Circuit Breaker

┌─────────────────────────────────────────────────────────┐
│                                                         │
│    ┌─────────┐     Success      ┌─────────┐           │
│    │         │ ───────────────► │         │           │
│    │  CLOSED │                   │  OPEN   │           │
│    │ (Normal)│ ◄─────────────── │  (Failing)│          │
│    └────┬────┘    5 failures    └────┬────┘           │
│         │                            │                 │
│         │                            │ Timeout passes  │
│         │                            ▼                 │
│         │                      ┌─────────┐            │
│         │                      │ HALF-   │            │
│         └─────────────────────►│  OPEN   │            │
│              Success            │ (Test)  │            │
│                                └─────────┘            │
│                                    │                   │
│                                    │ Failure           │
│                                    ▼                   │
│                                ┌─────────┐            │
│                                │  OPEN   │            │
│                                └─────────┘            │
└─────────────────────────────────────────────────────────┘

State Explanations

StateMeaningBehavior
CLOSEDNormal operationCalls go through. Failures are counted. When failure threshold reached → OPEN
OPENService is failingCalls fail immediately without attempting. After timeout period → HALF-OPEN
HALF-OPENTesting recoveryLimited calls allowed. Success → CLOSED. Failure → OPEN

Code Example (Resilience4j)

// Configure Circuit Breaker
CircuitBreakerConfig config = CircuitBreakerConfig.custom()
    .failureRateThreshold(50)                    // 50% failures opens circuit
    .slidingWindowSize(10)                       // Check last 10 calls
    .waitDurationInOpenState(Duration.ofSeconds(30))  // Stay open for 30 sec
    .permittedNumberOfCallsInHalfOpenState(3)    // Allow 3 test calls
    .recordException(e -> e instanceof TimeoutException)
    .build();

CircuitBreaker breaker = CircuitBreaker.of("payment-service", config);

// Use Circuit Breaker
Supplier<Payment> decoratedSupplier = CircuitBreaker
    .decorateSupplier(breaker, () -> paymentClient.process(request));

CompletableFuture.supplyAsync(decoratedSupplier)
    .thenAccept(payment -> handleSuccess(payment))
    .exceptionally(e -> handleFailure(e));

Real-World Configuration Example

payment-service:
  circuit-breaker:
    failure-threshold: 5 failures in 10 seconds
    timeout: 30 seconds (how long to stay OPEN)
    half-open-calls: 3 (test calls to check recovery)
    
Order of events:
1. Payment service fails 5 times in 10 seconds
2. Circuit OPENS for 30 seconds (no calls get through)
3. After 30 sec, circuit goes HALF-OPEN
4. 3 test calls allowed
5. If test succeeds → CLOSED (normal)
6. If test fails → OPEN again (wait another 30 sec)

4. Fallback Pattern

What is Fallback?

When a primary operation fails, provide an alternative response or action instead of showing an error to the user.

📖 Real-World Example: Your favorite restaurant is closed. Instead of starving, you order from your second favorite restaurant (fallback). Or you eat leftovers from the fridge (cached data fallback).

Types of Fallbacks

A. Static Fallback (Default Response)

If payment service fails:
    Return: {"status": "PENDING", "message": "Payment being processed"}
    
User doesn't see an error, just knows it's in progress.

B. Cached Fallback (Stale Data)

If product catalog service fails:
    Return last cached product data (even if slightly stale)
    
User still sees products, just not the absolute latest prices.

C. Secondary Provider Fallback

If Primary Payment Gateway fails:
    Switch to Backup Payment Gateway
    
User doesn't even know one provider failed.

D. Degraded Functionality Fallback

If Recommendation Service fails:
    Show default/top products instead of personalized recommendations
    
Core functionality (buying) works. Nice-to-have features degrade.

E. Async Retry with Queue

If Email Service fails:
    Add to retry queue instead of failing immediately
    Process later when service recovers
    
User gets email eventually, not instantly.

Code Example (Spring Cloud Circuit Breaker)

@Service
public class PaymentService {
    
    @CircuitBreaker(name = "paymentService", fallbackMethod = "paymentFallback")
    public PaymentResponse processPayment(PaymentRequest request) {
        return paymentClient.process(request);
    }
    
    // Fallback method - same signature + Throwable parameter
    public PaymentResponse paymentFallback(PaymentRequest request, Throwable t) {
        log.warn("Payment failed, using fallback. Error: {}", t.getMessage());
        
        // Option 1: Default response
        return new PaymentResponse("PENDING", "Payment queued");
        
        // Option 2: Return cached data
        // return cache.get(request.getOrderId());
        
        // Option 3: Add to queue for later
        // retryQueue.add(request);
        // return new PaymentResponse("QUEUED", "Will retry");
    }
}

Putting It All Together: Complete Payment Flow Example

┌─────────────────────────────────────────────────────────────────────┐
│                    COMPLETE RESILIENCE FLOW                          │
│                                                                      │
│  User clicks "Pay Now"                                               │
│         │                                                            │
│         ▼                                                            │
│  ┌─────────────────┐                                                │
│  │ 1. TIMEOUT      │  ← 2 seconds max wait                          │
│  │    (2 seconds)  │                                                │
│  └────────┬────────┘                                                │
│           │ (Timeout occurs)                                        │
│           ▼                                                          │
│  ┌─────────────────┐                                                │
│  │ 2. RETRY        │  ← Exponential backoff                         │
│  │    (Max 3 times)│    100ms → 200ms → 400ms                       │
│  └────────┬────────┘                                                │
│           │ (All retries fail)                                      │
│           ▼                                                          │
│  ┌─────────────────┐                                                │
│  │ 3. CIRCUIT      │  ← After 5 failures in 10 seconds              │
│  │    BREAKER      │    Circuit OPENS for 30 seconds                │
│  └────────┬────────┘                                                │
│           │                                                          │
│           ▼                                                          │
│  ┌─────────────────┐                                                │
│  │ 4. FALLBACK     │  ← Return "Payment Pending"                    │
│  │                 │    Add to retry queue                          │
│  └─────────────────┘                                                │
│                                                                      │
│  User sees: "Your payment is being processed. We'll notify you."    │
│  (No error message!)                                                │
└─────────────────────────────────────────────────────────────────────┘

Quick Comparison: All Resilience Patterns

PatternWhat it doesWhen it triggersWhat user seesReal-World Analogy
TimeoutLimits wait timeCall takes too longFast failure, not hangingHanging up after 30 seconds
RetryRe-attempts failed callTemporary failure (network, 5xx)May see slight delayRedialing a busy number
Circuit BreakerStops calling failing serviceRepeated failures (5+ in 10 sec)Fast failure, no waitingTripped electrical breaker
FallbackProvides alternative responseAll else failsGraceful degradation, not errorOrdering from backup restaurant

💡 Interview Power Phrases

"The resilience stack is timeout first, then retry, then circuit breaker, finally fallback. Each layer protects against a different type of failure."
"Timeouts prevent thread exhaustion. Without them, a single slow service can bring down your entire system."
"Exponential backoff with jitter prevents the thundering herd problem when retrying."
"Circuit breakers are not optional in production microservices. They prevent cascading failures."
"Fallbacks should always provide a degraded but functional experience, never just an error message."

📌 Key Takeaways

  • Timeout - EVERY remote call needs one. No exceptions.
  • Retry - Use exponential backoff + jitter. Only retry idempotent operations.
  • Circuit Breaker - Three states: CLOSED → OPEN → HALF-OPEN. Prevents cascading failures.
  • Fallback - Always provide graceful degradation. Never show raw errors to users.
  • Order matters - Apply in sequence: Timeout → Retry → Circuit Breaker → Fallback