Consistency Models & ACID vs BASE

0/2 in this phase0/45 across the roadmap

📖 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

codeTap to expand ⛶
1// ============================================
2// Consistency Models — Practical Examples
3// ============================================
4
5// ---------- Eventual Consistency with Conflict Resolution ----------
6
7class EventuallyConsistentStore {
8 constructor() {
9 this.replicas = [new Map(), new Map(), new Map()];
10 this.vectorClocks = new Map();
11 }
12
13 // Write to one replica, async replicate
14 async write(key, value) {
15 const version = this.incrementVersion(key);
16 const entry = { value, version, timestamp: Date.now() };
17
18 // Write to first available replica
19 this.replicas[0].set(key, entry);
20 console.log(`Written \${key} = \${value} (version: \${version})`);
21
22 // 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 }
29
30 // 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 }
37
38 // Last-Writer-Wins (LWW) conflict resolution
39 resolveConflict(entry1, entry2) {
40 return entry1.timestamp > entry2.timestamp ? entry1 : entry2;
41 }
42
43 incrementVersion(key) {
44 const current = this.vectorClocks.get(key) || 0;
45 this.vectorClocks.set(key, current + 1);
46 return current + 1;
47 }
48}
49
50// ---------- 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 }
57
58 async write(userId, key, value) {
59 await this.primary.write(key, value);
60 this.recentWrites.set(`\${userId}:\${key}`, Date.now());
61 }
62
63 async read(userId, key) {
64 const recentWrite = this.recentWrites.get(`\${userId}:\${key}`);
65
66 if (recentWrite && Date.now() - recentWrite < 5000) {
67 // User wrote recently — read from primary (guaranteed fresh)
68 return await this.primary.read(key);
69 }
70
71 // 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}
76
77// ---------- Causal Consistency with Version Vectors ----------
78class CausalStore {
79 constructor(nodeId) {
80 this.nodeId = nodeId;
81 this.data = new Map();
82 this.vectorClock = {};
83 }
84
85 write(key, value, dependsOn = null) {
86 // Increment our node's clock
87 this.vectorClock[this.nodeId] = (this.vectorClock[this.nodeId] || 0) + 1;
88
89 this.data.set(key, {
90 value,
91 vectorClock: { ...this.vectorClock },
92 dependsOn,
93 });
94
95 return { ...this.vectorClock };
96 }
97
98 read(key) {
99 return this.data.get(key);
100 }
101
102 // Check if operation A happened before operation B
103 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}
111
112// Demo
113const 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!)
117
118const causal = new CausalStore('node-1');
119const v1 = causal.write('post', 'Hello World');
120causal.write('comment', 'Nice post!', v1); // Depends on the post
121console.log('Causal order preserved via vector clock');

🏋️ Practice Exercise

  1. 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.

  2. Conflict Resolution: Two users simultaneously update the same document (Google Docs scenario). Design three conflict resolution strategies: last-writer-wins, merge, and operational transform.

  3. Vector Clocks: Implement vector clocks for a 3-node system. Demonstrate how they detect concurrent writes vs causal writes.

  4. 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