Consistency Models
What is Consistency?
Section titled “What is Consistency?”Consistency answers a fundamental question: When you update data in one place, when do other parts of your system see that change?
Think of it like updating a shared document. Do you want everyone to see changes instantly (strong consistency), or is it okay if they see them a few seconds later (eventual consistency)?
The Consistency Spectrum
Section titled “The Consistency Spectrum”Consistency isn’t binary—it’s a spectrum from strongest to weakest:
Model 1: Strong Consistency
Section titled “Model 1: Strong Consistency”Strong consistency guarantees that all nodes see the same data immediately after a write completes.
How It Works
Section titled “How It Works”Key Characteristic: The write operation blocks until all replicas confirm. Only then does the client get a success response.
Real-World Example: Bank Account Balance
Section titled “Real-World Example: Bank Account Balance”Trade-offs
Section titled “Trade-offs”| Aspect | Strong Consistency |
|---|---|
| Data Accuracy | ✅ Always correct, no stale reads |
| Write Latency | ❌ Higher (waits for all replicas) |
| Availability | ❌ Lower (if one node is down, writes may fail) |
| Complexity | ❌ Higher (need coordination, locks) |
| Use Cases | Financial systems, inventory, critical state |
Model 2: Eventual Consistency
Section titled “Model 2: Eventual Consistency”Eventual consistency allows temporary differences between nodes, but guarantees all nodes will eventually converge to the same state.
How It Works
Section titled “How It Works”Key Characteristic: The write returns immediately after updating the leader. Replication happens asynchronously in the background.
Real-World Example: Social Media Likes
Section titled “Real-World Example: Social Media Likes”Trade-offs
Section titled “Trade-offs”| Aspect | Eventual Consistency |
|---|---|
| Data Accuracy | ⚠️ Temporary inconsistencies possible |
| Write Latency | ✅ Lower (returns immediately) |
| Availability | ✅ Higher (can write even if some nodes down) |
| Complexity | ✅ Lower (no coordination needed) |
| Use Cases | Social media, analytics, non-critical data |
Model 3: Causal Consistency
Section titled “Model 3: Causal Consistency”Causal consistency preserves cause-and-effect relationships. If event A causes event B, all nodes that see B must have already seen A.
The Problem It Solves
Section titled “The Problem It Solves”How It Works
Section titled “How It Works”Causal consistency uses vector clocks or logical timestamps to track dependencies:
Key Characteristic: Events are delivered in causal order, even if they arrive out of order.
Real-World Example: Chat Application
Section titled “Real-World Example: Chat Application”Trade-offs
Section titled “Trade-offs”| Aspect | Causal Consistency |
|---|---|
| Data Accuracy | ✅ Preserves logical relationships |
| Write Latency | ✅ Lower than strong (no global coordination) |
| Availability | ✅ Higher than strong consistency |
| Complexity | ⚠️ Medium (need dependency tracking) |
| Use Cases | Chat, comments, collaborative editing |
Choosing the Right Consistency Model
Section titled “Choosing the Right Consistency Model”Decision Framework
Section titled “Decision Framework”| Use Case | Consistency Model | Why |
|---|---|---|
| Bank balance | Strong | Can’t have incorrect balances |
| Inventory count | Strong | Can’t oversell products |
| Social media likes | Eventual | Speed > exact count |
| View counts | Eventual | Approximate is fine |
| Chat messages | Causal | Need logical order |
| Comment threads | Causal | Replies must follow posts |
| User profiles | Eventual | Can tolerate slight delays |
LLD ↔ HLD Connection
Section titled “LLD ↔ HLD Connection”How consistency models affect your class design:
Strong Consistency → Use Locks
Section titled “Strong Consistency → Use Locks”When you need strong consistency, use synchronization primitives:
1from threading import Lock2
3class Account:4 def __init__(self, balance):5 self._balance = balance6 self._lock = Lock() # Ensures atomic operations7
8 def transfer(self, amount, to_account):9 # Lock both accounts to prevent race conditions10 with self._lock:11 with to_account._lock:12 if self._balance >= amount:13 self._balance -= amount14 to_account._balance += amount15 return True16 return False1import java.util.concurrent.locks.ReentrantLock;2
3public class Account {4 private double balance;5 private final ReentrantLock lock = new ReentrantLock();6
7 public boolean transfer(double amount, Account toAccount) {8 // Lock both accounts to prevent race conditions9 lock.lock();10 try {11 toAccount.lock.lock();12 try {13 if (balance >= amount) {14 balance -= amount;15 toAccount.balance += amount;16 return true;17 }18 } finally {19 toAccount.lock.unlock();20 }21 } finally {22 lock.unlock();23 }24 return false;25 }26}Eventual Consistency → Use Version Numbers
Section titled “Eventual Consistency → Use Version Numbers”For eventual consistency, track versions to detect conflicts:
1class Document:2 def __init__(self, content):3 self.content = content4 self.version = 0 # Track version for conflict detection5
6 def update(self, new_content, client_version):7 if client_version != self.version:8 # Conflict detected - need merge strategy9 raise ConflictError("Version mismatch")10
11 self.content = new_content12 self.version += 113 return self.version1public class Document {2 private String content;3 private int version = 0; // Track version for conflict detection4
5 public int update(String newContent, int clientVersion) {6 if (clientVersion != version) {7 // Conflict detected - need merge strategy8 throw new ConflictException("Version mismatch");9 }10
11 content = newContent;12 version++;13 return version;14 }15}Causal Consistency → Track Dependencies
Section titled “Causal Consistency → Track Dependencies”For causal consistency, maintain dependency graphs:
1from typing import List, Set2
3class Event:4 def __init__(self, data, dependencies: List[int]):5 self.data = data6 self.dependencies = set(dependencies) # Events this depends on7 self.id = None # Assigned by system8
9class EventStore:10 def __init__(self):11 self.events = {} # id -> Event12 self.seen = set() # Events this node has seen13
14 def can_deliver(self, event: Event) -> bool:15 # Can only deliver if all dependencies are seen16 return event.dependencies.issubset(self.seen)17
18 def deliver(self, event: Event):19 if self.can_deliver(event):20 self.seen.add(event.id)21 # Process event...1import java.util.*;2
3public class Event {4 private String data;5 private Set<Integer> dependencies; // Events this depends on6 private Integer id;7
8 public boolean canDeliver(Set<Integer> seen) {9 // Can only deliver if all dependencies are seen10 return seen.containsAll(dependencies);11 }12}13
14class EventStore {15 private Map<Integer, Event> events = new HashMap<>();16 private Set<Integer> seen = new HashSet<>();17
18 public boolean canDeliver(Event event) {19 return event.canDeliver(seen);20 }21
22 public void deliver(Event event) {23 if (canDeliver(event)) {24 seen.add(event.id);25 // Process event...26 }27 }28}Key Takeaways
Section titled “Key Takeaways”What’s Next?
Section titled “What’s Next?”Now that you understand consistency models, let’s explore the fundamental theorem that governs these trade-offs:
Next up: CAP Theorem Deep Dive — Learn why you can’t have everything: Consistency, Availability, and Partition tolerance.