Consistency Models & ACID vs BASE
📖 Concept
Understanding different consistency models is crucial for making informed design decisions.
Consistency Spectrum
From strongest to weakest:
| Model | Guarantee | Latency | Example |
|---|---|---|---|
| Linearizability | Reads see the latest write globally | Highest | Single-leader DB, ZooKeeper |
| Sequential | All clients see operations in same order | High | Replicated state machines |
| Causal | Operations that are causally related are seen in order | Medium | Social media comments |
| Eventual | All replicas converge to same state eventually | Lowest | DNS, Cassandra |
ACID vs BASE
ACID (Traditional SQL)
- Atomicity: Transaction is all-or-nothing
- Consistency: Data satisfies all constraints
- Isolation: Concurrent transactions don't interfere
- Durability: Committed data survives crashes
BASE (Distributed NoSQL)
- Basically Available: System guarantees availability
- Soft state: State may change without input (due to replication)
- Eventually consistent: System becomes consistent over time
| Aspect | ACID | BASE |
|---|---|---|
| Focus | Correctness | Availability |
| Scale | Vertical primarily | Horizontal |
| Use case | Banking, inventory | Social media, caching |
| Trade-off | Lower throughput | Possible stale reads |
Eventual Consistency Patterns
Read-Your-Own-Writes
After writing, the same user always sees their own write (others may see stale data temporarily).
Monotonic Reads
A user never sees older data after seeing newer data (prevents "time travel").
Causal Consistency
If operation B was caused by operation A, everyone sees A before B.
Key insight: Most applications don't need linearizability everywhere. Use strong consistency for critical operations (payments) and eventual consistency for everything else. This gives you the best of both worlds.
💻 Code Example
1// ============================================2// Consistency Models — Practical Examples3// ============================================45// ---------- Eventual Consistency with Conflict Resolution ----------67class EventuallyConsistentStore {8 constructor() {9 this.replicas = [new Map(), new Map(), new Map()];10 this.vectorClocks = new Map();11 }1213 // Write to one replica, async replicate14 async write(key, value) {15 const version = this.incrementVersion(key);16 const entry = { value, version, timestamp: Date.now() };1718 // Write to first available replica19 this.replicas[0].set(key, entry);20 console.log(`Written \${key} = \${value} (version: \${version})`);2122 // Async replication (may be delayed)23 setTimeout(() => {24 this.replicas[1].set(key, entry);25 this.replicas[2].set(key, entry);26 console.log(`Replicated \${key} to all replicas`);27 }, Math.random() * 2000);28 }2930 // Read from any replica (may be stale!)31 async read(key, replicaIndex = Math.floor(Math.random() * 3)) {32 const entry = this.replicas[replicaIndex].get(key);33 if (!entry) return null;34 console.log(`Read \${key} from replica \${replicaIndex}: \${entry.value}`);35 return entry.value;36 }3738 // Last-Writer-Wins (LWW) conflict resolution39 resolveConflict(entry1, entry2) {40 return entry1.timestamp > entry2.timestamp ? entry1 : entry2;41 }4243 incrementVersion(key) {44 const current = this.vectorClocks.get(key) || 0;45 this.vectorClocks.set(key, current + 1);46 return current + 1;47 }48}4950// ---------- Read-Your-Own-Writes Consistency ----------51class ReadYourOwnWritesRouter {52 constructor(primary, replicas) {53 this.primary = primary;54 this.replicas = replicas;55 this.recentWrites = new Map();56 }5758 async write(userId, key, value) {59 await this.primary.write(key, value);60 this.recentWrites.set(`\${userId}:\${key}`, Date.now());61 }6263 async read(userId, key) {64 const recentWrite = this.recentWrites.get(`\${userId}:\${key}`);6566 if (recentWrite && Date.now() - recentWrite < 5000) {67 // User wrote recently — read from primary (guaranteed fresh)68 return await this.primary.read(key);69 }7071 // No recent write — safe to read from replica (faster)72 const replicaIndex = Math.floor(Math.random() * this.replicas.length);73 return await this.replicas[replicaIndex].read(key);74 }75}7677// ---------- Causal Consistency with Version Vectors ----------78class CausalStore {79 constructor(nodeId) {80 this.nodeId = nodeId;81 this.data = new Map();82 this.vectorClock = {};83 }8485 write(key, value, dependsOn = null) {86 // Increment our node's clock87 this.vectorClock[this.nodeId] = (this.vectorClock[this.nodeId] || 0) + 1;8889 this.data.set(key, {90 value,91 vectorClock: { ...this.vectorClock },92 dependsOn,93 });9495 return { ...this.vectorClock };96 }9798 read(key) {99 return this.data.get(key);100 }101102 // Check if operation A happened before operation B103 happenedBefore(clockA, clockB) {104 return Object.keys(clockA).every(105 node => (clockA[node] || 0) <= (clockB[node] || 0)106 ) && Object.keys(clockA).some(107 node => (clockA[node] || 0) < (clockB[node] || 0)108 );109 }110}111112// Demo113const store = new EventuallyConsistentStore();114store.write('user:123', 'Alice');115store.read('user:123', 0); // Replica 0: "Alice"116store.read('user:123', 1); // Replica 1: might be null (not yet replicated!)117118const causal = new CausalStore('node-1');119const v1 = causal.write('post', 'Hello World');120causal.write('comment', 'Nice post!', v1); // Depends on the post121console.log('Causal order preserved via vector clock');
🏋️ Practice Exercise
Consistency Level Selection: For each feature of a social media app, choose the appropriate consistency model (linearizable, causal, eventual) and justify: (a) posting a tweet, (b) viewing a timeline, (c) following a user, (d) sending a direct message, (e) counting likes.
Conflict Resolution: Two users simultaneously update the same document (Google Docs scenario). Design three conflict resolution strategies: last-writer-wins, merge, and operational transform.
Vector Clocks: Implement vector clocks for a 3-node system. Demonstrate how they detect concurrent writes vs causal writes.
ACID Transaction Design: Design the database transactions for an e-commerce checkout: reserve inventory → charge payment → create order. What isolation level do you need? How do you handle partial failures?
⚠️ Common Mistakes
Assuming 'eventual' means 'soon' — eventual consistency could mean milliseconds or minutes, depending on the system. Always understand the expected convergence time for your system.
Not specifying which consistency model when designing — saying 'the system is consistent' is meaningless. Specify: linearizable, causal, or eventual, and explain why.
Using strong consistency everywhere 'to be safe' — strong consistency at scale requires consensus protocols, which add significant latency and reduce throughput. Most data doesn't need it.
Ignoring conflict resolution — in AP systems with eventual consistency, concurrent writes WILL conflict. You need a strategy: last-writer-wins, merge, or application-level resolution.
💼 Interview Questions
🎤 Mock Interview
Practice a live interview for Consistency Models & ACID vs BASE