CAP Theorem & PACELC: A Senior Engineer's Deep Dive
On the evening of October 21, 2018, a GitHub engineer replaced a 100G optical transceiver at the US East Coast datacenter โ routine maintenance, done hundreds of times before. During the swap, the link between East and West Coast dropped. For exactly 43 seconds.
That was enough. The automated failover tool concluded East Coast was dead and promoted the West Coast replica to primary. When the link came back, both coasts believed they were the write authority. Two sources of truth. Six MySQL clusters with diverged data. GitHub chose Consistency over Availability โ halted new writes, and began manual reconciliation. The price: 24 hours and 11 minutes of service degradation from 43 seconds of network loss.
This guide answers the two questions that story leaves open:
- Why couldn't GitHub have both consistency and availability during those 43 seconds? โ That's where CAP theorem enters.
- During the other 99.99% of the time when the network is fine โ what is the system silently trading off? โ That's where PACELC answers what CAP leaves unsaid.
Table of Contents
- The Core Definition and the "P" Constraint
- Choosing Your Trade-off: CP vs. AP
- Senior Nuance #1: Granularity of CAP
- Senior Nuance #2: The Consistency Spectrum
- How CAP Works Internally
- Real-World Implementations
- Integration Patterns
- Pros and Cons
- Interview Questions
- PACELC โ What Happens When the Network is Fine
- Senior Deep Dive: Advanced Topics
1. The Core Definition and the "P" Constraint
The CAP theorem states that a distributed data store can only simultaneously guarantee two of the following three traits:
- Consistency (C): Every read receives the most recent write or an error. (Note: In the context of CAP, this implies Strong Consistency).
- Availability (A): Every request receives a non-error response, without the guarantee that it contains the most recent write.
- Partition Tolerance (P): The system continues to operate despite an arbitrary number of messages being dropped (or delayed) by the network between nodes.
The Hard Truth: You Cannot Choose "CA"
In any distributed system deployed across a network (e.g., multi-region clouds like AWS or GCP), network failures are inevitable. Cables get cut, switches fail, and packets drop. Therefore, Partition Tolerance is not optional; it is a mandatory reality. When a network partition occurs, the system must make a forced choice:
- Cancel the operation (decreasing availability) to ensure data remains completely in sync. (CP)
- Proceed with the operation (decreasing consistency) and risk serving stale data to the user. (AP)
The correct reading of CAP: "Choose 2 of 3" is the most common โ and most misleading โ framing. Partition tolerance is not a design choice; it is a physical reality. A network partition can happen because a shark bites a submarine cable, a technician pulls the wrong fiber, a switch reboots at 2am, or a Java GC pause makes a node look dead for 40 seconds. The real question is: when the partition happens, what does the system do โ and how does it recover after? Brewer himself published a 12-year correction paper (2012) clarifying exactly this point.
The Senior Mental Model: 5 Grounding Principles
- Consistency is a hierarchy, not a binary: Systems do not merely choose between "Strong" and "Eventual". The spectrum progresses from Linearizability (strict external real-time order) โ Sequential (consistent execution interleaving) โ Causal (dependency-ordered) โ Read-Your-Writes / Monotonic (session consistency) โ Eventual. Stronger models add cross-client coordination at the expense of latency.
- CAP applies ONLY during a partition: During normal operation (99.99% of time), the trade-off is governed by PACELC: If Partition: Availability (A) vs Consistency (C); Else: Latency (L) vs Consistency (C).
- CAP "Availability" is an absolute mathematical definition: Gilbert & Lynch proved that CAP Availability requires every non-failing node to return a non-error response. Returning an HTTP 500, a timeout, or a degraded fallback violates CAP Availability.
- "CP vs AP" is not a system-wide religion: Production architectures pick guarantees per operation. In an e-commerce platform, user inventory reserve is CP (rejecting write if quorum is lost), while review counts and product recommendation feeds are AP.
- The AP invoice is paid later: AP systems do not avoid consistency costs; they simply defer reconciliation to conflict resolution mechanisms (CRDTs, vector clocks, or Last-Write-Wins data loss).
Visual Representation
CAP Vertex: Consistency (C)
Every read receives the most recent write or an error. In CAP context, this refers to linearizable strong consistency.
Trade-Off Selection: CP (Consistency + Partition Tolerance)
When a network split occurs, the system stops writing/reading from partitioned partitions to prevent data divergence, sacrificing availability.
Network Partition Example
Steady State Operation
2. Choosing Your Trade-off: CP vs. AP
When aligning on non-functional requirements during a system design interview, your core decision is whether the specific workflow prioritizes Strong Consistency or High Availability.
When to Choose Consistency (CP)
If serving stale data causes catastrophic downstream effects, you must choose CP. If a network partition occurs, you stop serving the data (return an error) rather than returning an outdated state.
Use Cases:
- Financial Systems: Updating an order book for stock trades.
- Inventory Management: Buying the absolute last item in an Amazon warehouse.
- Booking Systems: Reserving seat
6Aon an airline flight. If two users are served stale data, both will successfully book the same seat, resulting in a system failure. - Banking: Transferring money between accounts. You cannot allow double-spending.
- Authentication: User login and session management. Security requires accurate state.
Architectural Implications:
- Heavy reliance on distributed transactions (Two-Phase Commit).
- Bottlenecking writes to a single primary node to ensure atomic operations.
- Technologies: PostgreSQL, MySQL, Google Spanner, or DynamoDB (specifically using Strongly Consistent reads).
Example Implementation:
@Service
public class InventoryService {
@Autowired
private InventoryRepository inventoryRepository;
@Transactional(isolation = Isolation.SERIALIZABLE)
public boolean purchaseItem(Long itemId, int quantity) {
// CP: Strong consistency required
InventoryItem item = inventoryRepository.findById(itemId)
.orElseThrow(() -> new ItemNotFoundException());
if (item.getAvailableQuantity() >= quantity) {
item.setAvailableQuantity(item.getAvailableQuantity() - quantity);
inventoryRepository.save(item);
return true;
}
return false; // Out of stock
}
}
When to Choose Availability (AP)
If stale data is mildly inconvenient but system downtime is unacceptable, you choose AP. The overwhelming majority of consumer web applications fall into this category.
Use Cases:
- Social Media: If User B doesn't see User A's latest tweet for 30 seconds, there is no real-world damage.
- Content Platforms: Updating a movie description on Netflix.
- Review Sites: Yelp displaying a menu that is technically 1 minute out of date while the network recovers.
- Analytics: Real-time dashboards where slight delays are acceptable.
- Recommendation Systems: Product recommendations where freshness is not critical.
Architectural Implications:
- Leveraging multiple read replicas.
- Embracing Change Data Capture (CDC) and asynchronous message queues (Kafka).
- Technologies: Apache Cassandra, standard DynamoDB (multi-AZ), Redis.
Example Implementation:
@Service
public class SocialMediaService {
@Autowired
private PostRepository postRepository;
@Autowired
private KafkaTemplate<String, Post> kafkaTemplate;
// AP: High availability, eventual consistency
public Post createPost(Post post) {
// Write to primary database
Post savedPost = postRepository.save(post);
// Asynchronously notify other regions
kafkaTemplate.send("post-created", savedPost);
return savedPost;
}
// Read from nearest replica (may be slightly stale)
public Post getPost(Long postId) {
return postRepository.findById(postId).orElse(null);
}
}
Decision Framework
Strict CP System
Prioritize Consistency (CP)Downtime is costly, but serving stale data causes irreversible financial or operational damage (e.g. double booking or overdrafts). The system must reject writes/reads during partitions.
3. Senior Nuance #1: Granularity of CAP
A common mistake made by junior engineers is applying a single CAP label to an entire massive system (e.g., "Ticketmaster is CP"). Modern systems are compositions of microservices, and CAP theorem trade-offs exist at the sub-system level.
To demonstrate seniority, break down the system:
- Ticketmaster: You prioritize Availability (AP) for the Search and Event Catalog services. Users must be able to browse events even if the description is slightly stale. However, you strictly prioritize Consistency (CP) for the actual checkout and seat reservation service to prevent double-booking.
- Tinder: You prioritize Availability (AP) for browsing and updating profiles (swiping). But the actual Matching engine requires Consistency (CP)โif two users swipe right, the match must be registered and notified atomically.
- E-commerce: Product catalog and search are AP (stale product info is acceptable), but inventory and checkout are CP (cannot oversell).
- Banking: Account balances are CP (must be accurate), but transaction history and analytics are AP (slight delays acceptable).
Microservice-Level CAP Example
Inventory Reserve
Subsystem trade-off: CPService Boundary Considerations
When to Split CAP Decisions:
- Different business requirements within the same system
- Different data access patterns (read-heavy vs. write-heavy)
- Different performance requirements (latency-sensitive vs. throughput-sensitive)
- Different failure tolerance levels
Implementation Strategies:
- Use different databases for different services
- Implement data synchronization between CP and AP services
- Use event-driven architecture to maintain consistency where needed
- Implement circuit breakers to handle failures gracefully
4. Senior Nuance #2: The Consistency Spectrum
When the CAP theorem mentions "Consistency," it explicitly refers to Strong (Linearizable) Consistency. However, giving up Strong Consistency does not mean your system has no consistency. If you choose an AP system, you can still engineer specific consistency guarantees:
Consistency Models
1. Strong Consistency (Linearizability)โ
Every read receives the most recent write or an error. This is the "C" in CAP.
Properties:
- All operations appear to execute atomically
- Total ordering of operations
- No stale reads possible
Use Cases:
- Financial transactions
- Inventory management
- Authentication and authorization
Example:
// Strong consistency with distributed lock
@Service
public class StrongConsistencyService {
@Autowired
private DistributedLockManager lockManager;
public void transferMoney(Long fromAccount, Long toAccount, BigDecimal amount) {
String lockKey = "account:" + fromAccount;
try (Lock lock = lockManager.acquireLock(lockKey, 10, TimeUnit.SECONDS)) {
// Critical section: only one thread can execute this
Account from = accountRepository.findById(fromAccount);
Account to = accountRepository.findById(toAccount);
from.setBalance(from.getBalance().subtract(amount));
to.setBalance(to.getBalance().add(amount));
accountRepository.save(from);
accountRepository.save(to);
}
}
}
2. Causal Consistencyโ
Ensures that related events appear in the correct order. A reply to a comment will never load before the parent comment loads, even if the entire comment thread is delayed.
Properties:
- Causally related operations are ordered
- Unrelated operations may be reordered
- No total ordering required
Use Cases:
- Social media feeds
- Chat applications
- Collaborative editing
Example:
@Service
public class CausalConsistencyService {
@Autowired
private EventRepository eventRepository;
public void addComment(Long postId, Comment comment) {
// Include causal context (parent comment ID)
comment.setCausalContext(getCausalContext(postId));
// Store with vector clock
eventRepository.save(comment);
}
public List<Comment> getComments(Long postId) {
// Filter comments based on causal consistency
return eventRepository.findByPostId(postId)
.stream()
.filter(this::isCausallyConsistent)
.collect(Collectors.toList());
}
}
3. Read-Your-Own-Writes Consistencyโ
A user updating their profile will always see their own updates immediately, even if they are routed to a stale replica. Other users in different regions might see the old profile, but the actor's experience is seamless.
Properties:
- User always sees their own writes
- Other users may see stale data
- Session-based consistency
Use Cases:
- User profiles
- Shopping carts
- Personal dashboards
Example:
@Service
public class ReadYourOwnWritesService {
@Autowired
private UserRepository userRepository;
@Autowired
private CacheManager cacheManager;
public User updateUserProfile(Long userId, UserProfile profile) {
// Update primary database
User user = userRepository.findById(userId);
user.setProfile(profile);
userRepository.save(user);
// Update user's personal cache immediately
Cache userCache = cacheManager.getCache("user-profiles");
userCache.put(userId, user);
return user;
}
public User getUserProfile(Long userId, String sessionId) {
// Check user's personal cache first
Cache userCache = cacheManager.getCache("user-profiles");
User cachedUser = userCache.get(userId);
if (cachedUser != null && isOwnSession(sessionId, userId)) {
return cachedUser; // Read-your-own-writes
}
// Otherwise, read from database (may be stale)
return userRepository.findById(userId);
}
}
4. Eventual Consistencyโ
The lowest baseline. Given enough time without new writes, all nodes will converge to the same data state.
Properties:
- No ordering guarantees
- Nodes may have different views temporarily
- Convergence over time
Use Cases:
- Social media feeds
- Analytics and reporting
- Content delivery networks
Example:
@Service
public class EventualConsistencyService {
@Autowired
private KafkaTemplate<String, Object> kafkaTemplate;
public void publishEvent(Object event) {
// Publish to Kafka for eventual consistency
kafkaTemplate.send("events", event);
}
@KafkaListener(topics = "events")
public void processEvent(Object event) {
// Process events asynchronously
// Different consumers may process at different times
handleEvent(event);
}
}
Consistency Model Comparison
| Consistency Model | Latency | Availability | Complexity | Use Case |
|---|---|---|---|---|
| Strong | High | Low | Low | Financial systems |
| Causal | Medium | Medium | Medium | Social media |
| Read-Your-Own-Writes | Low | High | Medium | User profiles |
| Eventual | Very Low | Very High | High | Analytics |
Choosing the Right Consistency Model
1. Strong Consistency Details
Ensures that every reader gets the absolute newest value instantly or returns an error. Solves double-spending but slows performance.
5. How CAP Works Internally
Network Partition Detection
Heartbeat Mechanismโ
Systems detect network partitions through heartbeat messages between nodes.
@Service
public class PartitionDetector {
private final Map<String, Long> lastHeartbeat = new ConcurrentHashMap<>();
private final long HEARTBEAT_TIMEOUT = 5000; // 5 seconds
@Scheduled(fixedRate = 1000)
public void checkPartitions() {
long now = System.currentTimeMillis();
lastHeartbeat.forEach((nodeId, lastSeen) -> {
if (now - lastSeen > HEARTBEAT_TIMEOUT) {
handlePartition(nodeId);
}
});
}
public void receiveHeartbeat(String nodeId) {
lastHeartbeat.put(nodeId, System.currentTimeMillis());
}
private void handlePartition(String nodeId) {
log.warn("Node {} is partitioned", nodeId);
// Take appropriate action based on CAP choice
}
}
Gossip Protocolโ
More sophisticated systems use gossip protocols for failure detection.
class GossipProtocol:
def __init__(self, node_id, peers):
self.node_id = node_id
self.peers = peers
self.heartbeat = time.time()
self.failure_detector = PhiAccrualFailureDetector()
def gossip(self):
# Select random peers to gossip with
sample = random.sample(self.peers, min(3, len(self.peers)))
for peer in sample:
try:
peer.receive_heartbeat(self.node_id, self.heartbeat)
peer.receive_member_list(self.get_member_list())
except Exception:
self.failure_detector.record_failure(peer)
def receive_heartbeat(self, node_id, timestamp):
self.failure_detector.record_heartbeat(node_id, timestamp)
def get_member_list(self):
# Return list of live nodes
return [p for p in self.peers if self.failure_detector.is_alive(p)]
CP System Implementation
Quorum-Based Operationsโ
CP systems use quorum-based operations to ensure consistency.
@Service
public class CPDataService {
private final List<DataNode> nodes;
private final int replicationFactor = 3;
public boolean write(String key, String value) {
int quorum = (replicationFactor / 2) + 1;
int successfulWrites = 0;
for (DataNode node : nodes) {
try {
node.write(key, value);
successfulWrites++;
if (successfulWrites >= quorum) {
return true;
}
} catch (Exception e) {
log.error("Write failed on node {}", node.getId(), e);
}
}
return false; // Quorum not reached
}
public String read(String key) {
int quorum = (replicationFactor / 2) + 1;
List<String> values = new ArrayList<>();
for (DataNode node : nodes) {
try {
String value = node.read(key);
values.add(value);
if (values.size() >= quorum) {
return resolveConflicts(values);
}
} catch (Exception e) {
log.error("Read failed on node {}", node.getId(), e);
}
}
return null; // Quorum not reached
}
private String resolveConflicts(List<String> values) {
// Resolve conflicts by choosing the most recent value
return values.stream()
.max(Comparator.comparing(this::extractTimestamp))
.orElse(null);
}
}
Two-Phase Commit (2PC)โ
For distributed transactions, CP systems use Two-Phase Commit.
@Service
public class TwoPhaseCommitCoordinator {
@Autowired
private List<TransactionParticipant> participants;
public boolean executeTransaction(Transaction transaction) {
// Phase 1: Prepare
boolean allPrepared = true;
for (TransactionParticipant participant : participants) {
try {
if (!participant.prepare(transaction)) {
allPrepared = false;
break;
}
} catch (Exception e) {
allPrepared = false;
break;
}
}
// Phase 2: Commit or Rollback
if (allPrepared) {
for (TransactionParticipant participant : participants) {
try {
participant.commit(transaction);
} catch (Exception e) {
log.error("Commit failed", e);
}
}
return true;
} else {
for (TransactionParticipant participant : participants) {
try {
participant.rollback(transaction);
} catch (Exception e) {
log.error("Rollback failed", e);
}
}
return false;
}
}
}
AP System Implementation
Eventual Consistency with Replicationโ
AP systems use asynchronous replication for high availability.
@Service
public class APDataService {
@Autowired
private PrimaryNode primaryNode;
@Autowired
private List<ReplicaNode> replicaNodes;
@Autowired
private KafkaTemplate<String, Object> kafkaTemplate;
public void write(String key, String value) {
// Write to primary immediately
primaryNode.write(key, value);
// Asynchronously replicate to other nodes
kafkaTemplate.send("replication", new ReplicationEvent(key, value));
}
public String read(String key) {
// Read from nearest replica (may be stale)
ReplicaNode nearestReplica = findNearestReplica();
return nearestReplica.read(key);
}
@KafkaListener(topics = "replication")
public void handleReplication(ReplicationEvent event) {
// Replicate to all replicas asynchronously
for (ReplicaNode replica : replicaNodes) {
try {
replica.write(event.getKey(), event.getValue());
} catch (Exception e) {
log.error("Replication failed", e);
}
}
}
}
Conflict Resolutionโ
AP systems need conflict resolution mechanisms.
@Service
public class ConflictResolver {
public String resolveConflict(List<String> values) {
// Last-Write-Wins (LWW) strategy
return values.stream()
.max(Comparator.comparing(this::extractTimestamp))
.orElse(null);
}
public String resolveConflictWithMerge(List<String> values) {
// Merge strategy (for complex data types)
return values.stream()
.reduce(this::merge)
.orElse(null);
}
private String merge(String value1, String value2) {
// Custom merge logic
// For example, merging JSON objects
return mergeJson(value1, value2);
}
}
6. Real-World Implementations
Google Spanner (CP)
Key Features:
- True SQL database with strong consistency
- Global distribution with external consistency
- Uses TrueTime API for synchronization
- Supports distributed transactions
Architecture:
TrueTime API (Atomic Clocks)
TrueTime API provides synchronized atomic clock times globally with a bounded uncertainty window [t.earliest, t.latest].
- Uses GPS receivers combined with independent Rubidium atomic clocks in each data center.
- Guarantees monotonically increasing transactional timestamps across zones.
- Eliminates lock contention for read-only transactions, matching CP invariants at low latency.
Configuration:
-- Create a database with strong consistency
CREATE DATABASE my_database
OPTIONS (
default_leader = 'us-central1'
);
-- Create a table with global consistency
CREATE TABLE users (
user_id INT64,
name STRING(100),
email STRING(100),
) PRIMARY KEY (user_id);
-- Strongly consistent read
SELECT * FROM users WHERE user_id = 123;
-- Stale read (for performance)
SELECT * FROM users
WHERE user_id = 123
OPTIONS (staleness = '10s');
Apache Cassandra (AP)
Key Features:
- Masterless architecture
- Tunable consistency levels
- Linear scalability
- High write throughput
Architecture:
Masterless Ring (DHT)
Cassandra uses a masterless structure where nodes are arranged in a ring using Consistent Hashing (Distributed Hash Table).
- Nodes are assigned specific token ranges (e.g. Node 1 gets 0-33, Node 2 gets 34-66).
- A partition key is hashed to determine which node owns the primary data copy.
- No single point of failure (SPOF): any alive node can coordinate a read or write.
Configuration:
-- Create a keyspace with replication strategy
CREATE KEYSPACE my_keyspace
WITH replication = {
'class': 'NetworkTopologyStrategy',
'datacenter1': 3,
'datacenter2': 2
};
-- Create a table
CREATE TABLE users (
user_id uuid PRIMARY KEY,
name text,
email text
);
-- Write with QUORUM consistency
INSERT INTO users (user_id, name, email)
VALUES (uuid(), 'John Doe', '[email protected]')
USING CONSISTENCY QUORUM;
-- Read with QUORUM consistency
SELECT * FROM users
WHERE user_id = ?
USING CONSISTENCY QUORUM;
-- Read with ONE consistency (may be stale)
SELECT * FROM users
WHERE user_id = ?
USING CONSISTENCY ONE;
Amazon DynamoDB (Hybrid)
Key Features:
- Managed NoSQL service
- Tunable consistency (strong vs. eventual)
- Automatic scaling
- Multi-region replication
Configuration:
// Strongly consistent read
const params = {
TableName: 'Users',
Key: {
userId: { S: '123' }
},
ConsistentRead: true // Strong consistency
};
// Eventually consistent read (default)
const params = {
TableName: 'Users',
Key: {
userId: { S: '123' }
},
ConsistentRead: false // Eventual consistency
};
// Conditional write for consistency
const params = {
TableName: 'Users',
Item: {
userId: { S: '123' },
name: { S: 'John Doe' },
version: { N: '2' }
},
ConditionExpression: 'version = :version',
ExpressionAttributeValues: {
':version': { N: '1' }
}
};
Redis (AP)
Key Features:
- In-memory data store
- Single-threaded (mostly)
- Pub/sub support
- Cluster mode for scaling
Configuration:
# Redis Cluster configuration
cluster-enabled yes
cluster-config-file nodes.conf
cluster-node-timeout 5000
# Replication
replica-read-only yes
replica-serve-stale-data yes # AP: serve stale data
Usage:
import redis
# Connect to Redis cluster
r = redis.RedisCluster(host='localhost', port=6379)
# Write (immediate)
r.set('user:123:name', 'John Doe')
# Read (may be stale if reading from replica)
name = r.get('user:123:name')
# Write with expiration
r.setex('user:123:session', 3600, 'session_token')
# Pub/Sub for eventual consistency
pubsub = r.pubsub()
pubsub.subscribe('user-updates')
for message in pubsub.listen():
if message['type'] == 'message':
handle_update(message['data'])
7. Integration Patterns
Pattern 1: Hybrid CP/AP Architecture
Use CP for critical operations and AP for non-critical ones.
@Service
public class HybridService {
@Autowired
private CPService cpService;
@Autowired
private APService apService;
// CP: Critical operation
@Transactional
public void processPayment(Payment payment) {
cpService.updateBalance(payment.getFromAccount(), -payment.getAmount());
cpService.updateBalance(payment.getToAccount(), payment.getAmount());
}
// AP: Non-critical operation
public void updateAnalytics(UserEvent event) {
apService.recordEvent(event);
}
}
Pattern 2: Saga Pattern for Distributed Transactions
Implement sagas for eventual consistency across services.
@Service
public class OrderSaga {
@Autowired
private InventoryService inventoryService;
@Autowired
private PaymentService paymentService;
@Autowired
private ShippingService shippingService;
@SagaOrchestrationStart
public void createOrder(Order order) {
// Step 1: Reserve inventory
inventoryService.reserve(order.getProductId(), order.getQuantity());
// Step 2: Process payment
paymentService.charge(order.getPaymentId(), order.getAmount());
// Step 3: Create shipping order
shippingService.createShipment(order);
order.setStatus(OrderStatus.COMPLETED);
}
@Compensation
public void compensateOrder(Order order) {
// Compensate: Release inventory
inventoryService.release(order.getProductId(), order.getQuantity());
// Compensate: Refund payment
paymentService.refund(order.getPaymentId(), order.getAmount());
// Compensate: Cancel shipment
shippingService.cancelShipment(order);
order.setStatus(OrderStatus.CANCELLED);
}
}
Pattern 3: CQRS with Event Sourcing
Separate read and write models with different consistency requirements.
@Service
public class CQRSService {
@Autowired
private EventStore eventStore;
@Autowired
private ReadModelRepository readModelRepository;
// Write side: CP
@Transactional
public void handleCommand(Command command) {
// Process command
Event event = processCommand(command);
// Store event (strongly consistent)
eventStore.save(event);
// Update read model (eventually consistent)
publishEvent(event);
}
// Read side: AP
public QueryResult handleQuery(Query query) {
// Read from read model (may be slightly stale)
return readModelRepository.query(query);
}
@KafkaListener(topics = "events")
public void updateReadModel(Event event) {
// Update read model asynchronously
readModelRepository.update(event);
}
}
Pattern 4: Multi-Region Active-Active
Deploy across multiple regions with conflict resolution.
@Service
public class MultiRegionService {
@Autowired
private RegionAwareRepository repository;
public void writeData(Data data) {
// Write to local region immediately
repository.writeToLocalRegion(data);
// Asynchronously replicate to other regions
replicateToOtherRegions(data);
}
public Data readData(String key) {
// Read from nearest region
return repository.readFromNearestRegion(key);
}
private void replicateToOtherRegions(Data data) {
// Use conflict-free replicated data types (CRDTs)
for (Region region : getAllRegions()) {
if (!region.isLocal()) {
repository.replicateToRegion(data, region);
}
}
}
}
Pattern 5: Circuit Breaker for CP Systems
Implement circuit breakers to handle failures gracefully.
@Service
public class CircuitBreakerService {
@Autowired
private CPService cpService;
@CircuitBreaker(name = "cpService", fallbackMethod = "fallback")
public Data getData(String key) {
return cpService.getData(key);
}
public Data fallback(String key, Exception e) {
// Fallback to stale data or error
log.warn("CP service unavailable, using fallback", e);
return getStaleData(key);
}
private Data getStaleData(String key) {
// Return cached stale data or default value
return cache.get(key);
}
}
8. Pros and Cons
CP Systems
Pros: โ Strong Consistency: All reads return the most recent write โ Data Integrity: No risk of conflicting updates โ Simplified Logic: Easier to reason about system behavior โ ACID Transactions: Support for complex transactions
Cons: โ Lower Availability: May be unavailable during network partitions โ Higher Latency: Requires coordination between nodes โ Limited Scalability: Coordination overhead limits horizontal scaling โ Single Point of Failure: Leader election can be a bottleneck
AP Systems
Pros: โ High Availability: Always responsive, even during failures โ Low Latency: Reads can be served locally โ Better Scalability: No coordination overhead for reads โ Fault Tolerance: Can tolerate multiple node failures
Cons: โ Eventual Consistency: May serve stale data โ Conflict Resolution: Need to handle conflicting updates โ Complex Logic: Harder to reason about system behavior โ Data Inconsistency: Temporary inconsistencies possible
When to Choose CP
Choose CP when:
- Financial transactions and banking
- Inventory management and e-commerce checkout
- Authentication and authorization
- Any system where data accuracy is critical
- Regulatory compliance requires strong consistency
When to Choose AP
Choose AP when:
- Social media and content platforms
- Analytics and reporting
- Recommendation systems
- Real-time dashboards
- Any system where availability is more important than immediate consistency
Interview Questions
Beginner Level
Q1: What is the CAP theorem? A: The CAP theorem states that a distributed system can only guarantee two out of three properties: Consistency, Availability, and Partition Tolerance. In practice, Partition Tolerance is mandatory in distributed systems, so the choice is between Consistency (CP) and Availability (AP).
Q2: Why can't you have CA in a distributed system? A: In distributed systems, network partitions are inevitable. When a partition occurs, the system must choose between returning an error (sacrificing availability) or returning potentially stale data (sacrificing consistency). Therefore, CA is not possible in distributed systems.
Q3: What is the difference between CP and AP systems? A: CP systems prioritize consistency over availability. During a network partition, they return errors rather than serve stale data. AP systems prioritize availability over consistency. During a network partition, they continue serving requests, potentially with stale data.
Q4: Give an example of a CP system. A: Traditional relational databases like PostgreSQL and MySQL are CP systems. They use strong consistency and may become unavailable during network partitions to maintain data integrity.
Q5: Give an example of an AP system. A: NoSQL databases like Cassandra and DynamoDB (in eventual consistency mode) are AP systems. They prioritize availability and may serve stale data during network partitions.
Intermediate Level
Q6: How do you detect network partitions in a distributed system? A: Network partitions are detected using heartbeat mechanisms or gossip protocols. Nodes send periodic heartbeat messages to each other. If a node doesn't receive heartbeats from another node within a timeout period, it considers that node partitioned.
Q7: What is quorum-based consistency? A: Quorum-based consistency requires a majority of nodes to acknowledge a write or read operation. For a replication factor of 3, a quorum is 2 nodes. This ensures that at least one node has the latest data, providing consistency while maintaining some availability.
Q8: How does eventual consistency work? A: Eventual consistency means that if no new updates are made to a given data item, all accesses to that item will eventually return the last updated value. Updates propagate asynchronously to all nodes, and given enough time, all nodes will converge to the same state.
Q9: What is the difference between strong consistency and eventual consistency? A: Strong consistency ensures that all reads return the most recent write. Eventual consistency only guarantees that all nodes will eventually converge to the same state, but reads may return stale data in the meantime.
Q10: How do you handle conflicts in AP systems? A: Conflicts in AP systems are handled using conflict resolution strategies like Last-Write-Wins (LWW), merge operations, or application-specific resolution logic. Some systems use conflict-free replicated data types (CRDTs) that automatically resolve conflicts.
Advanced Level
Q11: Explain the consistency spectrum. A: The consistency spectrum ranges from strong consistency (linearizability) to eventual consistency. In between are models like causal consistency (related events are ordered), read-your-own-writes consistency (users see their own writes), and session consistency (consistency within a session).
Q12: How do you implement strong consistency in a distributed system? A: Strong consistency is implemented using techniques like distributed locks, quorum-based operations, two-phase commit (2PC), or Paxos/Raft consensus algorithms. These ensure that all nodes agree on the order and outcome of operations.
Q13: What is the difference between linearizability and serializability? A: Linearizability is a consistency model that ensures operations appear to execute atomically at some point in time. Serializability is a transaction isolation level that ensures transactions appear to execute sequentially. Linearizability is about single-object operations, while serializability is about multi-object transactions.
Q14: How do you design a system that uses both CP and AP? A: Design a hybrid system where critical operations use CP (e.g., financial transactions) and non-critical operations use AP (e.g., analytics). Use different databases or consistency levels for different parts of the system. Implement data synchronization between CP and AP components.
Q15: What is the impact of CAP on microservices architecture? A: In microservices architecture, CAP trade-offs exist at the service level. Different services can choose different consistency models based on their requirements. This allows for a flexible architecture where critical services use CP and non-critical services use AP.
Senior Level
Q16: Design a distributed system that handles both strong and eventual consistency. A: Implement a CQRS (Command Query Responsibility Segregation) architecture where the write side uses strong consistency and the read side uses eventual consistency. Use event sourcing to capture all changes and propagate them to read models asynchronously. Implement conflict resolution for concurrent updates.
Q17: How do you handle network partitions in a multi-region deployment? A: Implement region-aware routing where clients are directed to the nearest region. Use conflict-free replicated data types (CRDTs) for data that can be merged automatically. Implement conflict resolution for other data types. Use quorum-based operations for critical data. Monitor partition detection and recovery.
Q18: How do you optimize for both consistency and availability? A: Use techniques like read repair, hinted handoff, and anti-entropy to improve consistency while maintaining availability. Implement adaptive consistency levels based on operation criticality. Use caching and read replicas to improve performance while maintaining consistency where needed.
Q19: How do you debug consistency issues in distributed systems? A: Implement distributed tracing to follow requests across services. Use vector clocks or Lamport timestamps to track event ordering. Implement comprehensive logging and monitoring. Use chaos engineering to test failure scenarios. Implement consistency checks and validation.
Q20: How do you choose between CP and AP for a given use case? A: Evaluate the business impact of stale data vs. downtime. Consider regulatory requirements and compliance. Analyze data access patterns and performance requirements. Consider the cost of implementation and operational complexity. Prototype and test different approaches before committing.
10. PACELC โ What Happens When the Network is Fine
CAP is silent on this. PACELC answers it: during normal operation (E), systems still must choose between lower latency (accept writes without waiting for all replicas) vs stronger consistency (wait for replica acknowledgement before confirming success).
This is not a configuration choice โ it is physics. Replicas in the same AZ: ~0.5ms. Replicas across regions: SingaporeโTokyo ~70ms, Vietnamโus-east-1 ~200ms+. Each strongly-consistent write adds at least one round-trip to that replica.
Amazon internal study (2006): +100ms latency = โ1% revenue.
CAP only models trade-offs during network partitions. But partitions are rare โ most systems experience seconds to minutes of partition per year. During the other 99.99% of the time, the system is still making trade-offs โ and CAP says nothing about them.
Daniel Abadi proposed PACELC in 2010 to fill exactly this gap:
If there is a Partition (P), the system trades off between Availability (A) and Consistency (C) โ same as CAP.
Else (E) (when the network is healthy), the system trades off between Latency (L) and Consistency (C).
The key insight of the EโLC side: consistency requires coordination, coordination requires round-trips, and round-trips add latency. Every strongly consistent write must wait for replica acknowledgement before returning success. That wait is measurable, unavoidable, and cumulative across millions of requests.
The PACELC Formula
CAP is silent on this. PACELC answers it: during normal operation (E), systems still must choose between lower latency (accept writes without waiting for all replicas) vs stronger consistency (wait for replica acknowledgement before confirming success).
This is not a configuration choice โ it is physics. Replicas in the same AZ: ~0.5ms. Replicas across regions: SingaporeโTokyo ~70ms, Vietnamโus-east-1 ~200ms+. Each strongly-consistent write adds at least one round-trip to that replica.
Amazon internal study (2006): +100ms latency = โ1% revenue.
The three Cs across ACID, CAP, and PACELC are not the same letter:
| Context | What "C" Means |
|---|---|
| C in ACID | Database moves to a valid state โ no business rule is violated (e.g. balance โฅ 0). Correctness relative to your constraints. |
| C in CAP | Linearizability โ every read returns the most recent successful write, everywhere, immediately. |
| C in PACELC (ELC) | Same as CAP's C. During normal operation, how much synchronous consistency does the system maintain โ and how much latency does that cost? |
Latency vs Consistency (the EL trade-off)
Option L (Low latency): Manager sells based on local ledger without calling the other branch. Fast for the customer. Risk: both branches sell the last TV.
Option C (Consistency): Manager calls the other branch to confirm before completing the sale. Correct. The customer waits while the call goes through. If the other branch doesn't answer in 3 rings โ what then?
| Operation | EL Choice | Latency Cost | Business Reason |
|---|---|---|---|
| Inventory deduct (flash sale) | EC (Consistency) | Pay round-trip | Oversell costs more than the latency: customer dissatisfaction + refund ops |
| Like / share counter | EL (Latency) | None | 3 stale likes on a post harms nobody. Blocking 10M concurrent likes would. |
| User profile read | EL (Latency) | None | Session guarantee (Read-Your-Writes) covers the user's own view. No global consistency needed. |
| Bank balance before debit | EC (Consistency) | Pay round-trip | Reading stale balance before deduction risks overdraft. |
| Product catalog search | EL (Latency) | None | Showing 5-second-old prices is fine. Sub-millisecond search matters more. |
| Feature flag read | EL (Latency) + bounded staleness | None (cache TTL) | 1-5 min staleness acceptable. Consistency within a request session is enough. |
The latency cost of strong consistency is not abstract โ it is geographic:
- Replicas in the same AZ: < 1ms round-trip โ negligible
- Cross-region (Singapore โ Tokyo): ~70ms per write
- Vietnam โ us-east-1: 200ms+ per write
Amazon's internal study (2006): +100ms latency โ โ1% revenue. DynamoDB makes this literal in its pricing: a strongly consistent read costs 2ร a eventually consistent read. Consistency is not only an architectural concept โ it is a line item on your cloud bill.
Database Classification Matrix
Key patterns in the matrix:
- PA/EL systems (Cassandra, Riak): Prioritize availability and low latency in both scenarios. Accept eventual consistency as the price.
- PC/EC systems (Spanner, CockroachDB, ZooKeeper): Consistency in both scenarios. Pay latency and accept reduced availability during partition.
- Per-request systems (DynamoDB, MongoDB): The label changes depending on how you call the API โ the same database can be PA/EL or PC/EC.
Abadi's core insight: The PACELC label is often not a fixed property of a database โ it is a property of an individual operation. Engineers who choose a database's consistency profile are actually choosing a default that can be overridden per-request. Applying a single system-wide label is as imprecise as applying a single CAP label to an entire microservice architecture.
The GitHub Incident Revisited
CAP choice made: Consistency (CP) โ halted writes, reconciled manually.
Cost: 24 hours 11 minutes of service degradation.
Ratio: 43 seconds of partition โ 2051ร that in recovery time.
The only choice is what to do when it happens. GitHub chose Consistency.
(1) Node is dead permanently โ promote replica (correct).
(2) Node is slow (GC pause, CPU spike) โ wait (correct).
(3) Network blinked momentarily โ do nothing (correct).
The orchestrator can only see timeout. It cannot see which case it is in. Set the threshold too low โ false failovers (what happened here). Set it too high โ slow recovery when nodes actually die.
1. "Choose 2 of 3" is a misleading framing. The real question is: "What does the system do during the window of a partition?" The right question to ask is not which two you pick, but: how do you detect a partition, manage data during it, and recover after it ends?
2. The recovery phase is not free. GitHub's incident demonstrates this precisely: 43 seconds of partition โ 24 hours of reconciliation. CAP says nothing about what that recovery costs. PACELC says nothing either. The cost of the reconciliation phase is the real bill for choosing AP.
โ Airlines overbook flights, then resolve with vouchers and upgrades.
โ Hotels overbook rooms, then move guests to nearby hotels at cost.
โ Banks process transactions as "pending" for 1โ3 business days.
โ E-commerce flash sales oversell, cancel orders, and apologize by email.
None of them stop the entire business to ensure every ledger entry is consistent at every moment. They choose availability, and they have a compensation process for when things diverge. The engineering terms โ saga, compensating transaction, reconciliation, read-repair โ are the software equivalents of vouchers, apology emails, and manual ledger corrections that human organizations have run for centuries.
The GitHub 2018 incident illustrates three separate CAP/PACELC lessons in one event:
- Partitions are not chosen โ they are caused by sharks, technicians, GC pauses, and switches.
- Timeout cannot distinguish partition from slowness from death โ the detection problem is unsolvable with a single threshold.
- Recovery is the real cost โ 43 seconds of partition, 24+ hours of reconciliation. CAP and PACELC describe the partition window; they are silent on recovery cost.
The AP Invoice โ Conflict Resolution
(1) How long the partition lasted (more writes = more divergence).
(2) How many objects diverged (write hotspots magnify the debt).
(3) Which conflict resolution strategy you chose (LWW = silent data loss; CRDT = auto-resolved; Saga = compensating ops).
GitHub's 43-second partition created 24 hours of reconciliation debt across 6 MySQL clusters. The partition was cheap. The bill arrived later.
Choosing AP (availability during partition) defers โ but does not cancel โ the cost. When the network reconnects and two diverged nodes meet, one of these strategies must resolve the conflict:
| Strategy | Auto-resolves? | Data Loss Risk | Complexity |
|---|---|---|---|
| Last-Write-Wins (LWW) | โ Yes | โ ๏ธ Silent data loss on clock skew | Low |
| Vector Clocks | โ Detects only | โ No loss โ surfaces conflict | Medium |
| CRDTs | โ Yes (specific shapes) | โ No loss | High (limited applicability) |
| Saga / Compensating Txn | โ Yes (business logic) | Domain-specific | High (irreversibility risk) |
The key design question before choosing AP: What is the reconciliation process? If the answer is "LWW and hope clocks are close", you have accepted silent data loss as a feature. If the answer is "manual reconciliation by engineers", you have accepted a GitHub-scale incident as a plausible event. Neither is wrong โ they must be explicit, intentional choices.
11. Senior Deep Dive: Advanced Topics
Topic 1: Consensus Algorithms
Paxosโ
Paxos is a family of protocols for solving consensus in a network of unreliable processors.
Basic Paxos Algorithm:
class PaxosNode:
def __init__(self, node_id):
self.node_id = node_id
self.proposed_value = None
self.accepted_value = None
self.promised_id = 0
self.accepted_id = 0
def prepare(self, proposal_id):
# Phase 1: Prepare
if proposal_id > self.promised_id:
self.promised_id = proposal_id
return (True, self.accepted_id, self.accepted_value)
return (False, None, None)
def accept(self, proposal_id, value):
# Phase 2: Accept
if proposal_id >= self.promised_id:
self.promised_id = proposal_id
self.accepted_id = proposal_id
self.accepted_value = value
return True
return False
Raftโ
Raft is a consensus algorithm designed to be understandable.
Raft States:
Follower State
Default starting state. Passive; only responds to incoming RPC requests (heartbeats/prepares).
- Does not issue any commands.
- Redirects client writes/reads to the Leader.
- Votes for candidates in incoming RequestVote requests.
- Election Timeout: If no heartbeat is received within a randomized window (e.g. 150-300ms), transitions to Candidate.
Raft Implementation:
@Service
public class RaftNode {
private RaftState state = RaftState.FOLLOWER;
private int currentTerm = 0;
private String votedFor = null;
private List<LogEntry> log = new ArrayList<>();
private int commitIndex = 0;
private int lastApplied = 0;
public void onElectionTimeout() {
if (state == RaftState.FOLLOWER || state == RaftState.CANDIDATE) {
becomeCandidate();
}
}
private void becomeCandidate() {
state = RaftState.CANDIDATE;
currentTerm++;
votedFor = nodeId;
requestVotes();
}
private void requestVotes() {
for (String peer : peers) {
sendRequestVote(peer, currentTerm, log.size() - 1,
log.get(log.size() - 1).term);
}
}
public void onReceiveRequestVote(String candidateId, int term,
int lastLogIndex, int lastLogTerm) {
if (term > currentTerm) {
currentTerm = term;
state = RaftState.FOLLOWER;
votedFor = null;
}
boolean voteGranted = false;
if (term == currentTerm &&
(votedFor == null || votedFor.equals(candidateId)) &&
isLogUpToDate(lastLogIndex, lastLogTerm)) {
votedFor = candidateId;
voteGranted = true;
}
sendRequestVoteResponse(candidateId, currentTerm, voteGranted);
}
}
Topic 2: Distributed Transactions
Two-Phase Commit (2PC)โ
@Service
public class TwoPhaseCommitCoordinator {
@Autowired
private List<TransactionParticipant> participants;
public boolean executeTransaction(Transaction transaction) {
// Phase 1: Prepare
boolean allPrepared = true;
for (TransactionParticipant participant : participants) {
try {
PrepareResponse response = participant.prepare(transaction);
if (!response.isPrepared()) {
allPrepared = false;
break;
}
} catch (Exception e) {
allPrepared = false;
break;
}
}
// Phase 2: Commit or Rollback
if (allPrepared) {
for (TransactionParticipant participant : participants) {
try {
participant.commit(transaction);
} catch (Exception e) {
log.error("Commit failed", e);
}
}
return true;
} else {
for (TransactionParticipant participant : participants) {
try {
participant.rollback(transaction);
} catch (Exception e) {
log.error("Rollback failed", e);
}
}
return false;
}
}
}
Three-Phase Commit (3PC)โ
@Service
public class ThreePhaseCommitCoordinator {
@Autowired
private List<TransactionParticipant> participants;
public boolean executeTransaction(Transaction transaction) {
// Phase 1: CanCommit
boolean allCanCommit = true;
for (TransactionParticipant participant : participants) {
try {
CanCommitResponse response = participant.canCommit(transaction);
if (!response.canCommit()) {
allCanCommit = false;
break;
}
} catch (Exception e) {
allCanCommit = false;
break;
}
}
if (!allCanCommit) {
for (TransactionParticipant participant : participants) {
try {
participant.abort(transaction);
} catch (Exception e) {
log.error("Abort failed", e);
}
}
return false;
}
// Phase 2: PreCommit
boolean allPreCommitted = true;
for (TransactionParticipant participant : participants) {
try {
PreCommitResponse response = participant.preCommit(transaction);
if (!response.preCommitted()) {
allPreCommitted = false;
break;
}
} catch (Exception e) {
allPreCommitted = false;
break;
}
}
if (!allPreCommitted) {
for (TransactionParticipant participant : participants) {
try {
participant.abort(transaction);
} catch (Exception e) {
log.error("Abort failed", e);
}
}
return false;
}
// Phase 3: DoCommit
for (TransactionParticipant participant : participants) {
try {
participant.doCommit(transaction);
} catch (Exception e) {
log.error("Commit failed", e);
}
}
return true;
}
}
Topic 3: Conflict-Free Replicated Data Types (CRDTs)
G-Counter (Grow-only Counter)โ
class GCounter:
def __init__(self):
self.counts = {} # node_id -> count
def increment(self, node_id, delta=1):
if node_id not in self.counts:
self.counts[node_id] = 0
self.counts[node_id] += delta
def value(self):
return sum(self.counts.values())
def merge(self, other):
for node_id, count in other.counts.items():
if node_id not in self.counts:
self.counts[node_id] = count
else:
self.counts[node_id] = max(self.counts[node_id], count)
PN-Counter (Positive-Negative Counter)โ
class PNCounter:
def __init__(self):
self.p = GCounter() # increments
self.n = GCounter() # decrements
def increment(self, node_id, delta=1):
self.p.increment(node_id, delta)
def decrement(self, node_id, delta=1):
self.n.increment(node_id, delta)
def value(self):
return self.p.value() - self.n.value()
def merge(self, other):
self.p.merge(other.p)
self.n.merge(other.n)
LWW-Register (Last-Write-Wins Register)โ
class LWWRegister:
def __init__(self):
self.value = None
self.timestamp = 0
def assign(self, value, timestamp):
if timestamp > self.timestamp:
self.value = value
self.timestamp = timestamp
def value(self):
return self.value
def merge(self, other):
if other.timestamp > self.timestamp:
self.value = other.value
self.timestamp = other.timestamp
Topic 4: Vector Clocks
Vector Clock Implementationโ
public class VectorClock {
private final Map<String, Integer> clock = new ConcurrentHashMap<>();
public void increment(String nodeId) {
clock.merge(nodeId, 1, Integer::sum);
}
public void merge(VectorClock other) {
other.clock.forEach((nodeId, timestamp) ->
clock.merge(nodeId, timestamp, Math::max));
}
public boolean happenedBefore(VectorClock other) {
boolean happenedBefore = false;
for (Map.Entry<String, Integer> entry : clock.entrySet()) {
String nodeId = entry.getKey();
int timestamp = entry.getValue();
int otherTimestamp = other.clock.getOrDefault(nodeId, 0);
if (timestamp > otherTimestamp) {
return false;
} else if (timestamp < otherTimestamp) {
happenedBefore = true;
}
}
return happenedBefore;
}
public boolean isConcurrent(VectorClock other) {
return !happenedBefore(other) && !other.happenedBefore(this);
}
}
Topic 5: Distributed Locking
Redlock Algorithmโ
@Service
public class RedLock {
@Autowired
private List<RedisTemplate<String, String>> redisTemplates;
private final long lockValidityTime = 30000; // 30 seconds
public boolean acquireLock(String lockKey, String requestId) {
int acquiredCount = 0;
for (RedisTemplate<String, String> template : redisTemplates) {
Boolean acquired = template.opsForValue()
.setIfAbsent(lockKey, requestId, lockValidityTime, TimeUnit.MILLISECONDS);
if (Boolean.TRUE.equals(acquired)) {
acquiredCount++;
}
}
// Acquire lock on majority of nodes
return acquiredCount > redisTemplates.size() / 2;
}
public void releaseLock(String lockKey, String requestId) {
for (RedisTemplate<String, String> template : redisTemplates) {
String currentValue = template.opsForValue().get(lockKey);
if (requestId.equals(currentValue)) {
template.delete(lockKey);
}
}
}
}
Additional Resources
Academic Papers
- "Brewer's Conjecture and the Feasibility of Consistent, Available, Partition-Tolerant Web Services" by Seth Gilbert and Nancy Lynch
- "In Search of an Understandable Consensus Algorithm" by Diego Ongaro and John Ousterhout (Raft)
- "Dynamo: Amazon's Highly Available Key-value Store" by DeCandia et al.
Books
- "Designing Data-Intensive Applications" by Martin Kleppmann
- "Distributed Systems: Principles and Paradigms" by Tanenbaum and Van Steen
- "Release It!" by Michael Nygard
Online Resources
- Jepsen Analysis - Analysis of distributed systems
- The Morning Paper - Summaries of CS papers
- System Design Primer
Best Practices
- Understand Your Requirements: Clearly define consistency and availability requirements before choosing a database
- Test Failure Scenarios: Use chaos engineering to test system behavior under network partitions
- Monitor Everything: Track consistency metrics, latency, and availability
- Implement Fallbacks: Have fallback strategies for when consistency cannot be guaranteed
- Document Trade-offs: Maintain architecture decision records (ADRs) for CAP choices
- Use Appropriate Tools: Choose databases and technologies that match your CAP requirements
- Plan for Recovery: Have procedures for recovering from network partitions
- Implement Idempotency: Design operations to be idempotent to handle retries
- Use Circuit Breakers: Implement circuit breakers to handle failures gracefully
- Educate Your Team: Ensure all team members understand CAP implications
Interactive Exploration: The Network Partition
To truly understand how nodes behave under duress, use the simulator below. By intentionally severing the network connection between a US node and a European node, you can observe how an AP system reacts versus a CP system when an isolated user attempts to read data.
{"component":"LlmGeneratedComponent","props":{"height":"700px","prompt":"Create an interactive CAP Theorem simulator using D3.js. Objective: Visualize how a distributed system behaves during a network partition based on CP (Consistency) vs AP (Availability) configurations. Data State: Two database nodes (Node A 'USA', Node B 'Europe') storing a value (e.g., 'v1'). Strategy: Standard Layout. Inputs: A toggle for System Mode ('CP - Strong Consistency' vs 'AP - High Availability'), a button 'Toggle Network Partition', a button 'Write new data to Node A', a button 'Read from Node B'. Behavior: Draw the two nodes connected by a line (the network). Show the current data value inside each node. When 'Write new data to Node A' is clicked, update Node A's value. If the network is connected, immediately animate the value replicating to Node B. If the network is partitioned (line is broken/red), the replication fails. When 'Read from Node B' is clicked: if network is connected, display 'Read Success: [Fresh Value]'. If partitioned AND mode is AP, display 'Read Success: [Stale Value]'. If partitioned AND mode is CP, display 'Read Failed: Node isolated (Consistency enforced)'. Visually indicate the state of the network link and the read results clearly.","id":"im_f716ff9f3d5f6682"}}
