Multi-Datacenter Circuit Breaker Patterns — Cross-Region Resilience Architecture
In this tutorial, you will learn about Multi. We cover key concepts, practical examples, and best practices to help you master this topic.
Multi-datacenter circuit breaker patterns extend circuit resilience across geographic regions by coordinating circuit states between data centers, routing traffic away from regions with open circuits, and aggregating health data for global visibility.
flowchart LR
US[US-East] -->|State Sync| Global[Global State Store]
EU[EU-West] -->|State Sync| Global
AP[AP-Southeast] -->|State Sync| Global
Global -->|Route| DNS[Geo DNS]
DNS -->|Healthy| US
DNS -->|Healthy| EU
DNS -->|Circuit Open| AP
style Global fill:#f90,color:#fff
What You'll Learn
- Region-aware circuit breaker state
- Cross-region state propagation
- Active-passive circuit coordination
- Geo-routing with circuit awareness
- Global circuit health aggregation
Why It Matters
A circuit breaker in one region should influence traffic routing decisions globally. If the US-East region's payment service circuit is open, traffic should be routed to EU-West or AP-Southeast. Without multi-datacenter coordination, each region operates in isolation, missing opportunities to route around failures.
Real-World Use
DodaTech operates in 5 AWS regions. Each region has local circuit breakers that sync state to a global etcd cluster. When the US-East payment circuit opens, the global DNS load balancer shifts traffic to EU-West within 30 seconds. The circuit health dashboard shows all regions with open circuits highlighted in red.
Region-Aware Circuit State
import time
import json
class MultiRegionCircuitState:
def __init__(self):
self.regions = {}
def update_region(self, region, service, state, timestamp=None):
if region not in self.regions:
self.regions[region] = {}
self.regions[region][service] = {
'state': state,
'timestamp': timestamp or time.time()
}
def get_global_state(self, service):
states = {}
for region, circuits in self.regions.items():
if service in circuits:
states[region] = circuits[service]
return states
def get_healthy_regions(self, service):
healthy = []
for region, circuits in self.regions.items():
if service in circuits:
entry = circuits[service]
age = time.time() - entry['timestamp']
if entry['state'] == 'CLOSED' and age < 60:
healthy.append(region)
return healthy
def should_route_to_region(self, region, service):
if region not in self.regions:
return False
if service not in self.regions[region]:
return True
entry = self.regions[region][service]
age = time.time() - entry['timestamp']
return entry['state'] == 'CLOSED' and age < 60
state = MultiRegionCircuitState()
state.update_region("us-east-1", "payment-service", "CLOSED", time.time())
state.update_region("eu-west-1", "payment-service", "OPEN", time.time())
state.update_region("ap-southeast-1", "payment-service", "CLOSED", time.time())
print(f"Healthy regions for payment: {state.get_healthy_regions('payment-service')}")
print(f"Route to us-east-1: {state.should_route_to_region('us-east-1', 'payment-service')}")
print(f"Route to eu-west-1: {state.should_route_to_region('eu-west-1', 'payment-service')}")
Expected output:
Healthy regions for payment: ['us-east-1', 'ap-southeast-1']
Route to us-east-1: True
Route to eu-west-1: False
Cross-Region State Propagation
import time
import json
import threading
class CrossRegionSync:
def __init__(self, region_name):
self.region = region_name
self.local_states = {}
self.remote_states = {}
self.lock = threading.Lock()
def set_local_state(self, service, state):
with self.lock:
entry = {
'region': self.region,
'service': service,
'state': state,
'timestamp': time.time()
}
self.local_states[service] = entry
return entry
def receive_remote_state(self, region, service, state, timestamp):
with self.lock:
key = f"{region}:{service}"
old = self.remote_states.get(key)
if old and old['timestamp'] >= timestamp:
return
self.remote_states[key] = {
'region': region,
'service': service,
'state': state,
'timestamp': timestamp
}
print(f"[{self.region}] Received: {region}/{service} = {state}")
def get_all_states(self, service):
results = []
with self.lock:
if service in self.local_states:
results.append(self.local_states[service])
for key, entry in self.remote_states.items():
if entry['service'] == service:
results.append(entry)
return sorted(results, key=lambda x: x['timestamp'], reverse=True)
sync_us = CrossRegionSync("us-east-1")
sync_eu = CrossRegionSync("eu-west-1")
us_entry = sync_us.set_local_state("payment-service", "OPEN")
sync_eu.receive_remote_state("us-east-1", "payment-service", us_entry['state'], us_entry['timestamp'])
sync_eu.set_local_state("payment-service", "CLOSED")
all_states = sync_eu.get_all_states("payment-service")
for s in all_states:
print(f" {s['region']}: {s['state']} (age: {time.time() - s['timestamp']:.0f}s)")
Expected output:
[eu-west-1] Received: us-east-1/payment-service = OPEN
eu-west-1: CLOSED (age: 0s)
us-east-1: OPEN (age: 0s)
Geo-Routing with Circuit Awareness
import time
import random
class CircuitAwareGeoRouter:
def __init__(self):
self.region_health = {}
self.default_region = "us-east-1"
def update_region_health(self, region, service, state):
if region not in self.region_health:
self.region_health[region] = {}
self.region_health[region][service] = state
def get_best_region(self, service, preferred_region=None):
candidates = []
for region, services in self.region_health.items():
service_state = services.get(service, 'CLOSED')
if service_state == 'CLOSED':
candidates.append(region)
if not candidates:
return None
if preferred_region and preferred_region in candidates:
return preferred_region
return random.choice(candidates)
def get_routing_plan(self, service):
plan = {}
for region, services in self.region_health.items():
plan[region] = services.get(service, 'CLOSED')
best = self.get_best_region(service)
return {'regions': plan, 'recommended': best}
router = CircuitAwareGeoRouter()
router.update_region_health("us-east-1", "payment-service", "OPEN")
router.update_region_health("eu-west-1", "payment-service", "CLOSED")
router.update_region_health("ap-southeast-1", "payment-service", "CLOSED")
plan = router.get_routing_plan("payment-service")
print(f"Routing plan: {plan['regions']}")
print(f"Recommended region: {plan['recommended']}")
Expected output:
Routing plan: {'us-east-1': 'OPEN', 'eu-west-1': 'CLOSED', 'ap-southeast-1': 'CLOSED'}
Recommended region: eu-west-1
Common Mistakes
- Synchronous cross-region state propagation -- waiting for state confirmation from other regions before making routing decisions adds latency (100-300ms cross-region). Use asynchronous propagation with local state for fast decisions and reconcile conflicts later based on timestamps.
- No state staleness handling -- a circuit state from 5 minutes ago may be irrelevant. Attach timestamps to all state updates and reject updates older than a configurable threshold (e.g., 60 seconds for fast-changing, 300 seconds for slow-changing).
- Ignoring network partitions -- a Network Partition between regions causes stale state on both sides. Each region should make local decisions based on its own circuit state and treat missing remote state as "unknown" rather than "healthy."
- All traffic routed to one healthy region -- when two regions have open circuits and one is healthy, all traffic shifts to the healthy region, potentially overwhelming it. Implement capacity-aware routing that considers each region's remaining capacity.
- No region preference in routing -- blindly routing to any healthy region ignores latency costs. Route to the geographically closest healthy region first, then fall back to the next closest. Measure and prefer regions with lower latency.
Practice Questions
- How does multi-datacenter circuit breaker state differ from single-region?
- What happens during a cross-region network partition?
- How do you prevent overwhelming a single healthy region?
- What is the trade-off between state freshness and routing accuracy?
- How do you handle conflicting circuit states across regions?
Challenge
Build a multi-region circuit routing system: (1) 3 regions (us-east-1, eu-west-1, ap-southeast-1) each with local circuit breakers for 3 services (payment, inventory, notifications), (2) cross-region state sync via global etcd with 5-second polling, (3) state staleness threshold of 60 seconds, (4) geo-DNS routing that redirects traffic away from regions with open circuits, (5) capacity-aware routing that distributes rerouted traffic proportionally across healthy regions, (6) latency-based preference: route to closest healthy region first, (7) dashboard showing per-region circuit states and routing decisions.
FAQ
Mini Project
Build a multi-region circuit breaker system: (1) 3 simulated regions each with local circuit breakers for payment, inventory, and notification services, (2) global state store (simulated etcd) for cross-region state sharing, (3) asynchronous state propagation: each region publishes state changes to the global store every 5 seconds, (4) geo-routing: DNS-simulated router that checks global state before routing, (5) capacity-aware routing: limit 60% of total traffic to any single region, (6) partition detection: if a region's state is missing for >60 seconds, mark it as partitioned, (7) dashboard with per-region circuit states, routing decisions, and partition status.
What's Next
Continue with Saga Pattern for circuit breakers in distributed transactions. Then explore Production Readiness for deployment checklists.
Built by the developers of DodaTech
Doda Browser, DodaZIP & Durga Antivirus Pro