Fixed Delay
Fixed Delay Retry
Fixed delay waits the same amount of time between each retry attempt.
Implementation
def retry_fixed_delay(func, max_retries=3, delay=5):
"""Retry with fixed delay"""
for attempt in range(max_retries):
try:
return func()
except Exception as e:
if attempt < max_retries - 1:
time.sleep(delay) # Fixed delay
else:
raise e
# Usage
result = retry_fixed_delay(
lambda: call_external_api(),
max_retries=3,
delay=5 # Always 5 seconds
)
Retry Timeline
Attempt 1: Fail → wait 5s
Attempt 2: Fail → wait 5s
Attempt 3: Fail → raise exception
Total time: ~10 seconds
Pros and Cons
| Pros | Cons |
|---|---|
| Simple to implement | May overwhelm failing service |
| Predictable timing | Not adaptive to load |
| Easy to understand | May cause thundering herd |
When to Use
- Simple retry scenarios
- Known, stable failure patterns
- When service recovers quickly
- Testing and development
Exponential Backoff
Exponential Backoff
Exponential backoff increases the delay between retries exponentially.
Formula
delay = base_delay * 2^attempt
Attempt 0: 1 second
Attempt 1: 2 seconds
Attempt 2: 4 seconds
Attempt 3: 8 seconds
Attempt 4: 16 seconds
Implementation
def retry_exponential_backoff(func, max_retries=5, base_delay=1, max_delay=60):
"""Retry with exponential backoff"""
for attempt in range(max_retries):
try:
return func()
except Exception as e:
if attempt < max_retries - 1:
delay = min(base_delay * (2 ** attempt), max_delay)
time.sleep(delay)
else:
raise e
# Timeline:
# Attempt 1: Fail → wait 1s
# Attempt 2: Fail → wait 2s
# Attempt 3: Fail → wait 4s
# Attempt 4: Fail → wait 8s
# Attempt 5: Fail → raise exception
With Jitter
import random
def retry_exponential_backoff_jitter(func, max_retries=5, base_delay=1):
"""Exponential backoff with jitter"""
for attempt in range(max_retries):
try:
return func()
except Exception as e:
if attempt < max_retries - 1:
delay = base_delay * (2 ** attempt)
jitter = random.uniform(0, delay * 0.1) # 10% jitter
time.sleep(delay + jitter)
else:
raise e
Why Jitter?
Without jitter:
- All clients retry at same time
- Creates thundering herd
With jitter:
- Clients retry at slightly different times
- Smoother load distribution
Comparison
| Strategy | Pattern | Use Case |
|---|---|---|
| Fixed | 1, 1, 1, 1 | Simple, stable |
| Linear | 1, 2, 3, 4 | Moderate backoff |
| Exponential | 1, 2, 4, 8 | Aggressive backoff |
| Exponential + Jitter | 1±0.1, 2±0.2, 4±0.4 | Best practice |
Jitter
Jitter in Retries
Types of Jitter
1. Full Jitter:
delay = random(0, base * 2^attempt)
Range: [0, max]
2. Equal Jitter:
delay = base * 2^attempt / 2 + random(0, base * 2^attempt / 2)
Range: [half, max]
3. Decorrelated Jitter:
delay = min(max, random(base, prev_delay * 3))
Based on previous delay
Implementation
import random
def full_jitter(base, attempt, max_delay=60):
"""Full jitter: random between 0 and max"""
delay = base * (2 ** attempt)
return min(random.uniform(0, delay), max_delay)
def equal_jitter(base, attempt, max_delay=60):
"""Equal jitter: half fixed, half random"""
delay = base * (2 ** attempt)
half = delay / 2
return min(half + random.uniform(0, half), max_delay)
def decorrelated_jitter(base, attempt, prev_delay=0, max_delay=60):
"""Decorrelated: based on previous delay"""
if prev_delay == 0:
return base
delay = min(max_delay, random.uniform(base, prev_delay * 3))
return delay
Why Jitter Matters
Scenario: 100 clients retry simultaneously
Without jitter:
- All retry at T+1, T+2, T+4
- Peak load at each retry point
- May cause cascading failures
With jitter:
- Retries spread across time window
- Smoother load distribution
- Better system stability
AWS Recommended Algorithm
def aws_exponential_backoff_jitter(attempt, base=1, max_delay=20):
"""AWS recommended algorithm"""
delay = min(max_delay, base * (2 ** attempt))
return random.uniform(0, delay)
Circuit Breaker Integration
Circuit Breaker + Retry Integration
Pattern
Circuit Breaker + Retry:
1. Closed State:
- Normal operation
- Retries enabled
- Count failures
2. Open State:
- Failure threshold exceeded
- Retries disabled
- Fast-fail immediately
3. Half-Open State:
- After cooldown
- Allow one request
- Test if service recovered
Implementation
class CircuitBreakerRetry:
def __init__(self, failure_threshold=5, recovery_timeout=30):
self.failure_count = 0
self.failure_threshold = failure_threshold
self.recovery_timeout = recovery_timeout
self.state = 'closed'
self.last_failure_time = None
def call(self, func, max_retries=3, base_delay=1):
"""Call with circuit breaker and retry"""
if self.state == 'open':
if time.time() - self.last_failure_time > self.recovery_timeout:
self.state = 'half-open'
else:
raise CircuitOpenError('Circuit is open')
for attempt in range(max_retries):
try:
result = func()
self._on_success()
return result
except Exception as e:
if attempt < max_retries - 1:
delay = base_delay * (2 ** attempt)
time.sleep(delay)
else:
self._on_failure()
raise
def _on_success(self):
self.failure_count = 0
self.state = 'closed'
def _on_failure(self):
self.failure_count += 1
self.last_failure_time = time.time()
if self.failure_count >= self.failure_threshold:
self.state = 'open'
Integration Flow
Request → Circuit Breaker Check
↓
[Open] → Fail fast
[Closed] → Retry Logic
↓
Attempt → Success → Return
Attempt → Fail → Wait → Retry
All Fail → Circuit Open → Fail
Best Practices
- Use exponential backoff with jitter as default
- Set max retry limit to prevent infinite loops
- Integrate circuit breaker for failing services
- Log retry attempts for debugging
- Monitor retry rates for anomalies
Practice Problems
Design a scalable Retry Strategies system. Cover high-level architecture, data model, and API design.
Solution
// Complete system design:
// - Functional + Non-functional requirements
// - Capacity estimation
// - Data model (SQL/NoSQL choice)
// - API endpoints
// - Component architecture
// - Scaling strategy
// - Monitoring & reliabilityHow would you scale Retry Strategies to handle 10x the current load? Identify bottlenecks and solutions.
Solution
// Scaling approach:
// 1. Load balancing
// 2. Database sharding/replication
// 3. Cache layer (Redis)
// 4. CDN for static assets
// 5. Async processing (queues)
// 6. Microservices decompositionAnalyze potential failure modes for Retry Strategies and design mitigation strategies.
Solution
// Failure mitigation:
// 1. Redundancy (multi-AZ)
// 2. Circuit breakers
// 3. Retry with backoff
// 4. Dead letter queues
// 5. Health checks
// 6. Graceful degradationQuiz
1. What is exponential backoff?
2. Why add jitter to retries?
3. What does circuit breaker do during failures?
4. When should you use fixed delay retry?
5. What is the circuit breaker half-open state?
Flashcards
Question
Exponential backoff formula?
Click to reveal answer
Answer
delay = base_delay * 2^attempt (1s, 2s, 4s, 8s, 16s...)
Question
Why add jitter to retries?
Click to reveal answer
Answer
Prevents thundering herd by randomizing retry times so clients don't retry simultaneously
Question
Circuit breaker states?
Click to reveal answer
Answer
Closed (normal), Open (failing, fast-fail), Half-Open (testing recovery)
Question
Fixed vs exponential backoff?
Click to reveal answer
Answer
Fixed: constant delay, simple. Exponential: increasing delay, prevents overwhelming failing service.
Question
When use circuit breaker with retry?
Click to reveal answer
Answer
When calling external services - circuit breaker prevents cascading failures, retry handles transient errors
Revision Notes
Key Takeaways
- 1.Exponential backoff with jitter is the recommended default
- 2.Jitter prevents thundering herd by randomizing retry times
- 3.Circuit breaker stops calling failing services
- 4.Always set max retry limit to prevent infinite loops
- 5.Monitor retry rates for anomalies
Interview Tips
- •Know exponential backoff formula and jitter
- •Explain why jitter prevents thundering herd
- •Discuss circuit breaker states and transitions
- •Give examples of when to use each strategy
Cheat Sheet
Cheat Sheet: Retry Strategies
Fixed Delay
- Same delay between retries
- Simple but may overwhelm
Exponential Backoff
- delay = base * 2^attempt
- Prevents overwhelming
- Use max_delay cap
Jitter
- Randomize delay
- Prevents thundering herd
- AWS recommends full jitter
Circuit Breaker
- Closed: Normal + retry
- Open: Fast-fail
- Half-Open: Test recovery
Best Practices
- Exponential + Jitter default
- Set max retries
- Integrate circuit breaker