Loading learning content...
In the year 2000, computer scientist Eric Brewer stood before an audience at the ACM Symposium on Principles of Distributed Computing and presented a conjecture that would fundamentally reshape how we think about distributed databases. His claim was deceptively simple yet profound:
"It is impossible for a distributed data store to simultaneously provide more than two out of the following three guarantees: Consistency, Availability, and Partition tolerance."
Two years later, Seth Gilbert and Nancy Lynch published a formal proof, elevating Brewer's conjecture to Brewer's theorem, commonly known as the CAP theorem.
This theorem didn't introduce new engineering constraints—it formalized an inherent limitation of physics. When network partitions occur (and they will), every distributed system must choose: reject some requests to maintain consistency, or serve requests with potentially stale data to maintain availability.
The CAP theorem explains why NoSQL databases make the trade-offs they do.
By the end of this page, you will understand the CAP theorem deeply—not just the theorem statement, but its implications, limitations, and practical applications. You'll understand ACID vs. BASE semantics and be able to evaluate database systems through the lens of consistency-availability trade-offs.
Before analyzing the theorem, we must precisely define its three properties. Misunderstandings of these definitions lead to confusion about CAP's implications.
In CAP, "consistency" means linearizability—the strongest form of consistency guarantee. Every read receives the most recent write or an error. All nodes see the same data at the same time.
This is different from the "C" in ACID, which refers to maintaining database invariants (constraints, foreign keys). CAP consistency is about data freshness across distributed nodes.
Formally: After a write completes successfully, all subsequent reads (from any node) must return that write's value or a newer value.
Availability means every request to a non-failing node receives a response—without guarantee that it contains the most recent write.
Importantly:
Formally: Every request received by a non-failing node must result in a response.
Partition tolerance means the system continues to operate despite network partitions—when communication between some nodes is lost.
Network partitions are:
Formally: The system continues to operate despite arbitrary partitioning due to network failures.
| Property | Guarantee | Measured By | Cost of Not Having |
|---|---|---|---|
| Consistency | All nodes see same data simultaneously | Every read returns latest write | Stale reads, conflicting views |
| Availability | Every request gets a response | Successful responses (not errors) | Rejected requests, downtime |
| Partition Tolerance | System works despite network splits | Continued operation during partitions | Cannot build distributed systems |
Network partitions happen. Hardware fails. Cables get cut. Cloud availability zones become unreachable. Choosing 'CA' (Consistency + Availability without Partition Tolerance) means building a single-node system—which isn't distributed and can't scale horizontally. For distributed systems, the real choice is between CP and AP.
Now let's understand why CAP forces a choice. The proof is intuitive once you visualize what happens during a network partition.
Imagine a distributed database with two nodes, N1 and N2, each holding a copy of value V.
If we prioritize Consistency (CP):
If we prioritize Availability (AP):
There is no third option. During a partition, a node that doesn't know the latest state must either admit it (breaking availability) or guess (breaking consistency).
This isn't a limitation of current technology that might be overcome—it's mathematically proven impossible. Information cannot travel faster than light, network delays are unbounded, and a node cannot know what it doesn't know.
The CAP theorem is a fundamental constraint, like conservation of energy. Systems claiming to violate it are either:
Distributed databases can be broadly classified based on their CAP preference during partitions.
CP systems sacrifice availability during partitions to maintain consistency. When a partition occurs, affected nodes stop accepting writes (and sometimes reads) until the partition heals.
Examples of CP systems:
Use cases for CP:
AP systems sacrifice consistency during partitions to remain available. All nodes continue accepting reads and writes, with conflicts resolved later.
Examples of AP systems:
Use cases for AP:
Real databases aren't purely CP or AP—they offer tunable consistency. Cassandra can behave as CP with quorum reads/writes. MongoDB can behave as AP with eventual consistency reads. The CAP classification describes default behavior and design philosophy, not immutable properties.
The original CAP theorem presentation led to oversimplified thinking. Modern understanding recognizes important nuances.
CAP only constrains behavior during partitions. When the network is healthy, well-designed systems can provide high consistency AND high availability. The trade-off only manifests when partitions occur.
Partitions occur but aren't constant:
If partitions are rare and brief, CP systems experience minimal availability impact. The choice matters most in environments where partitions are frequent or long-lasting.
Very high latency is indistinguishable from a partition. If a response takes 30 seconds, is that availability (you get a response) or unavailability (unusable latency)?
Practical systems must make decisions under latency uncertainty:
Daniel Abadi proposed PACELC to capture what happens when there's no partition:
If there is a Partition, choose Availability or Consistency; Else, when running normally, choose Latency or Consistency.
This recognizes that even without partitions, there's a trade-off between low latency (asynchronous replication) and consistency (synchronous replication).
PACELC Classifications:
PACELC provides a more complete picture of database behavior across all operating conditions.
| System | During Partition | Else (Normal) | PACELC |
|---|---|---|---|
| DynamoDB (default) | Available | Low Latency | PA/EL |
| Cassandra (default) | Available | Low Latency | PA/EL |
| MongoDB (majority) | Consistent | Low Latency | PC/EL |
| CockroachDB | Consistent | Consistent | PC/EC |
| Google Spanner | Consistent | Consistent | PC/EC |
| Riak | Available | Low Latency | PA/EL |
To understand BASE, we must first review ACID—the consistency model that relational databases provide and that NoSQL databases often relax.
1234567891011121314151617
-- ACID bank transfer exampleBEGIN TRANSACTION; -- Atomicity: Both updates happen or neither happens UPDATE accounts SET balance = balance - 100 WHERE account_id = 'A'; UPDATE accounts SET balance = balance + 100 WHERE account_id = 'B'; -- Consistency: Constraint check happens automatically -- (e.g., balance >= 0 constraint) -- Isolation: Other transactions see either: -- - Both accounts unchanged (before commit) -- - Both accounts updated (after commit) -- - Never one updated and one not COMMIT;-- Durability: After COMMIT returns, data survives power lossImplementing ACID across distributed nodes is challenging:
Atomicity across nodes: Requires two-phase commit (2PC) or similar protocols Consistency distributed: All nodes must agree on constraints Isolation across nodes: Distributed locks or snapshot isolation Durability across nodes: Data must be persisted on multiple nodes
Each of these adds latency and coordination overhead. The more nodes involved, the more expensive ACID becomes.
This is why traditional RDBMS struggles to scale horizontally while maintaining ACID guarantees—and why NoSQL databases often adopt weaker guarantees like BASE.
BASE is a backronym (intentionally contrasting with ACID) describing the consistency model embraced by many NoSQL databases.
Basically Available, Soft state, Eventually consistent
Let's unpack each component:
Basically Available: The system guarantees availability—it will respond to every request, even if the response is stale or an indication that the operation is pending. The system doesn't fail; it degrades gracefully.
Soft State: The system's state may change over time, even without new input, as updates propagate through the system. There's no guarantee that all nodes have the same view at any moment.
Eventually Consistent: If no new updates are made, eventually all replicas will converge to the same state. Given enough time, all nodes will be consistent—but "eventually" might be milliseconds or hours.
| Property | ACID | BASE |
|---|---|---|
| Philosophy | Pessimistic (assume conflicts) | Optimistic (assume success) |
| Consistency | Strong (immediate) | Eventual (deferred) |
| Availability | May sacrifice for consistency | Prioritized over consistency |
| Isolation | Full transaction isolation | Limited or application-managed |
| Failure handling | Rollback entire transaction | Compensating actions, conflict resolution |
| Scalability | Limited by coordination | Scales horizontally |
| Complexity | Database handles complexity | Application handles complexity |
Eventual consistency sounds vague, but it can be bounded:
Consistency window: The time between a write and all replicas being updated
Read-your-writes consistency: A weaker guarantee where a client always sees their own writes, even if other clients might not yet.
Monotonic reads: Once a client has seen a value, they won't see older values (no time travel).
Causal consistency: Effects of causally related operations are seen in order. If A causes B, everyone who sees B also sees A.
In practice, 'eventual' often means 'within milliseconds.' Modern NoSQL databases propagate updates quickly through efficient replication. Eventual consistency doesn't mean 'inconsistent'—it means 'consistent after a brief propagation delay.' For many applications, this delay is imperceptible.
Modern databases recognize that consistency requirements vary by operation. Rather than forcing a system-wide choice, they offer tunable consistency levels.
Cassandra allows specifying consistency per operation:
Write consistency levels:
ANY: Write to at least one node (may be a hint)ONE: Write to at least one replicaQUORUM: Write to majority of replicas (⌊N/2⌋ + 1)ALL: Write to all replicasRead consistency levels:
ONE: Read from one replica (fast but possibly stale)QUORUM: Read from majority, return most recent valueALL: Read from all replicas (slowest, most consistent)The magic number: If W + R > N (write replicas + read replicas > total replicas), you achieve strong consistency because at least one replica has the latest data.
1234567891011121314151617181920
-- Configure consistency per query in Cassandra -- High availability, possible stale readsCONSISTENCY ONE;SELECT * FROM user_sessions WHERE user_id = '12345'; -- Strong consistency for important operationsCONSISTENCY QUORUM;UPDATE account_balance SET balance = 100.00 WHERE account_id = 'A'; -- Financial transaction requiring all nodesCONSISTENCY ALL;INSERT INTO transaction_log (id, amount, timestamp) VALUES (uuid(), 1000.00, now()); -- Consistency arithmetic:-- With replication factor N=3:-- QUORUM = 2 nodes-- W(QUORUM) + R(QUORUM) = 2 + 2 = 4 > 3 = N-- ∴ Strong consistency guaranteedMongoDB offers similar flexibility:
Write concern: How many replicas must acknowledge a write
w: 0: Fire and forget (no acknowledgment)w: 1: Primary onlyw: majority: Majority of replica setw: <number>: Specific countRead concern: What data can be read
local: Return whatever is in memory (might be uncommitted)available: Like local, but in sharded clustersmajority: Return only data written to majority of nodeslinearizable: Strongest—reflects all successful writesRead preference: Which nodes to read from
primary: Only the primary (freshest data)primaryPreferred: Primary if available, else secondarysecondary: Read from secondaries (reduces primary load)secondaryPreferred: Secondaries if available, else primarynearest: Lowest network latencyThe power of tunable consistency is matching guarantees to requirements. Payment processing might use strong consistency (QUORUM for both read and write), while social media feed fetching uses eventual consistency (ONE for reads). One database can serve both patterns.
In AP systems with eventual consistency, conflicting updates can occur during partitions. Different nodes may accept different writes for the same data. When the partition heals, these conflicts must be resolved.
12345678910111213141516171819202122232425262728293031323334353637383940414243
// Example: Shopping Cart conflict resolution // Last Write Wins - Simple but loses datainterface LWWCart { items: string[]; timestamp: number;} function resolveLWW(cart1: LWWCart, cart2: LWWCart): LWWCart { return cart1.timestamp > cart2.timestamp ? cart1 : cart2;}// Problem: If user adds ItemA on Node1, ItemB on Node2,// one item is lost! // Merge Function - Preserves all additionsinterface MergeCart { items: Set<string>;} function mergeCarts(cart1: MergeCart, cart2: MergeCart): MergeCart { return { items: new Set([...cart1.items, ...cart2.items]) };}// Better: Cart contains ItemA AND ItemB after merge // CRDT G-Counter - Increment-only counter, auto-mergeableinterface GCounter { [nodeId: string]: number; // Each node tracks its own increments} function mergeGCounters(a: GCounter, b: GCounter): GCounter { const result: GCounter = { ...a }; for (const [nodeId, count] of Object.entries(b)) { result[nodeId] = Math.max(result[nodeId] || 0, count); } return result;} function getValue(counter: GCounter): number { return Object.values(counter).reduce((sum, n) => sum + n, 0);}// Perfect: Count is always correct regardless of merge orderThere's no universal 'correct' conflict resolution. A shopping cart should merge items (union). A bank balance cannot—the database can't know whether to add or subtract conflicting updates. Choose resolution strategies that match your domain semantics, or use strong consistency where conflicts are unacceptable.
We've explored the theoretical foundations that govern distributed database design. These aren't academic exercises—they're practical constraints that shape every database decision.
What's next:
With the theoretical foundation established, we'll explore the practical landscape of NoSQL databases. The next page examines the four primary NoSQL categories—key-value, document, column-family, and graph databases—each optimized for different data models and access patterns.
You now understand the CAP theorem deeply—its properties, implications, and limitations. You can distinguish ACID from BASE semantics, classify databases by their CAP preferences, and understand conflict resolution strategies. This theoretical foundation will guide your database technology choices.