
Designing Distributed Systems
- 81 installs
- 426 repo stars
- Updated December 11, 2025
- ancoleman/ai-design-components
designing-distributed-systems is a Claude skill that guides designing scalable, fault-tolerant distributed systems using CAP/PACELC, consistency models, replication, partitioning, and resilience patterns.
About
This skill guides the design of scalable, reliable, fault-tolerant distributed systems using proven patterns and consistency models. A developer uses it when building microservices, multi-region systems, or choosing between consistency and availability during partitions. It covers CAP/PACELC, consistency models, replication, partitioning, distributed transactions, and resilience patterns.
- CAP/PACELC theorems and consistency-model spectrum guidance
- Replication (leader-follower, multi-leader, leaderless) and partitioning strategies
- Resilience patterns: circuit breaker, bulkhead, timeout/retry
Designing Distributed Systems by the numbers
- 81 all-time installs (skills.sh)
- Ranked #3,045 of 4,347 Backend & APIs skills by installs in the Skillselion catalog
- Data as of Aug 5, 2026 (Skillselion catalog sync)
designing-distributed-systems capabilities & compatibility
- Capabilities
- distributed systems design · consistency modeling · resilience design · api design
- Works with
- kafka · redis
- Use cases
- api development · devops
What designing-distributed-systems says it does
Design scalable, reliable, and fault-tolerant distributed systems using proven patterns and consistency models.
CAP Theorem:** In a distributed system experiencing a network partition, choose between Consistency (C) or Availability (A). Partition tolerance (P) is mandatory.
npx skills add https://github.com/ancoleman/ai-design-components --skill designing-distributed-systemsAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 81 |
|---|---|
| repo stars | ★ 426 |
| Last updated | December 11, 2025 |
| Repository | ancoleman/ai-design-components ↗ |
What it does
Designing distributed and microservice architectures with the right consistency, replication, partitioning, and resilience trade-offs.
Who is it for?
Designing microservices and multi-region systems and choosing consistency vs availability trade-offs.
Skip if: Simple single-node applications with no distribution or partitioning concerns.
When should I use this skill?
Designing microservices, choosing replication/consistency strategies, or adding resilience patterns.
What you get
A distributed architecture with justified consistency model, replication, partitioning, and resilience patterns.
- Consistency-model choice
- Replication and partitioning strategy
- Resilience pattern set
By the numbers
- Documents 3 replication patterns (leader-follower, multi-leader, leaderless)
- 3 partitioning strategies (hash, range, geographic)
Files
Designing Distributed Systems
Design scalable, reliable, and fault-tolerant distributed systems using proven patterns and consistency models.
Purpose
Distributed systems are the foundation of modern cloud-native applications. Understanding fundamental trade-offs (CAP theorem, PACELC), consistency models, replication patterns, and resilience strategies is essential for building systems that scale globally while maintaining correctness and availability.
When to Use This Skill
Apply when:
- Designing microservices architectures with multiple services
- Building systems that must scale across multiple datacenters or regions
- Choosing between consistency vs availability during network partitions
- Selecting replication strategies (single-leader, multi-leader, leaderless)
- Implementing distributed transactions (saga pattern, event sourcing, CQRS)
- Designing partition-tolerant systems with proper consistency guarantees
- Building resilient services with circuit breakers, bulkheads, retries
- Implementing service discovery and inter-service communication
Core Concepts
CAP Theorem Fundamentals
CAP Theorem: In a distributed system experiencing a network partition, choose between Consistency (C) or Availability (A). Partition tolerance (P) is mandatory.
Network partitions WILL occur → Always design for P
During partition:
├─ CP (Consistency + Partition Tolerance)
│ Use when: Financial transactions, inventory, seat booking
│ Trade-off: System unavailable during partition
│ Examples: HBase, MongoDB (default), etcd
│
└─ AP (Availability + Partition Tolerance)
Use when: Social media, caching, analytics, shopping carts
Trade-off: Stale reads possible, conflicts need resolution
Examples: Cassandra, DynamoDB, RiakPACELC: Extends CAP to consider normal operations (no partition).
- If Partition: Choose Availability (A) or Consistency (C)
- Else (normal): Choose Latency (L) or Consistency (C)
Consistency Models Spectrum
Strong Consistency ◄─────────────────────► Eventual Consistency
│ │ │
Linearizable Causal Consistency Convergent
(Slowest, (Middle Ground, (Fastest,
Most Consistent) Causally Ordered) Eventually Consistent)Strong Consistency (Linearizability):
- All operations appear atomically in sequential order
- Reads always return most recent write
- Use for: Bank balances, inventory stock, seat booking
- Trade-off: Higher latency, reduced availability
Eventual Consistency:
- If no new updates, all replicas eventually converge
- Use for: Social feeds, product catalogs, user profiles, DNS
- Trade-off: Stale reads possible, conflict resolution needed
Causal Consistency:
- Causally related operations seen in same order by all nodes
- Use for: Chat apps, collaborative editing, comment threads
- Trade-off: More complex than eventual, requires causality tracking
Bounded Staleness:
- Staleness bounded by time or version count
- Use for: Real-time dashboards, leaderboards, monitoring
- Trade-off: Must monitor lag, more complex than eventual
Replication Patterns
1. Leader-Follower (Single-Leader):
- All writes to leader, replicated to followers
- Followers handle reads (load distribution)
- Synchronous: Wait for follower ACK (strong consistency, higher latency)
- Asynchronous: Don't wait (eventual consistency, possible data loss)
- Use for: Most common pattern, strong consistency with sync replication
2. Multi-Leader:
- Multiple leaders accept writes in different datacenters
- Leaders replicate to each other
- Conflict resolution required: Last-Write-Wins, application merge, vector clocks
- Use for: Multi-datacenter, low write latency, geo-distributed users
- Trade-off: Conflict resolution complexity
3. Leaderless (Dynamo-style):
- No single leader, quorum-based reads/writes
- Quorum rule: W + R > N (W=write quorum, R=read quorum, N=replicas)
- Example: N=5, W=3, R=2 → Strong consistency (overlap guaranteed)
- Use for: Maximum availability, partition tolerance
- Trade-off: Complexity, read repair needed
Partitioning Strategies
Hash Partitioning (Consistent Hashing):
- Key → Hash(Key) → Partition assignment
- Even distribution, minimal rebalancing when nodes added/removed
- Use for: Point queries by ID, even distribution critical
- Examples: Cassandra, DynamoDB, Redis Cluster
Range Partitioning:
- Key ranges assigned to partitions (A-F, G-M, N-S, T-Z)
- Enables range queries, ordered data
- Risk: Hot spots if data skewed
- Use for: Time-series data, leaderboards, range scans
- Examples: HBase, Bigtable
Geographic Partitioning:
- Partition by location (US-East, EU-West, APAC)
- Use for: Data locality, GDPR compliance, low latency
- Examples: Spanner, Cosmos DB
Resilience Patterns
Circuit Breaker:
[Closed] → Normal operation
│ (failures exceed threshold)
▼
[Open] → Fail fast (don't call failing service)
│ (timeout expires)
▼
[Half-Open] → Try single request
│ success → [Closed]
│ failure → [Open]- Prevents cascading failures
- Fast-fail instead of waiting for timeout
- See references/resilience-patterns.md
Bulkhead Isolation:
- Isolate resources (thread pools, connection pools)
- Failure in one partition doesn't affect others
- Like ship compartments preventing total flooding
Timeout and Retry:
- Timeout: Set deadlines, fail fast if exceeded
- Retry: Exponential backoff with jitter
- Idempotency: Ensure safe retry (critical)
Rate Limiting and Backpressure:
- Protect services from overload
- Token bucket, leaky bucket algorithms
- Backpressure: Signal upstream to slow down
Transaction Patterns
Saga Pattern:
- Coordinate distributed transactions across services
- No distributed 2PC (two-phase commit)
Choreography: Services react to events
Order Service → OrderCreated event
Payment Service → listens → PaymentProcessed event
Inventory Service → listens → InventoryReserved event
(Compensating: if payment fails → InventoryReleased event)Orchestration: Central coordinator
Saga Orchestrator:
1. Call Order Service
2. Call Payment Service
3. Call Inventory Service
(If step fails → call compensating transactions in reverse)Event Sourcing:
- Store state changes as immutable events
- Rebuild state by replaying events
- Audit trail, time travel, debugging
- Trade-off: Query complexity, snapshot optimization
CQRS (Command Query Responsibility Segregation):
- Separate read and write models
- Write model: Normalized, transactional
- Read model: Denormalized, cached, optimized
- Use for: Different read/write patterns, high read:write ratio (10:1+)
- Often paired with Event Sourcing
Service Discovery
Client-Side Discovery:
- Client queries service registry (Consul, etcd, Eureka)
- Client load balances and calls service directly
- Pro: No proxy overhead
- Con: Client complexity
Server-Side Discovery:
- Client calls load balancer
- Load balancer queries registry and routes
- Pro: Simple clients
- Con: Load balancer single point of failure
Service Mesh:
- Sidecar proxies handle discovery, routing, retry, circuit breaking
- Examples: Istio, Linkerd
- Pro: Decouples communication logic from services
- Con: Operational complexity
Caching Strategies
Cache-Aside (Lazy Loading):
Read:
1. Check cache → hit? return
2. Miss? Query database
3. Store in cache, returnWrite-Through:
Write:
1. Write to cache
2. Cache writes to database synchronously
3. Return successWrite-Behind (Write-Back):
Write:
1. Write to cache
2. Return success
3. Cache writes to database asynchronously (batched)Cache Invalidation:
- TTL (Time-To-Live): Expire after duration
- Event-based: Invalidate on data change
- Manual: Explicit invalidation on update
Decision Frameworks
Choosing Consistency Model
Decision Tree:
├─ Money involved? → Strong Consistency
├─ Double-booking unacceptable? → Strong Consistency
├─ Causality important (chat, edits)? → Causal Consistency
├─ Read-heavy, stale tolerable? → Eventual Consistency
└─ Default? → Eventual (then strengthen if needed)Choosing Replication Pattern
├─ Single region writes? → Leader-Follower
├─ Multi-region writes + conflicts OK? → Multi-Leader
├─ Multi-region writes + no conflicts? → Leader-Follower with failover
└─ Maximum availability? → Leaderless (quorum)Choosing Partitioning Strategy
├─ Need range scans? → Range Partitioning (risk: hot spots)
├─ Data residency requirements? → Geographic Partitioning
└─ Default? → Hash Partitioning (consistent hashing)Quick Reference Tables
CAP/PACELC System Comparison
| System | If Partition | Else (Normal) | Use Case |
|---|---|---|---|
| Spanner | PC | EC (strong) | Global SQL |
| DynamoDB | PA | EL (eventual) | High availability |
| Cassandra | PA | EL (tunable) | Wide-column store |
| MongoDB | PC | EC (default) | Document store |
| Cosmos DB | PA/PC | EL/EC (5 levels) | Multi-model |
Consistency Model Use Cases
| Use Case | Consistency Model |
|---|---|
| Bank account balance | Strong (Linearizable) |
| Seat booking (airline) | Strong (Linearizable) |
| Inventory stock count | Strong or Bounded |
| Shopping cart | Eventual |
| Product catalog | Eventual |
| Collaborative editing | Causal |
| Chat messages | Causal |
| Social media likes | Eventual |
| DNS records | Eventual |
Quorum Configurations
| Configuration | W | R | N | Consistency | Use Case |
|---|---|---|---|---|---|
| Strong | 3 | 3 | 5 | Strong | Banking |
| Balanced | 3 | 2 | 5 | Strong | Default |
| Write-heavy | 2 | 3 | 5 | Strong | Logs |
| Read-heavy | 3 | 1 | 5 | Eventual | Cache |
| Max Avail | 1 | 1 | 5 | Eventual | Analytics |
Progressive Disclosure
Detailed References
For comprehensive coverage of specific topics, see:
- references/cap-pacelc-theorem.md - CAP and PACELC deep-dive with PACELC matrix
- references/consistency-models.md - Strong, eventual, causal, bounded staleness patterns
- references/replication-patterns.md - Leader-follower, multi-leader, leaderless replication
- references/partitioning-strategies.md - Hash, range, geographic partitioning with examples
- references/consensus-algorithms.md - Raft and Paxos overview (when consensus needed)
- references/resilience-patterns.md - Circuit breaker, bulkhead, timeout, retry, rate limiting
- references/saga-pattern.md - Choreography vs orchestration with working examples
- references/event-sourcing-cqrs.md - Event sourcing and CQRS implementation patterns
- references/service-discovery.md - Client-side, server-side, service mesh patterns
- references/caching-strategies.md - Cache-aside, write-through, write-behind, invalidation
Working Examples
Complete, runnable examples demonstrating patterns:
- examples/consistent-hashing/ - Consistent hashing implementation with virtual nodes
- examples/circuit-breaker/ - Circuit breaker pattern with state transitions
- examples/saga-orchestration/ - Saga orchestrator with compensating transactions
- examples/event-sourcing/ - Event store with replay and snapshots
- examples/cqrs/ - CQRS with separate read/write models
- examples/service-discovery/ - Consul-based service discovery and registration
ASCII Diagrams
Visual representations for complex concepts:
- diagrams/cap-theorem.txt - CAP theorem decision tree
- diagrams/replication-topologies.txt - Leader-follower, multi-leader, leaderless
- diagrams/saga-flow.txt - Saga choreography and orchestration flows
- diagrams/caching-patterns.txt - Cache-aside, write-through, write-behind
Integration with Other Skills
Related Skills:
For Kubernetes deployment: See kubernetes-operations skill for pod anti-affinity, service mesh For infrastructure: See infrastructure-as-code skill for deploying distributed systems For databases: See databases-sql and databases-nosql for replication configuration For messaging: See message-queues skill for event-driven architectures, saga orchestration For monitoring: See observability skill for distributed tracing, monitoring patterns For testing: See performance-engineering skill for load testing distributed systems For security: See security-hardening skill for mTLS, service authentication
Common Patterns
Multi-Datacenter Pattern
1. Choose replication: Multi-leader or Leaderless
2. Partition data geographically
3. Implement conflict resolution (LWW, vector clocks, app-specific)
4. Monitor replication lag
5. Add circuit breakers between datacentersEvent-Driven Saga Pattern
1. Define saga steps and compensating actions
2. Choose choreography (events) or orchestration (coordinator)
3. Implement idempotent handlers (retries safe)
4. Publish events with outbox pattern (transactional)
5. Monitor saga progress and timeoutsHigh-Availability Pattern
1. Use leaderless replication (N=5, W=3, R=2)
2. Partition with consistent hashing
3. Add circuit breakers for failing nodes
4. Implement read repair and anti-entropy
5. Monitor quorum healthBest Practices
Design for Failure:
- Network partitions will occur - always design for partition tolerance
- Use timeouts, retries with exponential backoff
- Implement circuit breakers to prevent cascading failures
- Test chaos engineering scenarios (partition nodes, inject latency)
Choose Consistency Carefully:
- Default to eventual consistency, strengthen only where needed
- Strong consistency has real costs (latency, availability)
- Use bounded staleness for middle ground
Idempotency is Critical:
- Design operations to be safely retryable
- Use unique request IDs for deduplication
- Essential for saga compensating transactions
Monitor and Observe:
- Distributed tracing with correlation IDs
- Monitor replication lag, quorum health
- Alert on circuit breaker state changes
- Track saga progress and failures
Partition Strategically:
- Hash partitioning for even distribution
- Range partitioning for range queries (monitor hot spots)
- Geographic partitioning for compliance, latency
Version Everything:
- Event schemas evolve - use versioning
- API versioning for service compatibility
- Database schema migrations in distributed systems
Anti-Patterns to Avoid
Distributed Monolith:
- Microservices with tight coupling
- Shared database across services
- Fix: Database per service, async communication
Two-Phase Commit (2PC) Overuse:
- Slow, blocking, reduces availability
- Fix: Use saga pattern for distributed transactions
Ignoring Network Failures:
- Assuming network is reliable
- Fix: Always add timeouts, retries, circuit breakers
Strong Consistency Everywhere:
- Unnecessary latency and complexity
- Fix: Use eventual consistency by default, strengthen where needed
No Conflict Resolution Strategy:
- Multi-leader without handling conflicts
- Fix: Choose LWW, vector clocks, or app-specific merge
Cache Stampede:
- TTL expires, all clients query database
- Fix: Probabilistic early expiration, request coalescing
Troubleshooting
Replication Lag Too High:
- Check network bandwidth between datacenters
- Monitor write throughput on leader
- Consider async replication or multi-leader
Split-Brain Scenario:
- Multiple leaders elected during partition
- Fix: Use consensus (Raft, Paxos) for leader election
- Implement fencing tokens to prevent dual writes
Hot Partitions:
- Range partitioning with skewed data
- Fix: Add hash component, manually redistribute, use composite keys
Saga Timeout/Stalled:
- Service unavailable, saga can't complete
- Fix: Implement saga timeout with automated rollback
- Dead letter queue for manual intervention
Conflict Resolution Failures:
- Multi-leader conflicts unhandled
- Fix: Implement clear resolution strategy (LWW, merge, manual)
- Monitor conflict rate, alert on spikes
CACHING PATTERNS
================
1. CACHE-ASIDE (Lazy Loading)
──────────────────────────────
READ:
┌────────┐ ┌───────┐ ┌──────────┐
│ Client │────►│ Cache │────►│ Database │
└────────┘ └───────┘ └──────────┘
▲ │ │
│ Hit?│ Miss│
│ ▼ ▼
└─────────────┴───────────────┘
(Store & Return)
WRITE:
┌────────┐ ┌──────────┐
│ Client │────►│ Database │
└────────┘ └──────────┘
│
└─► Invalidate Cache (optional)
2. WRITE-THROUGH
────────────────
WRITE:
┌────────┐ ┌───────┐ ┌──────────┐
│ Client │────►│ Cache │────►│ Database │
└────────┘ └───────┘ └──────────┘
│ │
└──(sync)─────┘
READ:
┌────────┐ ┌───────┐
│ Client │────►│ Cache │ (Always hit)
└────────┘ └───────┘
3. WRITE-BEHIND (Write-Back)
─────────────────────────────
WRITE:
┌────────┐ ┌───────┐
│ Client │────►│ Cache │ (Return immediately)
└────────┘ └───────┘
│
│ (async, batched)
▼
┌──────────┐
│ Database │
└──────────┘
Trade-off: Fast writes, risk of data loss
INVALIDATION STRATEGIES
────────────────────────
- TTL (Time-To-Live): Expire after duration
- Event-based: Invalidate on data change
- Manual: Explicit invalidation on update
CAP THEOREM DECISION TREE
=========================
Network Partitions WILL Occur → Always Design for P (Partition Tolerance)
During Partition, Choose:
┌─────────────────────────────────────────────────────────────┐
│ CP (Consistency + Partition Tolerance) │
├─────────────────────────────────────────────────────────────┤
│ Behavior: Reject writes if quorum unavailable │
│ Use When: Correctness > Availability │
│ Examples: │
│ - Bank account balance │
│ - Inventory stock count │
│ - Seat/ticket booking │
│ - Distributed locks │
│ │
│ Systems: HBase, MongoDB (default), etcd, Consul │
│ Trade-off: System unavailable during partition │
└─────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────┐
│ AP (Availability + Partition Tolerance) │
├─────────────────────────────────────────────────────────────┤
│ Behavior: Accept writes, resolve conflicts later │
│ Use When: Availability > Strict Consistency │
│ Examples: │
│ - Social media (likes, follows, posts) │
│ - Shopping cart │
│ - Product catalog │
│ - Analytics │
│ │
│ Systems: Cassandra, DynamoDB, Riak, Couchbase │
│ Trade-off: Stale reads, conflict resolution needed │
└─────────────────────────────────────────────────────────────┘
REPLICATION TOPOLOGIES
======================
1. LEADER-FOLLOWER (Single-Leader)
───────────────────────────────────
┌─────────┐
│ Leader │ ◄─── All writes
└────┬────┘
│ (replicates)
┌────┴────┬────────┐
│ │ │
▼ ▼ ▼
Follower Follower Follower
(reads) (reads) (reads)
Use: Most common, strong consistency
Examples: PostgreSQL, MySQL, MongoDB
2. MULTI-LEADER
───────────────
┌─────────┐ ┌─────────┐
│ Leader │◄────►│ Leader │
│ (US) │ │ (EU) │
└────┬────┘ └────┬────┘
│ │
(replicates) (replicates)
│ │
Followers Followers
Use: Multi-datacenter, low write latency
Challenges: Conflict resolution needed
Examples: Cassandra, CouchDB
3. LEADERLESS (Dynamo-style)
─────────────────────────────
┌─────────┐ ┌─────────┐
│ Node │◄────►│ Node │
│ (R/W) │ │ (R/W) │
└────┬────┘ └────┬────┘
│ ╲ ╱ │
│ ╲ ╱ │
│ ╲ ╱ │
┌────┴────┐ ╲ ╱ ┌────┴────┐
│ Node │ ╳ │ Node │
│ (R/W) │ ╱ ╲│ (R/W) │
└─────────┘╱ ╲└─────────┘
Use: High availability, partition tolerance
Quorum: W + R > N
Examples: Cassandra, Riak, DynamoDB
SAGA PATTERNS
=============
1. CHOREOGRAPHY (Event-Based)
──────────────────────────────
Order Service → OrderCreated event
↓
Payment Service listens → PaymentProcessed event
↓
Inventory Service listens → InventoryReserved event
↓
Shipping Service listens → OrderShipped event
IF PAYMENT FAILS:
Payment Service → PaymentFailed event
Order Service listens → CancelOrder (compensating action)
2. ORCHESTRATION (Coordinator-Based)
─────────────────────────────────────
┌─────────────────────────┐
│ Saga Orchestrator │
└────────┬────────────────┘
│
├─► 1. Order Service → Create Order
│
├─► 2. Payment Service → Process Payment
│
├─► 3. Inventory Service → Reserve Inventory
│
└─► 4. Shipping Service → Ship Order
IF STEP 3 FAILS:
Compensate Step 2: Refund Payment
Compensate Step 1: Cancel Order
COMPARISON
──────────
Choreography:
+ Decoupled services
- Hard to trace flow
Orchestration:
+ Clear workflow
- Central coordinator dependency
"""
Circuit Breaker Pattern Implementation
Demonstrates: Circuit breaker with CLOSED, OPEN, HALF-OPEN states
Dependencies:
- Python 3.7+
Usage:
python circuit_breaker.py
"""
from enum import Enum
from datetime import datetime, timedelta
import threading
import time
import random
class CircuitState(Enum):
CLOSED = "closed" # Normal operation
OPEN = "open" # Failing, reject requests
HALF_OPEN = "half_open" # Testing recovery
class CircuitBreaker:
"""Circuit breaker to prevent cascading failures"""
def __init__(self, failure_threshold=5, timeout=60, success_threshold=2):
"""
Initialize circuit breaker
Args:
failure_threshold: Number of failures before opening circuit
timeout: Seconds before attempting reset from OPEN to HALF-OPEN
success_threshold: Number of successes in HALF-OPEN before closing
"""
self.failure_threshold = failure_threshold
self.timeout = timeout
self.success_threshold = success_threshold
self.state = CircuitState.CLOSED
self.failure_count = 0
self.success_count = 0
self.last_failure_time = None
self.lock = threading.Lock()
def call(self, func, *args, **kwargs):
"""Execute function with circuit breaker protection"""
with self.lock:
if self.state == CircuitState.OPEN:
if self._should_attempt_reset():
print(f" [CB] Transitioning to HALF-OPEN")
self.state = CircuitState.HALF_OPEN
else:
raise Exception("Circuit breaker is OPEN - failing fast")
try:
result = func(*args, **kwargs)
self._on_success()
return result
except Exception as e:
self._on_failure()
raise e
def _should_attempt_reset(self):
"""Check if timeout has expired"""
return (self.last_failure_time and
datetime.now() - self.last_failure_time > timedelta(seconds=self.timeout))
def _on_success(self):
"""Handle successful call"""
with self.lock:
if self.state == CircuitState.HALF_OPEN:
self.success_count += 1
print(f" [CB] HALF-OPEN: Success {self.success_count}/{self.success_threshold}")
if self.success_count >= self.success_threshold:
print(f" [CB] Transitioning to CLOSED (recovered)")
self.state = CircuitState.CLOSED
self.failure_count = 0
self.success_count = 0
else:
self.failure_count = 0
def _on_failure(self):
"""Handle failed call"""
with self.lock:
self.failure_count += 1
self.last_failure_time = datetime.now()
print(f" [CB] Failure {self.failure_count}/{self.failure_threshold} (state: {self.state.value})")
if self.failure_count >= self.failure_threshold:
print(f" [CB] Transitioning to OPEN")
self.state = CircuitState.OPEN
elif self.state == CircuitState.HALF_OPEN:
print(f" [CB] Transitioning to OPEN (failed in HALF-OPEN)")
self.state = CircuitState.OPEN
self.success_count = 0
def get_state(self):
"""Get current circuit breaker state"""
return self.state
# Simulated external service
class UnreliableService:
def __init__(self, failure_rate=0.5):
self.failure_rate = failure_rate
self.call_count = 0
def call(self):
"""Simulate service call with configurable failure rate"""
self.call_count += 1
time.sleep(0.1) # Simulate network latency
if random.random() < self.failure_rate:
raise Exception(f"Service failed (call {self.call_count})")
return f"Success (call {self.call_count})"
if __name__ == "__main__":
print("Circuit Breaker Pattern Demo\n")
# Create circuit breaker and unreliable service
circuit_breaker = CircuitBreaker(failure_threshold=3, timeout=5, success_threshold=2)
service = UnreliableService(failure_rate=0.8) # 80% failure rate
print("Phase 1: Failing service (80% failure rate)")
print("=" * 60)
# Make calls until circuit opens
for i in range(10):
try:
result = circuit_breaker.call(service.call)
print(f"Call {i+1}: {result}")
except Exception as e:
print(f"Call {i+1}: Failed - {e}")
time.sleep(0.5)
print(f"\nCircuit state: {circuit_breaker.get_state().value}")
print("Circuit is OPEN - failing fast for next requests\n")
# Wait for timeout
print(f"Waiting {circuit_breaker.timeout} seconds for timeout...")
time.sleep(circuit_breaker.timeout + 1)
print("\nPhase 2: Service recovered (0% failure rate)")
print("=" * 60)
# Service recovers
service.failure_rate = 0.0
# Make successful calls to close circuit
for i in range(5):
try:
result = circuit_breaker.call(service.call)
print(f"Call {i+1}: {result}")
except Exception as e:
print(f"Call {i+1}: Failed - {e}")
time.sleep(0.5)
print(f"\nFinal circuit state: {circuit_breaker.get_state().value}")
"""
Consistent Hashing Implementation
Demonstrates: Consistent hashing with virtual nodes for even distribution
Dependencies:
- Python 3.7+
- No external dependencies (uses standard library)
Usage:
python consistent_hash.py
"""
import hashlib
import bisect
class ConsistentHash:
"""Consistent hashing implementation with virtual nodes"""
def __init__(self, nodes=None, replicas=150):
"""
Initialize consistent hash ring
Args:
nodes: List of physical node identifiers
replicas: Number of virtual nodes per physical node (default: 150)
"""
self.replicas = replicas
self.ring = {} # hash -> physical node
self.sorted_keys = []
if nodes:
for node in nodes:
self.add_node(node)
def _hash(self, key):
"""Hash function returning integer"""
return int(hashlib.md5(key.encode()).hexdigest(), 16)
def add_node(self, node):
"""Add physical node with virtual replicas to the ring"""
for i in range(self.replicas):
virtual_key = f"{node}:{i}"
hash_val = self._hash(virtual_key)
self.ring[hash_val] = node
bisect.insort(self.sorted_keys, hash_val)
print(f"Added node '{node}' with {self.replicas} virtual nodes")
def remove_node(self, node):
"""Remove physical node and its virtual replicas from the ring"""
for i in range(self.replicas):
virtual_key = f"{node}:{i}"
hash_val = self._hash(virtual_key)
if hash_val in self.ring:
del self.ring[hash_val]
self.sorted_keys.remove(hash_val)
print(f"Removed node '{node}' and its virtual nodes")
def get_node(self, key):
"""Find physical node responsible for key"""
if not self.ring:
return None
hash_val = self._hash(key)
# Binary search for first node >= hash_val
idx = bisect.bisect(self.sorted_keys, hash_val)
if idx == len(self.sorted_keys):
idx = 0 # Wrap around to first node
return self.ring[self.sorted_keys[idx]]
def get_nodes(self, key, n=3):
"""Get N physical nodes for replication"""
if not self.ring or n > len(set(self.ring.values())):
return []
hash_val = self._hash(key)
idx = bisect.bisect(self.sorted_keys, hash_val)
nodes = []
seen = set()
while len(nodes) < n:
if idx >= len(self.sorted_keys):
idx = 0
node = self.ring[self.sorted_keys[idx]]
if node not in seen:
nodes.append(node)
seen.add(node)
idx += 1
return nodes
def distribution(self):
"""Analyze key distribution across nodes"""
node_counts = {}
test_keys = [f"key{i}" for i in range(10000)]
for key in test_keys:
node = self.get_node(key)
node_counts[node] = node_counts.get(node, 0) + 1
return node_counts
if __name__ == "__main__":
# Create consistent hash ring with 4 nodes
ch = ConsistentHash(['node1', 'node2', 'node3', 'node4'])
# Test key assignments
print("\nKey assignments:")
test_keys = ['user123', 'user456', 'user789', 'product100']
for key in test_keys:
node = ch.get_node(key)
replicas = ch.get_nodes(key, n=3)
print(f" {key} → {node} (replicas: {replicas})")
# Check distribution
print("\nInitial distribution (10,000 keys):")
dist = ch.distribution()
for node, count in sorted(dist.items()):
percentage = (count / 10000) * 100
print(f" {node}: {count} keys ({percentage:.2f}%)")
# Add new node and check redistribution
print("\nAdding node5...")
ch.add_node('node5')
print("\nDistribution after adding node5:")
dist = ch.distribution()
for node, count in sorted(dist.items()):
percentage = (count / 10000) * 100
print(f" {node}: {count} keys ({percentage:.2f}%)")
# Remove node and check redistribution
print("\nRemoving node2...")
ch.remove_node('node2')
print("\nDistribution after removing node2:")
dist = ch.distribution()
for node, count in sorted(dist.items()):
percentage = (count / 10000) * 100
print(f" {node}: {count} keys ({percentage:.2f}%)")
"""
CQRS (Command Query Responsibility Segregation) Pattern
Demonstrates: Separate read/write models
Dependencies:
- Python 3.7+
Usage:
python cqrs_example.py
"""
from datetime import datetime
from typing import Dict, List
# Write Model (Command Side)
class OrderWriteModel:
"""Normalized write model for commands"""
def __init__(self):
self.orders = {} # order_id -> order
self.order_items = {} # order_id -> [items]
self.event_handlers = []
def create_order(self, command: Dict):
"""Handle CreateOrder command"""
order_id = command['order_id']
if order_id in self.orders:
raise ValueError(f"Order {order_id} already exists")
# Write to normalized storage
self.orders[order_id] = {
'order_id': order_id,
'customer_id': command['customer_id'],
'status': 'pending',
'created_at': datetime.utcnow()
}
self.order_items[order_id] = command['items']
# Publish event for read model update
event = {
'type': 'OrderCreated',
'order_id': order_id,
'customer_id': command['customer_id'],
'items': command['items'],
'timestamp': datetime.utcnow()
}
for handler in self.event_handlers:
handler(event)
print(f"[Write Model] Created order {order_id}")
return order_id
def add_event_handler(self, handler):
"""Register event handler"""
self.event_handlers.append(handler)
# Read Model (Query Side)
class OrderReadModel:
"""Denormalized read model for queries"""
def __init__(self):
self.orders_cache = {} # order_id -> denormalized order
self.customer_orders_index = {} # customer_id -> [order_ids]
def get_order(self, order_id: str) -> Dict:
"""Get order summary (optimized read)"""
return self.orders_cache.get(order_id)
def get_customer_orders(self, customer_id: str) -> List[Dict]:
"""Get all orders for customer"""
order_ids = self.customer_orders_index.get(customer_id, [])
return [self.orders_cache[oid] for oid in order_ids if oid in self.orders_cache]
def update_from_event(self, event):
"""Update read model from event"""
if event['type'] == 'OrderCreated':
order_id = event['order_id']
customer_id = event['customer_id']
# Denormalized document (optimized for reads)
order_doc = {
'order_id': order_id,
'customer_id': customer_id,
'items': event['items'],
'item_count': len(event['items']),
'total_quantity': sum(item['quantity'] for item in event['items']),
'created_at': event['timestamp'],
'status': 'pending'
}
# Update cache
self.orders_cache[order_id] = order_doc
# Update customer index
if customer_id not in self.customer_orders_index:
self.customer_orders_index[customer_id] = []
self.customer_orders_index[customer_id].append(order_id)
print(f"[Read Model] Updated order {order_id} in cache and indexes")
if __name__ == "__main__":
print("CQRS Pattern Demo\n")
# Create write and read models
write_model = OrderWriteModel()
read_model = OrderReadModel()
# Connect event handler
write_model.add_event_handler(read_model.update_from_event)
# Command: Create order
print("Command: Create Order")
print("=" * 60)
command = {
'order_id': 'order-123',
'customer_id': 'customer-456',
'items': [
{'product_id': 'prod-1', 'quantity': 2, 'price': 50},
{'product_id': 'prod-2', 'quantity': 1, 'price': 100}
]
}
write_model.create_order(command)
# Query: Get order (from read model)
print("\nQuery: Get Order")
print("=" * 60)
order = read_model.get_order('order-123')
print(f"Order summary: {order}")
# Create another order for same customer
print("\nCommand: Create Another Order")
print("=" * 60)
command2 = {
'order_id': 'order-124',
'customer_id': 'customer-456',
'items': [
{'product_id': 'prod-3', 'quantity': 1, 'price': 200}
]
}
write_model.create_order(command2)
# Query: Get customer orders
print("\nQuery: Get Customer Orders")
print("=" * 60)
customer_orders = read_model.get_customer_orders('customer-456')
print(f"Customer has {len(customer_orders)} orders:")
for order in customer_orders:
print(f" - {order['order_id']}: {order['item_count']} items, "
f"{order['total_quantity']} total quantity")
"""
Event Sourcing Pattern
Demonstrates: Event store with replay and snapshots
Dependencies:
- Python 3.7+
Usage:
python event_store.py
"""
from datetime import datetime
from typing import List, Dict, Any
class Event:
"""Immutable event"""
def __init__(self, aggregate_id: str, event_type: str, data: Dict, version: int):
self.aggregate_id = aggregate_id
self.event_type = event_type
self.data = data
self.version = version
self.timestamp = datetime.utcnow()
def __repr__(self):
return f"Event({self.event_type}, v{self.version}, {self.data})"
class EventStore:
"""Event store with replay capability"""
def __init__(self):
self.events = {} # aggregate_id -> [events]
self.snapshots = {} # aggregate_id -> snapshot
def append(self, aggregate_id: str, event: Event):
"""Append event to store"""
if aggregate_id not in self.events:
self.events[aggregate_id] = []
self.events[aggregate_id].append(event)
print(f"Appended: {event}")
def get_events(self, aggregate_id: str, from_version=0):
"""Get events for aggregate starting from version"""
events = self.events.get(aggregate_id, [])
return [e for e in events if e.version >= from_version]
def replay(self, aggregate_id: str):
"""Replay events to rebuild current state"""
events = self.get_events(aggregate_id)
state = {}
for event in events:
state = self._apply_event(state, event)
return state
def _apply_event(self, state: Dict, event: Event) -> Dict:
"""Apply event to state"""
if event.event_type == 'AccountCreated':
state['account_id'] = event.aggregate_id
state['balance'] = event.data['initial_balance']
state['status'] = 'active'
elif event.event_type == 'MoneyDeposited':
state['balance'] += event.data['amount']
elif event.event_type == 'MoneyWithdrawn':
state['balance'] -= event.data['amount']
elif event.event_type == 'AccountClosed':
state['status'] = 'closed'
return state
def create_snapshot(self, aggregate_id: str):
"""Create snapshot of current state"""
state = self.replay(aggregate_id)
version = len(self.events[aggregate_id])
self.snapshots[aggregate_id] = {
'state': state,
'version': version,
'timestamp': datetime.utcnow()
}
print(f"Snapshot created at version {version}")
if __name__ == "__main__":
print("Event Sourcing Pattern Demo\n")
event_store = EventStore()
# Create account
print("Creating account...")
event_store.append('account-123', Event(
aggregate_id='account-123',
event_type='AccountCreated',
data={'initial_balance': 1000},
version=1
))
# Deposit
print("\nDeposit $500...")
event_store.append('account-123', Event(
aggregate_id='account-123',
event_type='MoneyDeposited',
data={'amount': 500},
version=2
))
# Withdraw
print("\nWithdraw $200...")
event_store.append('account-123', Event(
aggregate_id='account-123',
event_type='MoneyWithdrawn',
data={'amount': 200},
version=3
))
# Replay to get current state
print("\nReplaying events to get current state...")
current_state = event_store.replay('account-123')
print(f"Current state: {current_state}")
# More transactions
print("\nDeposit $1000...")
event_store.append('account-123', Event(
aggregate_id='account-123',
event_type='MoneyDeposited',
data={'amount': 1000},
version=4
))
print("\nReplaying events again...")
current_state = event_store.replay('account-123')
print(f"Current state: {current_state}")
# Demonstrate time travel
print("\nTime travel: Get state at version 2...")
events_v2 = event_store.get_events('account-123', from_version=0)[:2]
state_v2 = {}
for event in events_v2:
state_v2 = event_store._apply_event(state_v2, event)
print(f"State at v2: {state_v2}")
"""
Saga Orchestration Pattern
Demonstrates: Saga orchestrator with compensating transactions
Dependencies:
- Python 3.7+
Usage:
python saga_orchestrator.py
"""
class SagaOrchestrator:
"""Orchestrator for distributed transactions with compensation"""
def __init__(self):
self.steps = []
self.compensations = []
def add_step(self, name, action, compensation):
"""Add saga step with compensating action"""
self.steps.append({'name': name, 'action': action})
self.compensations.append({'name': name, 'compensation': compensation})
def execute(self, context):
"""Execute saga with automatic rollback on failure"""
executed_steps = []
print(f"Starting saga with context: {context}\n")
try:
for step in self.steps:
print(f"Executing: {step['name']}")
result = step['action'](context)
executed_steps.append(step)
context.update(result)
print(f" Success: {result}\n")
print(f"Saga completed successfully!")
return {'status': 'success', 'context': context}
except Exception as e:
print(f" Failed: {e}\n")
print(f"Rolling back {len(executed_steps)} executed steps...")
# Rollback in reverse order
for step in reversed(executed_steps):
idx = self.steps.index(step)
compensation = self.compensations[idx]
try:
print(f"Compensating: {compensation['name']}")
compensation['compensation'](context)
print(f" Compensated\n")
except Exception as comp_error:
print(f" Compensation failed: {comp_error}\n")
return {'status': 'failed', 'error': str(e)}
# Mock services
class OrderService:
def create_order(self, context):
order_id = f"order-{context['product_id']}"
print(f" Creating order {order_id}")
return {'order_id': order_id, 'status': 'created'}
def cancel_order(self, context):
print(f" Cancelling order {context['order_id']}")
class PaymentService:
def process_payment(self, context):
payment_id = f"payment-{context['order_id']}"
if context.get('fail_payment'):
raise Exception("Payment declined")
print(f" Processing payment {payment_id}")
return {'payment_id': payment_id}
def refund(self, context):
print(f" Refunding payment {context['payment_id']}")
class InventoryService:
def reserve(self, context):
reservation_id = f"reservation-{context['product_id']}"
print(f" Reserving inventory {reservation_id}")
return {'reservation_id': reservation_id}
def release(self, context):
print(f" Releasing inventory {context.get('reservation_id', 'N/A')}")
if __name__ == "__main__":
order_service = OrderService()
payment_service = PaymentService()
inventory_service = InventoryService()
# Create saga
saga = SagaOrchestrator()
saga.add_step(
name="Create Order",
action=order_service.create_order,
compensation=order_service.cancel_order
)
saga.add_step(
name="Process Payment",
action=payment_service.process_payment,
compensation=payment_service.refund
)
saga.add_step(
name="Reserve Inventory",
action=inventory_service.reserve,
compensation=inventory_service.release
)
# Scenario 1: Success
print("Scenario 1: All steps succeed")
print("=" * 60)
result = saga.execute({'product_id': 'prod-123'})
print(f"Result: {result['status']}\n\n")
# Scenario 2: Payment failure
print("Scenario 2: Payment fails, trigger compensation")
print("=" * 60)
result = saga.execute({'product_id': 'prod-456', 'fail_payment': True})
print(f"Result: {result['status']}")
"""
Service Discovery with Consul
Demonstrates: Client-side service discovery
Dependencies:
- pip install python-consul requests
Usage:
# Start Consul first:
# consul agent -dev
python consul_discovery.py
"""
import random
class MockConsul:
"""Mock Consul client for demonstration"""
def __init__(self):
self.services = {
'payment-service': [
{'Address': 'localhost', 'Port': 8001},
{'Address': 'localhost', 'Port': 8002}
],
'inventory-service': [
{'Address': 'localhost', 'Port': 9001},
{'Address': 'localhost', 'Port': 9002},
{'Address': 'localhost', 'Port': 9003}
]
}
def health_service(self, service_name, passing=True):
"""Mock health service query"""
services = self.services.get(service_name, [])
# Format: (index, [{'Service': {...}}])
return (0, [{'Service': s} for s in services])
class ServiceDiscovery:
"""Client-side service discovery"""
def __init__(self, consul_client=None):
self.consul = consul_client or MockConsul()
def discover(self, service_name: str):
"""Discover healthy service instances"""
index, services = self.consul.health_service(service_name, passing=True)
instances = [
{'host': s['Service']['Address'], 'port': s['Service']['Port']}
for s in services
]
return instances
def call_service(self, service_name: str, path: str = '/'):
"""Call service with load balancing"""
instances = self.discover(service_name)
if not instances:
raise Exception(f"No healthy instances of {service_name}")
# Load balance (random selection)
instance = random.choice(instances)
url = f"http://{instance['host']}:{instance['port']}{path}"
print(f" Calling {service_name} at {url}")
return {'url': url, 'instance': instance}
if __name__ == "__main__":
print("Service Discovery Pattern Demo\n")
sd = ServiceDiscovery()
# Discover payment service instances
print("Discovering payment-service...")
instances = sd.discover('payment-service')
print(f"Found {len(instances)} instances:")
for inst in instances:
print(f" - {inst['host']}:{inst['port']}")
# Call payment service (load balanced)
print("\nCalling payment-service 5 times (observe load balancing):")
for i in range(5):
result = sd.call_service('payment-service', '/process-payment')
# Discover inventory service
print("\nDiscovering inventory-service...")
instances = sd.discover('inventory-service')
print(f"Found {len(instances)} instances:")
for inst in instances:
print(f" - {inst['host']}:{inst['port']}")
print("\nCalling inventory-service 5 times:")
for i in range(5):
result = sd.call_service('inventory-service', '/check-stock')
skill: "designing-distributed-systems"
version: "1.0"
domain: "infrastructure"
# Base outputs required for all distributed systems projects
base_outputs:
- path: "docs/architecture.md"
must_contain: ["CAP", "consistency", "replication", "partitioning"]
reason: "Architecture documentation with distributed systems trade-offs"
- path: "docs/consistency-model.md"
must_contain: ["consistency model", "trade-off"]
reason: "Document chosen consistency model and rationale"
- path: "config/service-discovery.yaml"
must_contain: ["service", "discovery"]
reason: "Service discovery configuration"
- path: "docs/resilience-patterns.md"
must_contain: ["circuit breaker", "retry", "timeout"]
reason: "Document resilience patterns implemented"
# Conditional outputs based on configuration
conditional_outputs:
maturity:
starter:
- path: "services/simple-replication.yaml"
must_contain: ["replicas:", "leader"]
reason: "Basic leader-follower replication setup"
- path: "config/cache-config.yaml"
must_contain: ["cache", "ttl"]
reason: "Simple cache-aside pattern configuration"
- path: "docs/cap-choice.md"
must_contain: ["CAP", "CP", "AP"]
reason: "Document CAP theorem choice (CP vs AP)"
- path: "services/circuit-breaker.py"
must_contain: ["CircuitBreaker", "CLOSED", "OPEN"]
reason: "Basic circuit breaker implementation"
intermediate:
- path: "services/replication/"
must_contain: ["leader", "follower", "synchronous"]
reason: "Leader-follower replication with sync/async options"
- path: "services/partitioning/"
must_contain: ["hash", "consistent_hashing", "partition"]
reason: "Hash-based partitioning implementation"
- path: "services/saga/"
must_contain: ["saga", "compensating", "transaction"]
reason: "Saga pattern for distributed transactions"
- path: "config/quorum-config.yaml"
must_contain: ["quorum", "W", "R", "N"]
reason: "Quorum configuration (W+R > N)"
- path: "services/circuit-breaker/"
must_contain: ["circuit_breaker", "failure_threshold", "timeout"]
reason: "Production circuit breaker with configurable thresholds"
- path: "services/service-discovery/"
must_contain: ["consul", "etcd", "registry"]
reason: "Service registry integration"
- path: "monitoring/replication-lag.yaml"
must_contain: ["lag", "replica", "alert"]
reason: "Monitor replication lag"
advanced:
- path: "services/multi-leader-replication/"
must_contain: ["multi-leader", "conflict", "resolution", "vector_clock"]
reason: "Multi-leader replication with conflict resolution"
- path: "services/leaderless-replication/"
must_contain: ["quorum", "read_repair", "anti_entropy"]
reason: "Leaderless (Dynamo-style) replication"
- path: "services/event-sourcing/"
must_contain: ["event", "store", "replay", "snapshot"]
reason: "Event sourcing implementation with snapshots"
- path: "services/cqrs/"
must_contain: ["command", "query", "read_model", "write_model"]
reason: "CQRS with separate read/write models"
- path: "services/saga-orchestrator/"
must_contain: ["orchestrator", "saga", "compensating", "rollback"]
reason: "Saga orchestration pattern with compensating transactions"
- path: "services/consensus/"
must_contain: ["raft", "paxos", "leader_election"]
reason: "Consensus algorithm implementation (Raft or Paxos)"
- path: "services/geographic-partitioning/"
must_contain: ["geo", "partition", "region", "locality"]
reason: "Geographic partitioning for data locality"
- path: "config/causal-consistency.yaml"
must_contain: ["causal", "causality", "vector_clock", "lamport"]
reason: "Causal consistency tracking configuration"
- path: "monitoring/distributed-tracing.yaml"
must_contain: ["trace", "correlation_id", "jaeger", "zipkin"]
reason: "Distributed tracing for request correlation"
- path: "monitoring/saga-monitoring.yaml"
must_contain: ["saga", "timeout", "compensation", "state"]
reason: "Monitor saga progress and failures"
- path: "chaos-testing/chaos-experiments.yaml"
must_contain: ["chaos", "partition", "latency", "failure"]
reason: "Chaos engineering experiments for resilience testing"
infrastructure:
kubernetes:
- path: "k8s/service-mesh.yaml"
must_contain: ["istio", "linkerd", "sidecar"]
reason: "Service mesh for resilience and observability"
- path: "k8s/pod-anti-affinity.yaml"
must_contain: ["podAntiAffinity", "topology"]
reason: "Pod anti-affinity for replica distribution"
- path: "k8s/network-policies.yaml"
must_contain: ["NetworkPolicy", "ingress", "egress"]
reason: "Network policies for service-to-service communication"
- path: "k8s/circuit-breaker-config.yaml"
must_contain: ["circuit_breaker", "outlier_detection"]
reason: "Service mesh circuit breaker configuration"
docker_compose:
- path: "docker-compose.yml"
must_contain: ["services:", "replicas:", "networks:"]
reason: "Multi-service Docker Compose setup"
- path: "docker-compose.override.yml"
must_contain: ["depends_on:", "healthcheck:"]
reason: "Service dependencies and health checks"
- path: "haproxy/haproxy.cfg"
must_contain: ["backend", "balance", "server"]
reason: "HAProxy for load balancing and service discovery"
cloud_provider:
aws:
- path: "aws/dynamodb-config.tf"
must_contain: ["aws_dynamodb_table", "hash_key", "global_secondary_index"]
reason: "DynamoDB for leaderless replication (AP system)"
- path: "aws/rds-multi-az.tf"
must_contain: ["multi_az", "replica"]
reason: "RDS Multi-AZ for leader-follower replication"
- path: "aws/elasticache-redis.tf"
must_contain: ["replication_group", "automatic_failover_enabled"]
reason: "ElastiCache Redis cluster for caching"
- path: "aws/route53-failover.tf"
must_contain: ["failover", "health_check"]
reason: "Route53 failover routing for multi-region"
gcp:
- path: "gcp/cloud-spanner.tf"
must_contain: ["google_spanner_database", "num_nodes"]
reason: "Cloud Spanner for globally distributed SQL (CP system)"
- path: "gcp/cloud-sql-ha.tf"
must_contain: ["availability_type", "REGIONAL"]
reason: "Cloud SQL with high availability (leader-follower)"
- path: "gcp/memorystore-redis.tf"
must_contain: ["tier", "HA"]
reason: "Memorystore Redis for caching with HA"
azure:
- path: "azure/cosmos-db.tf"
must_contain: ["azurerm_cosmosdb_account", "consistency_level", "geo_location"]
reason: "Cosmos DB with multi-region replication"
- path: "azure/sql-failover-group.tf"
must_contain: ["failover_group", "read_write_endpoint"]
reason: "Azure SQL failover groups for multi-region"
- path: "azure/redis-cache.tf"
must_contain: ["azurerm_redis_cache", "redis_configuration"]
reason: "Azure Cache for Redis"
multi-cloud:
- path: "multi-cloud/spanner-cockroachdb.tf"
must_contain: ["cockroach", "node", "region"]
reason: "CockroachDB for multi-cloud distributed SQL"
- path: "multi-cloud/consul-config.hcl"
must_contain: ["datacenter", "wan_join", "retry_join"]
reason: "Consul for multi-datacenter service mesh"
- path: "multi-cloud/replication-config.yaml"
must_contain: ["datacenter", "replication", "conflict_resolution"]
reason: "Multi-datacenter replication configuration"
service_mesh:
istio:
- path: "istio/circuit-breaker.yaml"
must_contain: ["DestinationRule", "trafficPolicy", "outlierDetection"]
reason: "Istio circuit breaker configuration"
- path: "istio/retry-policy.yaml"
must_contain: ["VirtualService", "retries", "perTryTimeout"]
reason: "Istio retry policy with exponential backoff"
- path: "istio/timeout.yaml"
must_contain: ["VirtualService", "timeout"]
reason: "Request timeout configuration"
- path: "istio/rate-limiting.yaml"
must_contain: ["EnvoyFilter", "ratelimit"]
reason: "Rate limiting to prevent overload"
linkerd:
- path: "linkerd/service-profile.yaml"
must_contain: ["ServiceProfile", "routes", "timeout"]
reason: "Linkerd service profile with timeouts and retries"
- path: "linkerd/traffic-split.yaml"
must_contain: ["TrafficSplit", "backend", "weight"]
reason: "Traffic splitting for multi-leader routing"
consul:
- path: "consul/service-mesh.hcl"
must_contain: ["service", "connect", "sidecar_service"]
reason: "Consul Connect service mesh configuration"
- path: "consul/intentions.hcl"
must_contain: ["intention", "source", "destination", "action"]
reason: "Service-to-service authorization"
# Scaffolding files that should be created as starting points
scaffolding:
- path: "docs/"
type: "directory"
description: "Documentation for distributed system design decisions"
- path: "services/"
type: "directory"
description: "Service implementations (replication, partitioning, saga, etc.)"
- path: "config/"
type: "directory"
description: "Configuration files for consistency, partitioning, caching"
- path: "monitoring/"
type: "directory"
description: "Monitoring configurations for distributed system metrics"
- path: "examples/"
type: "directory"
description: "Working code examples for patterns (circuit breaker, saga, etc.)"
- path: "scripts/"
type: "directory"
description: "Utility scripts for testing, chaos engineering, failover"
- path: "docs/architecture.md"
type: "file"
template: |
# Distributed System Architecture
## CAP Theorem Choice
**Selected:** [CP / AP]
**Rationale:**
- [Why this choice makes sense for the use case]
- [Trade-offs accepted]
## PACELC Analysis
- **If Partition:** [Availability / Consistency]
- **Else (normal):** [Latency / Consistency]
**System classification:** [PC/EL, PA/EL, etc.]
## Consistency Model
**Selected:** [Strong / Eventual / Causal / Bounded Staleness]
**Rationale:**
- [Business requirements]
- [Performance requirements]
## Replication Pattern
**Selected:** [Leader-Follower / Multi-Leader / Leaderless]
**Configuration:**
- Replication type: [Synchronous / Asynchronous]
- Number of replicas: [N]
- Quorum (if applicable): W=[W], R=[R], N=[N]
## Partitioning Strategy
**Selected:** [Hash / Range / Geographic]
**Rationale:**
- [Query patterns]
- [Data distribution]
- [Hot spot prevention]
## Resilience Patterns
- Circuit Breaker: [Yes/No]
- Bulkhead: [Yes/No]
- Retry with backoff: [Yes/No]
- Timeout: [Yes/No]
- Rate limiting: [Yes/No]
## Service Discovery
**Method:** [Client-side / Server-side / Service Mesh]
**Tool:** [Consul / etcd / Eureka / Istio / Linkerd]
## Transaction Pattern
**Selected:** [ACID / Saga / Event Sourcing + CQRS / None]
**Saga type (if applicable):** [Choreography / Orchestration]
- path: "docs/consistency-model.md"
type: "file"
template: |
# Consistency Model
## Chosen Model
**[Strong Consistency / Eventual Consistency / Causal Consistency / Bounded Staleness]**
## Use Cases and Trade-offs
### Use Cases Requiring This Model
- [Use case 1: e.g., bank transfers require strong consistency]
- [Use case 2: e.g., social feed can tolerate eventual consistency]
### Trade-offs Accepted
**Performance:**
- Latency: [Impact on latency]
- Throughput: [Impact on throughput]
**Availability:**
- Availability during partition: [Impact]
## Configuration
[Document specific configuration parameters, quorum settings, etc.]
- path: "docs/resilience-patterns.md"
type: "file"
template: |
# Resilience Patterns
## Circuit Breaker
**Implemented:** [Yes/No]
**Configuration:**
- Failure threshold: [e.g., 5 failures]
- Timeout: [e.g., 60 seconds]
- Success threshold: [e.g., 2 successes in HALF-OPEN]
## Retry Strategy
**Policy:** [Exponential backoff / Fixed interval / No retry]
**Configuration:**
- Max retries: [e.g., 3]
- Initial delay: [e.g., 100ms]
- Max delay: [e.g., 10s]
- Jitter: [Yes/No]
## Timeout
**Request timeout:** [e.g., 30s]
**Connection timeout:** [e.g., 5s]
## Bulkhead Isolation
**Thread pools:**
- Service A: [pool size]
- Service B: [pool size]
## Rate Limiting
**Algorithm:** [Token bucket / Leaky bucket / Fixed window]
**Limits:** [e.g., 100 requests/minute per client]
- path: "README.md"
type: "file"
template: |
# Distributed System
This project implements a distributed system with the following characteristics:
## Architecture
- **CAP Choice:** [CP / AP]
- **Consistency Model:** [Strong / Eventual / Causal]
- **Replication:** [Leader-Follower / Multi-Leader / Leaderless]
- **Partitioning:** [Hash / Range / Geographic]
## Key Features
- Circuit breaker for fault tolerance
- [Saga pattern / Event sourcing] for distributed transactions
- Service discovery with [tool]
- Distributed tracing with [tool]
## Quick Start
See `docs/architecture.md` for detailed design decisions.
## Testing
- Unit tests: `make test`
- Integration tests: `make test-integration`
- Chaos tests: `make chaos-test`
## Monitoring
- Replication lag: [dashboard link]
- Circuit breaker state: [dashboard link]
- Saga progress: [dashboard link]
- path: ".gitignore"
type: "file"
template: |
# Python
__pycache__/
*.py[cod]
venv/
# Secrets
.env
secrets/
*.key
*.pem
# Terraform
.terraform/
*.tfstate
*.tfstate.backup
# Logs
*.log
logs/
# OS
.DS_Store
# Metadata
metadata:
primary_blueprints: ["api-first", "k8s", "infrastructure"]
contributes_to:
- "Distributed system architecture"
- "Microservices patterns"
- "High availability and fault tolerance"
- "Multi-datacenter and multi-region deployments"
- "Scalability and consistency trade-offs"
common_patterns:
- "Leader-follower replication with async/sync options"
- "Circuit breaker for preventing cascading failures"
- "Saga pattern for distributed transactions (choreography/orchestration)"
- "Consistent hashing for partitioning"
- "Event sourcing + CQRS for audit and performance"
- "Quorum-based consistency (W+R > N)"
- "Service mesh for resilience (Istio/Linkerd)"
integration_points:
kubernetes: "Deploy with pod anti-affinity, network policies, service mesh"
databases: "Configure replication, partitioning, consistency levels"
messaging: "Use for saga orchestration, event-driven architectures"
observability: "Distributed tracing, replication lag monitoring, circuit breaker metrics"
security: "mTLS for service-to-service communication, network segmentation"
typical_directory_structure: |
project/
├── docs/
│ ├── architecture.md # CAP/PACELC, consistency, replication, partitioning
│ ├── consistency-model.md # Detailed consistency model choice
│ ├── resilience-patterns.md # Circuit breaker, retry, timeout, bulkhead
│ └── runbook.md # Operational procedures
├── services/
│ ├── replication/
│ │ ├── leader-follower.py # Leader-follower implementation
│ │ ├── multi-leader.py # Multi-leader with conflict resolution
│ │ └── leaderless.py # Quorum-based replication
│ ├── partitioning/
│ │ ├── consistent-hash.py # Consistent hashing
│ │ ├── range-partition.py # Range partitioning
│ │ └── geo-partition.py # Geographic partitioning
│ ├── saga/
│ │ ├── choreography.py # Event-driven saga
│ │ └── orchestration.py # Centralized saga orchestrator
│ ├── event-sourcing/
│ │ ├── event-store.py # Event store implementation
│ │ └── projections.py # Event replay and projections
│ ├── cqrs/
│ │ ├── write-model.py # Command side
│ │ └── read-model.py # Query side (denormalized)
│ ├── circuit-breaker/
│ │ └── circuit-breaker.py # Circuit breaker implementation
│ └── service-discovery/
│ └── consul-client.py # Service registry client
├── config/
│ ├── consistency-config.yaml # Consistency model settings
│ ├── quorum-config.yaml # W, R, N values
│ ├── cache-config.yaml # Caching strategy
│ └── service-discovery.yaml # Registry configuration
├── monitoring/
│ ├── replication-lag.yaml # Alert on high lag
│ ├── circuit-breaker-metrics.yaml # CB state changes
│ ├── saga-monitoring.yaml # Saga timeouts and failures
│ └── distributed-tracing.yaml # Jaeger/Zipkin config
├── k8s/ # Kubernetes manifests
│ ├── service-mesh.yaml # Istio/Linkerd
│ ├── pod-anti-affinity.yaml # Replica distribution
│ └── network-policies.yaml # Service-to-service communication
├── scripts/
│ ├── failover-test.sh # Test failover scenarios
│ └── chaos-test.sh # Chaos engineering experiments
└── examples/ # Working code examples
├── circuit-breaker/
├── saga-orchestration/
├── event-sourcing/
└── consistent-hashing/
tools:
replication:
- name: "PostgreSQL"
use_when: "Strong consistency, ACID transactions, leader-follower"
- name: "MongoDB"
use_when: "Flexible schema, leader-follower or multi-leader"
- name: "Cassandra"
use_when: "Leaderless replication, multi-datacenter, AP system"
- name: "DynamoDB"
use_when: "AWS-native, leaderless, AP system"
- name: "Cloud Spanner"
use_when: "Global SQL, strong consistency, CP system"
- name: "CockroachDB"
use_when: "Multi-cloud, strong consistency, PostgreSQL-compatible"
service_mesh:
- name: "Istio"
use_when: "Full-featured mesh, circuit breakers, retries, observability"
- name: "Linkerd"
use_when: "Lightweight, simple, Kubernetes-native"
- name: "Consul Connect"
use_when: "Multi-cloud, VM + Kubernetes, service discovery + mesh"
service_discovery:
- name: "Consul"
use_when: "Multi-datacenter, health checks, KV store"
- name: "etcd"
use_when: "Kubernetes-native, strongly consistent"
- name: "Eureka"
use_when: "Spring ecosystem, AP system"
messaging:
- name: "Kafka"
use_when: "Event sourcing, saga choreography, high throughput"
- name: "RabbitMQ"
use_when: "Saga orchestration, task queues, complex routing"
- name: "NATS"
use_when: "Lightweight, low latency, pub/sub"
validation_checks:
- "CAP/PACELC choice documented with rationale"
- "Consistency model chosen and configured"
- "Replication pattern implemented (leader-follower/multi-leader/leaderless)"
- "Partitioning strategy documented (hash/range/geographic)"
- "Circuit breaker configured with thresholds"
- "Retry policy with exponential backoff and jitter"
- "Timeouts set for all service calls"
- "Service discovery configured and health checks enabled"
- "Idempotency implemented for all distributed transactions"
- "Distributed tracing configured with correlation IDs"
- "Replication lag monitored and alerted"
- "Chaos engineering tests created and run regularly"
- "Saga compensating transactions tested"
- "Conflict resolution strategy documented (for multi-leader)"
- "Backup and disaster recovery plan documented"
Caching Strategies
Table of Contents
1. Cache-Aside (Lazy Loading) 2. Write-Through 3. Write-Behind (Write-Back) 4. Cache Invalidation 5. Best Practices
Cache-Aside (Lazy Loading)
Pattern
Read:
1. Check cache → hit? return
2. Miss? Query database
3. Store in cache, return
Write:
1. Write to database
2. Invalidate cache (optional)Implementation
import redis
import json
class CacheAside:
def __init__(self, cache, database):
self.cache = cache # Redis
self.db = database
def get(self, key):
# Try cache first
cached = self.cache.get(key)
if cached:
return json.loads(cached)
# Cache miss - query database
value = self.db.query(key)
if value:
self.cache.setex(key, 3600, json.dumps(value))
return value
def set(self, key, value):
# Write to database
self.db.write(key, value)
# Invalidate cache
self.cache.delete(key)Write-Through
Pattern
Write:
1. Write to cache
2. Cache writes to database synchronously
3. Return successImplementation
class WriteThrough:
def __init__(self, cache, database):
self.cache = cache
self.db = database
def set(self, key, value):
# Write to database first
self.db.write(key, value)
# Then update cache
self.cache.set(key, json.dumps(value))
def get(self, key):
# Always from cache
cached = self.cache.get(key)
return json.loads(cached) if cached else NoneWrite-Behind (Write-Back)
Pattern
Write:
1. Write to cache
2. Return success immediately
3. Cache writes to database asynchronously (batched)Benefits: Low latency writes
Trade-off: Data loss if cache fails before DB write
Cache Invalidation
Strategies
TTL (Time-To-Live):
cache.setex(key, 3600, value) # Expire after 1 hourEvent-Based:
# Invalidate on update
def update_user(user_id, data):
db.update(user_id, data)
cache.delete(f"user:{user_id}")Manual:
# Explicit invalidation
cache.delete(key)Best Practices
- Set appropriate TTLs
- Handle cache misses gracefully (thundering herd)
- Monitor cache hit rate
- Use consistent hashing for distributed caches
CAP and PACELC Theorem
Table of Contents
1. CAP Theorem Deep-Dive 2. PACELC Extension 3. PACELC Matrix 4. Real-World System Classification 5. Decision Framework
CAP Theorem Deep-Dive
Definition
In a distributed system experiencing a network partition, choose between:
- Consistency (C): All nodes see the same data at the same time
- Availability (A): Every request receives a response (success or failure)
- Partition Tolerance (P): System continues operating despite network failures
Critical Insight: Partition tolerance is mandatory in distributed systems. Network failures WILL occur. The choice is between C or A during partitions.
CAP Decision Tree
START: Designing distributed system
│
├─► Will network partitions occur?
│ Answer: YES (always in distributed systems)
│
├─► DURING PARTITION, choose:
│ │
│ ├─► CP (Consistency + Partition Tolerance)
│ │ │
│ │ ├─ Behavior: Reject writes, return errors
│ │ ├─ Use when: Correctness > availability
│ │ ├─ Examples:
│ │ │ - Banking (account balance must be correct)
│ │ │ - Inventory (prevent overselling)
│ │ │ - Seat booking (no double-booking)
│ │ │ - Distributed locks
│ │ │
│ │ ├─ Systems: HBase, MongoDB (default), etcd, Consul
│ │ └─ Trade-off: System unavailable during partition
│ │
│ └─► AP (Availability + Partition Tolerance)
│ │
│ ├─ Behavior: Accept writes, resolve conflicts later
│ ├─ Use when: Availability > strict consistency
│ ├─ Examples:
│ │ - Social media (likes, follows, posts)
│ │ - Shopping cart (can merge carts)
│ │ - Product catalog (stale prices OK briefly)
│ │ - Analytics (approximate counts acceptable)
│ │
│ ├─ Systems: Cassandra, DynamoDB, Riak, Couchbase
│ └─ Trade-off: Stale reads, conflict resolution neededCP Systems: Consistency + Partition Tolerance
Behavior During Partition:
┌─────────────────────────────────────────────────────┐
│ CP System During Partition │
├─────────────────────────────────────────────────────┤
│ Datacenter A │ Network Partition │
│ ┌─────────┐ │ │
│ │ Leader │ │ Datacenter B │
│ │ │ │ ┌─────────┐ │
│ │ Can │ │ │Follower │ │
│ │ serve │ │ │ │ │
│ │ writes │ │ ╳╳╳╳╳ │ Cannot │ │
│ │ (quorum)│◄───────┼───╳╳╳╳╳───│ reach │ │
│ └─────────┘ │ ╳╳╳╳╥ │ quorum │ │
│ ▲ │ │ │ │
│ │ │ │ REJECTS │ │
│ Succeeds │ │ WRITES │ │
│ │ └─────────┘ │
│ │ ▲ │
│ │ │ │
│ │ Returns │
│ │ Error │
└─────────────────────────────────────────────────────┘
Result: Only partition with quorum accepts writes
Other partition rejects writes (unavailable)Examples:
- MongoDB (default): Requires majority for writes during partition
- etcd: Raft consensus requires quorum (N/2 + 1)
- HBase: Region servers must reach ZooKeeper quorum
AP Systems: Availability + Partition Tolerance
Behavior During Partition:
┌─────────────────────────────────────────────────────┐
│ AP System During Partition │
├─────────────────────────────────────────────────────┤
│ Datacenter A │ Network Partition │
│ ┌─────────┐ │ │
│ │ Node 1 │ │ Datacenter B │
│ │ │ │ ┌─────────┐ │
│ │ Accepts │ │ │ Node 2 │ │
│ │ write │ │ │ │ │
│ │ x=1 │ │ ╥╥╥╥╥ │ Accepts │ │
│ │ │ │ ╥╥╥╥╥ │ write │ │
│ └─────────┘ │ ╥╥╥╥╥ │ x=2 │ │
│ ▲ │ └─────────┘ │
│ │ │ ▲ │
│ Client A │ Client B │
│ (succeeds) │ (succeeds) │
│ │ │
│ When partition heals: │
│ Conflict: x=1 vs x=2 │
│ Resolution: Last-Write-Wins, vector clocks, etc. │
└─────────────────────────────────────────────────────┘
Result: Both partitions accept writes
Conflicts resolved after partition healsExamples:
- Cassandra: Tunable consistency (can choose AP with QUORUM ONE)
- DynamoDB: Eventual consistency by default
- Riak: Last-Write-Wins or sibling resolution
PACELC Extension
Why PACELC?
CAP only describes behavior during partitions. What about normal operations (99%+ of the time)?
PACELC: If Partition, choose A or C; Else (normal), choose Latency or Consistency
PACELC Components
P-A-C-E-L-C
│ │ │ │ │ │
│ │ │ │ │ └─ Consistency (normal operation)
│ │ │ │ └─── Latency (normal operation)
│ │ │ └───── Else (no partition)
│ │ └─────── Consistency (during partition)
│ └───────── Availability (during partition)
└─────────── Partition occursPACELC Decision Matrix
┌─────────────────────────────────────────────────────┐
│ PACELC Trade-offs │
├─────────────────────────────────────────────────────┤
│ │
│ IF PARTITION OCCURS: │
│ PA: Choose Availability (stale reads OK) │
│ PC: Choose Consistency (reject some requests) │
│ │
│ ELSE (Normal Operation): │
│ EL: Choose Latency (async replication, fast) │
│ EC: Choose Consistency (sync replication, slow) │
│ │
│ Common Combinations: │
│ PA/EL: High availability, eventual consistency │
│ PC/EC: Strong consistency, reduced availability │
│ PC/EL: Hybrid (consistent during partition, │
│ optimized for latency normally) │
└─────────────────────────────────────────────────────┘PACELC Matrix
Database Classification
| System | P: A or C? | E: L or C? | Notes |
|---|---|---|---|
| Spanner | PC | EC | Global strong consistency |
| DynamoDB | PA | EL | Eventual consistency by default |
| Cassandra | PA | EL | Tunable (can choose PC/EC) |
| MongoDB | PC | EC | Default: strong consistency |
| Cosmos DB | PA/PC | EL/EC | 5 consistency levels |
| VoltDB | PC | EC | In-memory, strong consistency |
| Riak | PA | EL | Eventual consistency |
| etcd | PC | EC | Raft consensus |
| Redis | PC | EC | Cluster mode with sync rep |
Detailed System Behaviors
PA/EL Systems (DynamoDB, Cassandra):
During Partition:
- Accept writes in both partitions (A)
- Conflicts resolved later
Normal Operation:
- Async replication (L)
- Low latency writes
- Eventual consistencyPC/EC Systems (MongoDB, etcd):
During Partition:
- Only majority partition accepts writes (C)
- Minority partition rejects writes
Normal Operation:
- Synchronous replication (C)
- Higher latency (wait for replicas)
- Strong consistencyTunable Systems (Cosmos DB):
Cosmos DB offers 5 consistency levels:
1. Strong (PC/EC)
2. Bounded Staleness (PC/EC with lag bound)
3. Session (PA/EL with session consistency)
4. Consistent Prefix (PA/EL with ordered reads)
5. Eventual (PA/EL)Real-World System Classification
Banking System (PC/EC)
Requirements:
- Account balance must be correct
- No overdrafts allowed
- Transactions must be atomic
Design:
├─ During Partition: Choose Consistency (PC)
│ Reject writes if quorum unavailable
│
└─ Normal Operation: Choose Consistency (EC)
Synchronous replication to all replicas
Higher latency acceptable for correctness
Technology: Spanner, VoltDB, PostgreSQL (sync replication)Social Media Feed (PA/EL)
Requirements:
- Users can always post
- Slight delay in seeing posts OK
- Likes/follows can be approximate
Design:
├─ During Partition: Choose Availability (PA)
│ Accept posts in all datacenters
│ Resolve conflicts later (merge likes)
│
└─ Normal Operation: Choose Latency (EL)
Async replication for low latency
Eventual consistency acceptable
Technology: Cassandra, DynamoDB, CouchbaseE-Commerce (Hybrid: PC for inventory, PA for catalog)
Inventory:
├─ PC/EC: Strong consistency for stock count
└─ Prevent overselling
Product Catalog:
├─ PA/EL: Eventual consistency for descriptions, prices
└─ Stale data acceptable briefly
Shopping Cart:
├─ PA/EL: Eventually consistent, can merge carts
└─ High availability criticalDecision Framework
Step 1: Identify Requirements
Ask: 1. Is data correctness critical? (banking, inventory) 2. Is availability more important than consistency? (social media) 3. Are users globally distributed? (latency matters) 4. Can the system tolerate stale reads? (analytics)
Step 2: Choose CAP Profile
Correctness Critical:
└─► CP (Consistency + Partition Tolerance)
Examples: Banking, inventory, booking
Availability Critical:
└─► AP (Availability + Partition Tolerance)
Examples: Social media, analytics, catalog
Hybrid (Different Per Feature):
└─► CP for critical data, AP for non-critical
Examples: E-commerce, SaaS platformsStep 3: Choose PACELC Profile
Low Latency Critical:
└─► PA/EL (Eventual consistency, async replication)
Examples: Social media, caching, analytics
Strong Consistency Critical:
└─► PC/EC (Strong consistency, sync replication)
Examples: Banking, financial systems
Middle Ground:
└─► PC/EL (Consistent during partition, optimized latency normally)
Examples: Session stores, collaborative editingStep 4: Select Technology
PA/EL Systems:
- Cassandra (wide-column, tunable)
- DynamoDB (key-value, managed)
- Riak (key-value, open-source)
PC/EC Systems:
- MongoDB (document, strong by default)
- etcd (key-value, Raft consensus)
- Spanner (SQL, global consistency)
Tunable Systems:
- Cosmos DB (multi-model, 5 levels)
- Cassandra (tunable quorum)
- PostgreSQL (async, sync, quorum replication)Example Decision Path
Use Case: Multi-Region Order System
Step 1: Requirements
- Orders must be processed (availability)
- Payment must be correct (consistency)
- Users globally distributed (latency)
Step 2: Hybrid Approach
├─ Order placement: PA/EL (accept orders anywhere)
├─ Payment processing: PC/EC (strong consistency)
└─ Order history: PA/EL (eventual consistency)
Step 3: Technology Choices
├─ Order service: DynamoDB (PA/EL)
├─ Payment service: PostgreSQL with sync replication (PC/EC)
└─ Read model (CQRS): Elasticsearch (PA/EL)
Step 4: Conflict Resolution
├─ Orders: Timestamps + vector clocks
├─ Payments: Saga pattern with compensation
└─ Idempotency: Unique request IDsBest Practices
Start with PACELC, not just CAP:
- CAP only covers partition scenarios
- PACELC covers both partition and normal operation
- Most systems operate normally 99%+ of the time
Don't Assume CA is Achievable:
- CA (Consistency + Availability without Partition Tolerance) is impossible in distributed systems
- Network partitions WILL occur
Use Tunable Consistency:
- Cassandra: Per-query consistency (ONE, QUORUM, ALL)
- Cosmos DB: 5 consistency levels
- Choose based on operation criticality
Test Partition Scenarios:
- Use chaos engineering (e.g., Chaos Monkey)
- Simulate network partitions in testing
- Verify behavior matches expectations (CP or AP)
Monitor Trade-offs:
- Track replication lag (PA/EL systems)
- Monitor quorum health (PC systems)
- Alert on inconsistency windows exceeding SLA
Consensus Algorithms
Table of Contents
1. What is Consensus 2. Raft Algorithm 3. Paxos Algorithm 4. When to Use Consensus 5. Consensus vs Replication
What is Consensus
Definition
Consensus is the problem of getting multiple nodes to agree on a single value, even in the presence of failures.
Requirements (FLP Impossibility)
Consensus algorithm must guarantee: 1. Agreement: All correct nodes decide on the same value 2. Validity: Decided value was proposed by some node 3. Termination: All correct nodes eventually decide
FLP Impossibility: Cannot guarantee all three in an asynchronous system with even one failure. Practical algorithms trade termination for safety.
Use Cases
- Leader election: Elect exactly one leader
- Distributed locks: Only one node acquires lock
- Configuration management: All nodes agree on config
- Atomic commit: All nodes commit or all abort
Raft Algorithm
Overview
Raft is a consensus algorithm designed for understandability. It's equivalent to Paxos in fault-tolerance and performance.
Raft Components
┌──────────────────────────────────────────────────────┐
│ Raft Roles │
├──────────────────────────────────────────────────────┤
│ LEADER: │
│ - Handles all client requests │
│ - Replicates log to followers │
│ - Only one leader per term │
│ │
│ FOLLOWER: │
│ - Passive: responds to leader/candidate requests │
│ - Becomes candidate if no heartbeat │
│ │
│ CANDIDATE: │
│ - Requests votes from other nodes │
│ - Becomes leader if wins majority │
└──────────────────────────────────────────────────────┘Raft Leader Election
┌──────────────────────────────────────────────────────┐
│ Raft Leader Election │
├──────────────────────────────────────────────────────┤
│ 1. Initial State: All nodes are followers │
│ Election timeout: 150-300ms (randomized) │
│ │
│ 2. Follower times out (no heartbeat from leader) │
│ → Becomes CANDIDATE │
│ → Increments term number │
│ → Votes for itself │
│ → Sends RequestVote RPCs to all nodes │
│ │
│ 3. Other nodes respond: │
│ ├─ Grant vote if: │
│ │ - Haven't voted this term │
│ │ - Candidate's log is up-to-date │
│ │ │
│ └─ Deny vote otherwise │
│ │
│ 4. Outcomes: │
│ ├─ Wins election (majority votes) │
│ │ → Becomes LEADER │
│ │ → Sends heartbeats to all nodes │
│ │ │
│ ├─ Another node wins election │
│ │ → Receives heartbeat from new leader │
│ │ → Becomes FOLLOWER │
│ │ │
│ └─ Split vote (no majority) │
│ → Times out │
│ → Starts new election (higher term) │
└──────────────────────────────────────────────────────┘Raft Log Replication
┌──────────────────────────────────────────────────────┐
│ Raft Log Replication │
├──────────────────────────────────────────────────────┤
│ Leader: [1][2][3][4][5] │
│ │ │ │ │ │ │
│ └──┼──┼──┼──┼──► AppendEntries RPC │
│ │ │ │ │ │
│ Follower 1: [1][2][3][4][5] (up-to-date) │
│ │
│ Follower 2: [1][2][3][ ][ ] (lagging) │
│ └──────────────► Receives entries 4,5 │
│ │
│ Follower 3: [1][2][X][4][ ] (conflict at 3) │
│ └──────────────► Deletes 3, gets 3,4,5 │
│ │
│ Commit: │
│ Leader commits entry when replicated to majority │
│ Leader notifies followers of commit index │
│ Followers apply committed entries to state machine │
└──────────────────────────────────────────────────────┘Raft Safety Properties
Election Safety: At most one leader per term Leader Append-Only: Leader never deletes/overwrites entries Log Matching: If two logs contain same entry at same index, all preceding entries are identical Leader Completeness: If entry committed in term T, present in all leaders of future terms State Machine Safety: If node applies entry at index i, no other node applies different entry at i
Example: etcd
# Start etcd cluster (3 nodes for fault tolerance)
etcd --name node1 --initial-cluster node1=http://10.0.1.1:2380,node2=http://10.0.1.2:2380,node3=http://10.0.1.3:2380
etcd --name node2 --initial-cluster node1=http://10.0.1.1:2380,node2=http://10.0.1.2:2380,node3=http://10.0.1.3:2380
etcd --name node3 --initial-cluster node1=http://10.0.1.1:2380,node2=http://10.0.1.2:2380,node3=http://10.0.1.3:2380
# Write key-value (goes to leader, replicated via Raft)
etcdctl put mykey "myvalue"
# Read key (can read from any node)
etcdctl get mykey
# Check cluster status
etcdctl endpoint status --cluster
# Simulate leader failure (kill leader process)
# Raft elects new leader automatically (election timeout ~300ms)
# Writes continue with new leader
# Leader election observable:
# - Term number increases
# - New leader sends heartbeats
# - Old leader becomes follower when recoveredPaxos Algorithm
Overview
Paxos is the original consensus algorithm, known for complexity but proven correct.
Paxos Roles
┌──────────────────────────────────────────────────────┐
│ Paxos Roles │
├──────────────────────────────────────────────────────┤
│ PROPOSER: │
│ - Proposes values │
│ - Coordinates consensus rounds │
│ │
│ ACCEPTOR: │
│ - Votes on proposed values │
│ - Majority of acceptors must agree │
│ │
│ LEARNER: │
│ - Learns chosen value │
│ - Passive (doesn't participate in voting) │
│ │
│ Note: Nodes can play multiple roles │
└──────────────────────────────────────────────────────┘Paxos Phases
┌──────────────────────────────────────────────────────┐
│ Paxos Two-Phase Process │
├──────────────────────────────────────────────────────┤
│ PHASE 1: Prepare │
│ Proposer: │
│ 1. Choose proposal number n (higher than any seen) │
│ 2. Send PREPARE(n) to majority of acceptors │
│ │
│ Acceptors: │
│ 3. If n > highest seen: │
│ - Promise not to accept proposals < n │
│ - Reply with highest accepted proposal (if any) │
│ │
│ PHASE 2: Accept │
│ Proposer: │
│ 4. If majority promises: │
│ - If any acceptor returned value, use it │
│ - Otherwise, use own value │
│ 5. Send ACCEPT(n, value) to majority │
│ │
│ Acceptors: │
│ 6. If haven't promised higher n: │
│ - Accept proposal (n, value) │
│ - Reply with acceptance │
│ │
│ LEARN: │
│ 7. Majority accepts → Value is chosen │
│ 8. Notify learners of chosen value │
└──────────────────────────────────────────────────────┘Paxos Example
Scenario: 5 nodes (A, B, C, D, E) need to agree on value
Round 1:
Proposer A: PREPARE(1) → B, C, D
B, C, D: Promise (no prior proposals)
Proposer A: ACCEPT(1, "value_A") → B, C, D
B, C, D: Accept
Result: "value_A" chosen
Round 2 (concurrent with Round 1):
Proposer E: PREPARE(2) → B, C, D
B, C, D: Promise (already accepted (1, "value_A"))
Proposer E: ACCEPT(2, "value_A") → Must use "value_A"
B, C, D: Accept
Result: "value_A" chosen (consistency maintained)Multi-Paxos
Single Paxos: Expensive (2 round trips per decision)
Multi-Paxos: Optimize for multiple decisions
- Elect stable leader
- Leader skips Phase 1 for subsequent proposals
- Similar to Raft (leader-based)
Used by: Google Chubby, Google SpannerWhen to Use Consensus
Scenarios Requiring Consensus
Leader Election:
Problem: Need exactly one leader
Solution: Raft/Paxos elects leader with majority votes
Examples: etcd, Consul, ZooKeeperDistributed Locks:
Problem: Multiple services need exclusive access to resource
Solution: Consensus on lock ownership
Examples: etcd locks, Consul sessionsConfiguration Management:
Problem: All nodes need same configuration
Solution: Consensus on config values
Examples: etcd for Kubernetes configAtomic Commit (Distributed Transactions):
Problem: All nodes must commit or all must abort
Solution: Two-phase commit with consensus for coordinator election
Examples: Spanner, CockroachDBWhen NOT to Use Consensus
High Write Throughput:
Consensus is slow (coordination overhead)
Alternative: Leaderless replication (Dynamo-style)Eventual Consistency Acceptable:
Consensus is overkill for social media, caching
Alternative: Asynchronous replicationSingle Datacenter:
Leader-follower sufficient (simpler than consensus)
Alternative: PostgreSQL streaming replicationConsensus vs Replication
Comparison
| Aspect | Consensus (Raft/Paxos) | Replication (Leader-Follower) |
|---|---|---|
| Leader Election | Automatic (consensus) | Manual or external tool |
| Consistency | Strong (majority) | Strong (sync) or Eventual (async) |
| Availability | Majority needed | Leader needed |
| Complexity | High | Medium |
| Latency | Higher (2 round trips) | Lower (1 round trip) |
| Use Case | Critical systems | General replication |
Decision Framework
Choose Consensus When:
├─ Automatic leader election required
├─ Strong consistency critical
├─ Partition tolerance essential
└─ Complexity acceptable
Choose Replication When:
├─ Manual failover acceptable
├─ Lower latency needed
├─ Simpler operations preferred
└─ Eventual consistency OKTechnology Examples
Consensus-Based:
- etcd: Raft for key-value store
- Consul: Raft for service discovery, configuration
- ZooKeeper: ZAB (Zookeeper Atomic Broadcast, Paxos-like)
- CockroachDB: Raft for distributed SQL
- TiDB: Raft for distributed SQL
Replication-Based:
- PostgreSQL: Streaming replication (leader-follower)
- MySQL: Binary log replication
- MongoDB: Replica sets (with automatic failover, not full consensus)
- Redis: Sentinel (master-replica with failover)
Best Practices
Cluster Size:
- Odd numbers (3, 5, 7) for quorum
- 3 nodes: Tolerates 1 failure
- 5 nodes: Tolerates 2 failures
- More nodes = higher latency
Network Stability:
- Consensus sensitive to network partitions
- Ensure low-latency, stable network
- Co-locate nodes in same datacenter if possible
Monitoring:
- Track leader elections (frequency)
- Monitor quorum health
- Alert on slow consensus rounds
- Watch for split votes
Testing:
- Chaos engineering (kill nodes during consensus)
- Network partition tests
- Leader election under load
Consistency Models
Table of Contents
1. Consistency Spectrum 2. Strong Consistency (Linearizability) 3. Sequential Consistency 4. Causal Consistency 5. Eventual Consistency 6. Bounded Staleness 7. Choosing Consistency Models
Consistency Spectrum
Strongest ◄─────────────────────────────────────────────► Weakest
│ │ │ │ │
│ │ │ │ │
Linearizable Sequential Causal Bounded Eventual
(Strictest) (Program (Causally Staleness (Converges
order) ordered) (Time-bounded) eventually)
Trade-offs:
Latency: Highest →→→→→→→→→→→→→→→→→→→→→→→→→→→→→→ Lowest
Throughput: Lowest →→→→→→→→→→→→→→→→→→→→→→→→→→→→→→ Highest
Availability: Low →→→→→→→→→→→→→→→→→→→→→→→→→→→→→→ High
Complexity: Low →→→→→→→→→→→→→→→→→→→→→→→→→→→→→→ HighStrong Consistency (Linearizability)
Definition
All operations appear to execute atomically in some sequential order. Once a write completes, all subsequent reads see that value or a newer one.
Formal Guarantee
If operation A completes before operation B begins, then B must see the effects of A.
Timeline Example
┌──────────────────────────────────────────────────────┐
│ Strong Consistency (Linearizable) │
├──────────────────────────────────────────────────────┤
│ Time → │
│ │
│ Client A: Write(x=1) ───┬───► [Complete] │
│ │ │
│ │ (replication latency) │
│ │ │
│ Client B: └──────► Read(x) → 1 │
│ │ │
│ Client C: └───► Read(x) → 1 │
│ │
│ Guarantee: All reads after write see new value │
│ No stale reads allowed │
└──────────────────────────────────────────────────────┘Implementation Approaches
1. Single-Leader with Synchronous Replication:
┌────────────┐
│ Leader │ ◄── All writes
└─────┬──────┘
│ (sync replication)
├──────────┬──────────┐
▼ ▼ ▼
Follower Follower Follower
(wait ACK) (wait ACK) (wait ACK)
│ │ │
└──────────┴──────────┘
│
Write confirmed only after
all replicas ACK2. Consensus Algorithms (Raft, Paxos):
- Leader election + log replication
- Majority quorum required
- Examples: etcd, Consul
3. Two-Phase Commit (2PC):
- Coordinator + participants
- Prepare phase + commit phase
- Blocking (not recommended for high availability)
Use Cases
Financial Transactions:
-- Bank transfer: Must be atomic and consistent
BEGIN TRANSACTION;
UPDATE accounts SET balance = balance - 100 WHERE id = 'A';
UPDATE accounts SET balance = balance + 100 WHERE id = 'B';
COMMIT;
-- Subsequent read must see both updates
SELECT balance FROM accounts WHERE id IN ('A', 'B');Inventory Management:
Read stock: 5 items
Client attempts purchase: 2 items
Write new stock: 3 items
All subsequent reads must see 3 (or fewer if another purchase)
Never see stale value of 5Seat/Ticket Booking:
- Prevent double-booking
- Once seat sold, all clients see it as unavailable
Trade-offs
Benefits:
- ✅ Simplifies application logic (no conflicts)
- ✅ Immediate consistency guarantees
- ✅ Easy to reason about
Costs:
- ❌ Higher latency (coordination overhead)
- ❌ Reduced availability (partition blocks writes)
- ❌ Lower throughput (synchronous operations)
Technology Examples
| Database | Strong Consistency Implementation |
|---|---|
| Spanner | TrueTime API + Paxos |
| VoltDB | In-memory, single-threaded |
| MongoDB | Majority write concern (default) |
| etcd | Raft consensus |
| Consul | Raft consensus |
| PostgreSQL | Synchronous replication |
Sequential Consistency
Definition
Operations from each client appear in the order they were issued, but operations from different clients may be interleaved differently at different nodes.
Difference from Linearizability
- Linearizability: Global real-time ordering
- Sequential: Per-client ordering, but no global time
Timeline Example
┌──────────────────────────────────────────────────────┐
│ Sequential Consistency │
├──────────────────────────────────────────────────────┤
│ Client A: Write(x=1) → Write(x=2) │
│ (Order preserved) │
│ │
│ Client B: Read(x) → May see 0, 1, or 2 │
│ But if sees 2, cannot later see 1 │
│ (Order within client preserved) │
│ │
│ Node 1 sees: x=1, x=2 │
│ Node 2 sees: x=1, x=2 (Same order) │
│ │
│ Guarantee: Per-client program order preserved │
│ But no real-time ordering across clients │
└──────────────────────────────────────────────────────┘Use Cases
- Distributed caches with invalidation
- Session stores
- Collaborative applications (weaker than causal)
Implementation
- Lamport clocks
- Vector clocks (partial)
- FIFO queues per client
Causal Consistency
Definition
Operations that are causally related are seen by all nodes in the same order. Concurrent operations may be seen in different orders.
Causality Rules
Event A → Event B (A "happens-before" B) if:
1. A and B occur on same process, A before B
2. A is send(message), B is receive(message)
3. Transitivity: A→B and B→C, then A→C
Concurrent events (A || B):
Neither A→B nor B→A
Can be observed in any orderTimeline Example
┌──────────────────────────────────────────────────────┐
│ Causal Consistency Example │
├──────────────────────────────────────────────────────┤
│ Alice: Post message A │
│ │ │
│ │ (causes - Bob sees A first) │
│ ▼ │
│ Bob: Reply B (to A) │
│ │ │
│ │ (causes - Carol sees A and B first) │
│ ▼ │
│ Carol: Reply C (to B) │
│ │
│ Guarantee: All users see messages A → B → C │
│ (Causally ordered) │
│ │
│ Charlie: Post X (concurrent with A, B, C) │
│ │
│ No Guarantee: X may appear anywhere in timeline │
│ (Not causally related to A, B, C) │
│ │
│ Possible orderings for users: │
│ - User 1 sees: A, B, X, C │
│ - User 2 sees: X, A, B, C │
│ - User 3 sees: A, B, C, X │
│ │
│ Invalid ordering: B, A, C (violates causality) │
└──────────────────────────────────────────────────────┘Use Cases
Chat Applications:
User A: "What's for dinner?"
User B: "Pizza!" (reply to A)
User C: "Sounds great!" (reply to B)
All users must see:
"What's for dinner?" → "Pizza!" → "Sounds great!"
Concurrent message: "Meeting at 3pm" may appear anywhereCollaborative Editing:
User A: Add paragraph at line 10
User B: Edit paragraph at line 10 (depends on A's add)
All editors must see A's add before B's editComment Threads:
- Replies to comments preserve causality
- Concurrent comments can appear in any order
Implementation Approaches
1. Vector Clocks:
class VectorClock:
def __init__(self, node_id, nodes):
self.node_id = node_id
self.clock = {node: 0 for node in nodes}
def increment(self):
self.clock[self.node_id] += 1
def update(self, other_clock):
for node, count in other_clock.items():
self.clock[node] = max(self.clock[node], count)
self.increment()
def happens_before(self, other):
# self → other if self ≤ other and self ≠ other
return (all(self.clock[n] <= other.clock[n] for n in self.clock)
and self.clock != other.clock)
def concurrent(self, other):
return not (self.happens_before(other) or other.happens_before(self))2. Lamport Timestamps:
class LamportClock:
def __init__(self):
self.time = 0
def tick(self):
self.time += 1
return self.time
def update(self, received_time):
self.time = max(self.time, received_time) + 1
return self.time3. Database Support:
- Azure Cosmos DB: Session consistency level
- Cassandra: WITH CONSISTENCY QUORUM + lightweight transactions
Trade-offs
Benefits:
- ✅ Stronger than eventual, weaker than strong
- ✅ Better performance than strong consistency
- ✅ Intuitive for users (causality preserved)
- ✅ Suitable for collaborative applications
Costs:
- ❌ More complex than eventual consistency
- ❌ Requires tracking causality metadata
- ❌ Higher storage overhead (vector clocks)
Eventual Consistency
Definition
If no new updates are made, all replicas will eventually converge to the same value. Stale reads are possible.
Timeline Example
┌──────────────────────────────────────────────────────┐
│ Eventual Consistency Timeline │
├──────────────────────────────────────────────────────┤
│ Time → │
│ │
│ Client A: Write(x=1) ──► Leader (success) │
│ │ │
│ │ (async replication starts) │
│ │ │
│ Client B: └──► Read(x) → 0 (STALE!) │
│ │ │
│ │ (replication continues) │
│ │ │
│ Client B (later): └──► Read(x) → 1 (fresh) │
│ │
│ Guarantee: Eventually consistent (seconds to minutes)│
│ Intermediate stale reads possible │
└──────────────────────────────────────────────────────┘Conflict Resolution Strategies
1. Last-Write-Wins (LWW):
def resolve_lww(value1, timestamp1, value2, timestamp2):
if timestamp1 > timestamp2:
return value1
return value22. Application-Specific Merge:
def merge_shopping_carts(cart1, cart2):
# Union of items with quantity sum
merged = {}
for item_id, qty in cart1.items():
merged[item_id] = qty
for item_id, qty in cart2.items():
merged[item_id] = merged.get(item_id, 0) + qty
return merged3. Conflict-Free Replicated Data Types (CRDTs):
# G-Counter (Grow-only counter)
class GCounter:
def __init__(self, node_id, nodes):
self.node_id = node_id
self.counts = {node: 0 for node in nodes}
def increment(self):
self.counts[self.node_id] += 1
def value(self):
return sum(self.counts.values())
def merge(self, other):
for node, count in other.counts.items():
self.counts[node] = max(self.counts[node], count)Use Cases
Social Media:
- Likes, follows, unfollows (counts can be approximate)
- Post feeds (slight delay acceptable)
- Profile updates (stale OK briefly)
Product Catalogs:
- Prices, descriptions (brief staleness OK)
- Inventory counts (with reservation system)
DNS:
- Zone updates propagate eventually
- TTL-based caching
Analytics:
- View counts, metrics (approximation acceptable)
- Aggregations (eventual consistency sufficient)
Implementation Techniques
1. Asynchronous Replication:
Leader → Log entry → Async send to followers
↓
Return success
(Don't wait for followers)2. Anti-Entropy (Gossip Protocol):
def gossip_protocol(node, peers, interval=10):
while True:
# Select random peer
peer = random.choice(peers)
# Exchange data
my_data = node.get_data()
peer_data = peer.get_data()
# Merge (reconcile differences)
node.merge(peer_data)
peer.merge(my_data)
time.sleep(interval)3. Read Repair:
def read_with_repair(key, replicas, quorum):
# Read from quorum nodes
responses = [replica.read(key) for replica in replicas[:quorum]]
# Find most recent value
latest = max(responses, key=lambda r: r.timestamp)
# Repair stale replicas in background
for replica, response in zip(replicas, responses):
if response.timestamp < latest.timestamp:
asyncio.create_task(replica.write(key, latest))
return latest.valueTrade-offs
Benefits:
- ✅ Low latency (no coordination)
- ✅ High availability (accepts writes anytime)
- ✅ Scales better (no synchronous waits)
- ✅ Partition-tolerant
Costs:
- ❌ Application must handle stale reads
- ❌ Conflict resolution complexity
- ❌ Hard to reason about (non-deterministic)
- ❌ Testing challenges
Bounded Staleness
Definition
Reads may lag behind writes, but staleness is bounded by time or number of versions.
Timeline Example
┌──────────────────────────────────────────────────────┐
│ Bounded Staleness (Bound: 5 seconds) │
├──────────────────────────────────────────────────────┤
│ Time → │
│ │
│ T=0: Write(x=1) ──► Leader │
│ │
│ T=2: Read(x) → May return 0 or 1 (within bound) │
│ │
│ T=4: Read(x) → May return 0 or 1 (within bound) │
│ │
│ T=6: Read(x) → MUST return 1 (bound exceeded) │
│ │
│ Guarantee: Staleness ≤ 5 seconds │
│ OR │
│ Guarantee: Staleness ≤ K versions behind │
└──────────────────────────────────────────────────────┘Configuration Examples
Time-Based Bound:
-- Azure Cosmos DB: Bounded Staleness
-- Max lag: 10 seconds OR 1000 operations
CREATE COLLECTION my_collection
WITH CONSISTENCY_LEVEL = 'BoundedStaleness',
MAX_STALENESS_PREFIX = 1000,
MAX_STALENESS_INTERVAL = 10;Version-Based Bound:
def read_with_bound(key, max_versions_behind):
# Read with staleness bound
current_version = leader.get_version(key)
replica_value, replica_version = replica.read(key)
# If too stale, read from leader
if current_version - replica_version > max_versions_behind:
return leader.read(key)
return replica_valueUse Cases
Real-Time Dashboards:
- Metrics with acceptable lag (e.g., 10 seconds)
- SLA: "Dashboard data max 30 seconds old"
Inventory with Buffer:
- Stock count can be slightly stale
- Buffer ensures no overselling
Leaderboards:
- Slight delay acceptable (eventual rankings)
- Bound: "Rank updates within 1 minute"
Trade-offs
Benefits:
- ✅ Middle ground: consistency + performance
- ✅ Predictable staleness window
- ✅ Suitable for real-time systems with tolerance
Costs:
- ❌ More complex than eventual
- ❌ Requires monitoring lag
- ❌ Must handle bound violations
Choosing Consistency Models
Decision Framework
START: Choose consistency model
│
├─► Money involved? → Strong Consistency
│
├─► Double-booking unacceptable? → Strong Consistency
│
├─► Causality important (chat, edits)? → Causal Consistency
│
├─► Stale reads tolerable with time bound? → Bounded Staleness
│
├─► Read-heavy, stale tolerable? → Eventual Consistency
│
└─► Default? → Eventual (then strengthen if needed)Use Case Matrix
| Use Case | Consistency Model | Rationale |
|---|---|---|
| Bank account balance | Strong (Linearizable) | Money correctness critical |
| Seat booking (airline) | Strong (Linearizable) | No double-booking allowed |
| Inventory stock count | Strong or Bounded | Prevent overselling, buffer OK |
| Shopping cart | Eventual | Can merge carts, availability↑ |
| Product catalog | Eventual | Stale prices OK briefly |
| Collaborative editing | Causal | Preserve edit order |
| Chat messages | Causal | Preserve reply causality |
| Social media likes | Eventual | Approximation acceptable |
| DNS records | Eventual | Propagation delay expected |
| Real-time dashboard | Bounded Staleness | Lag acceptable within SLA |
Implementation Checklist
For Strong Consistency:
- [ ] Use synchronous replication or consensus (Raft, Paxos)
- [ ] Accept higher latency for correctness
- [ ] Plan for reduced availability during partitions
- [ ] Consider PostgreSQL sync replication, etcd, Spanner
For Eventual Consistency:
- [ ] Implement conflict resolution strategy (LWW, merge, CRDTs)
- [ ] Design for idempotency (safe retries)
- [ ] Monitor convergence time
- [ ] Consider Cassandra, DynamoDB, Riak
For Causal Consistency:
- [ ] Implement vector clocks or Lamport timestamps
- [ ] Track causality metadata
- [ ] Test causality violations
- [ ] Consider Azure Cosmos DB (Session consistency)
For Bounded Staleness:
- [ ] Define acceptable staleness window
- [ ] Monitor replication lag
- [ ] Alert on bound violations
- [ ] Consider Azure Cosmos DB (Bounded Staleness level)
Service Discovery
Table of Contents
1. Client-Side Discovery 2. Server-Side Discovery 3. Service Mesh
Client-Side Discovery
Architecture
Service Registry (Consul, etcd, Eureka)
↑
| (register)
|
Services (register on startup)
↑
| (query & call)
|
Client (queries registry, selects instance, calls directly)Implementation (Consul)
import consul
import random
import requests
class ServiceDiscovery:
def __init__(self, consul_host='localhost', consul_port=8500):
self.consul = consul.Consul(host=consul_host, port=consul_port)
def register(self, service_name, service_id, host, port, health_check_url):
self.consul.agent.service.register(
name=service_name,
service_id=service_id,
address=host,
port=port,
check=consul.Check.http(health_check_url, interval='10s')
)
def discover(self, service_name):
index, services = self.consul.health.service(service_name, passing=True)
return [{'host': s['Service']['Address'], 'port': s['Service']['Port']}
for s in services]
def call_service(self, service_name, path):
instances = self.discover(service_name)
if not instances:
raise Exception(f"No instances of {service_name}")
# Load balance (random)
instance = random.choice(instances)
url = f"http://{instance['host']}:{instance['port']}{path}"
return requests.get(url)
# Usage
sd = ServiceDiscovery()
sd.register('payment-service', 'payment-1', 'localhost', 8080, 'http://localhost:8080/health')
response = sd.call_service('payment-service', '/process-payment')Server-Side Discovery
Architecture
Client → Load Balancer → Service Registry → ServicesThe load balancer queries the service registry and routes requests.
Benefits: Simple clients
Trade-off: Load balancer as single point of failure
Service Mesh
Concept
Sidecar proxies handle service discovery, routing, retries, circuit breaking.
Examples
- Istio: Full-featured service mesh
- Linkerd: Lightweight service mesh
- Consul Connect: Service mesh from Consul
Architecture
Service A Container + Envoy Sidecar
↓ (routes through)
Service B Container + Envoy SidecarBenefits
- Decouples communication logic
- Centralized traffic management
- mTLS encryption
- Observability (tracing, metrics)
Trade-offs
- Operational complexity
- Latency overhead (proxy hop)
- Resource usage (sidecar per service)
Related skills
FAQ
What does the CAP theorem force you to choose?
During a network partition you must choose between Consistency (CP) or Availability (AP); partition tolerance is mandatory.
When should I use strong vs eventual consistency?
Use strong consistency for bank balances, inventory, and seat booking; use eventual consistency for social feeds, catalogs, and profiles.