Requirements & scope
Problem Statement & Requirements
Functional Requirements
put(key, value)— store a key-value pairget(key)→ value — retrieve the value for a given keydelete(key)— remove a key-value pair- Support variable-size values (1 byte to 1 MB)
- Keys are strings (up to 256 bytes)
- Automatic data expiration (TTL support)
Non-Functional Requirements
- High availability — always writable, even during network partitions (AP system)
- Low latency — single-digit millisecond reads and writes (p99 < 10 ms)
- Scalability — horizontally scale to petabytes across thousands of nodes
- Durability — no data loss once acknowledged
- Tunable consistency — allow clients to choose between strong and eventual consistency per request
- Fault tolerance — handle node failures, network partitions, and datacenter outages
Out of Scope
- Range queries / secondary indexes (that's a full database)
- Transactions across multiple keys
- Complex data types (lists, sets, sorted sets — that's Redis)
- SQL query interface
Scale estimations
Scale Estimations
Traffic & Storage
| Metric | Value |
|---|---|
| Total key-value pairs | 10 billion |
| Average key size | 64 bytes |
| Average value size | 10 KB |
| Total data size | 10B × ~10 KB = ~100 TB |
| Replication factor | 3 |
| Total storage with replication | ~300 TB |
| Reads / second | 500K RPS |
| Writes / second | 100K RPS |
| Read:Write ratio | 5:1 |
Node Sizing
| Metric | Value |
|---|---|
| Storage per node | 2 TB SSD |
| Nodes needed (data) | 300 TB / 2 TB = 150 nodes |
| RAM per node | 64 GB |
| Total cluster RAM | 150 × 64 GB = 9.6 TB |
| Hot data in memory | ~10% of data = 10 TB → distributed across nodes |
Network
| Metric | Value |
|---|---|
| Read bandwidth | 500K × 10 KB = ~5 GB/s |
| Write bandwidth | 100K × 10 KB = ~1 GB/s |
| Per-node read bandwidth | 5 GB/s / 150 = ~33 MB/s |
| Replication bandwidth | 1 GB/s × 2 (replicas) = ~2 GB/s |
Layered architecture
Phase 1 of 4 · Client & edge
Start with the entry points
First, define who uses the system and where requests enter. This is the boundary of the design, not yet a complete request path.
- Client SDK
- Expose simple key-based operations and explicit consistency options.
This first layer defines the system boundary. The next chapters add application logic and data dependencies. After examining the request paths and tradeoffs, we will add operational safeguards and bring the complete architecture together.
API & contracts
Phase 2 of 4 · Application & compute
Add the application logic
Keep the entry points and add the services that do the work. Give each service a clear responsibility before defining its contracts. Storage and background dependencies come next.
- Coordinator
- Any node can coordinate a request, so the cluster has no single request-routing leader.
- Hash ring
- Map each key to a stable partition, moving only part of the keyspace when membership changes.
Data Model & API
Wire Protocol
Internal Data Format (On Disk)
Data model
Phase 3 of 4 · Data & async work
Connect data and background work
The services now need their dependencies: durable state, fast lookups, or work that continues outside a request. Separate these responsibilities rather than treating every component as a database.
- Storage nodes
- Append to a durable write-ahead log before acknowledging local writes, then flush sorted tables.
- Replicas
- Use configurable read and write quorums. Overlapping quorums alone do not guarantee linearizability.
- Version resolver
- Track concurrent versions and choose an application-appropriate conflict-resolution policy.
Storage Engine
LSM-Tree (Log-Structured Merge Tree) — Write-Optimized
Bloom Filters — Avoiding Unnecessary Disk Reads
B-Tree vs LSM-Tree Tradeoff
| Aspect | LSM-Tree | B-Tree |
|---|---|---|
| Write throughput | ✅ Excellent (sequential I/O) | ❌ Random I/O for each write |
| Read latency | ❌ May check multiple levels | ✅ O(log N) single lookup |
| Space amplification | ❌ Temporary duplicates during compaction | ✅ In-place updates |
| Write amplification | ❌ Data written multiple times (compaction) | ✅ Written once (usually) |
| Range queries | ✅ Data sorted within SSTables | ✅ Natural ordering |
| Best for | Write-heavy KV stores (our use case) | Read-heavy databases |
Our choice: LSM-Tree — write-heavy workload, sequential disk I/O, good for SSDs.
Core design decisions
CAP Theorem & Design Philosophy
Our choice: AP with tunable consistency
- Default to availability + partition tolerance
- Allow clients to request stronger consistency when needed (quorum reads)
- This is the Dynamo model — write always succeeds, conflicts resolved on read
Core Component: Consistent Hashing
The Problem with Naive Hashing
Consistent Hashing Solution
Virtual Nodes (vnodes) — Solving Uneven Distribution
How Many Virtual Nodes?
| Nodes in Cluster | vnodes per Node | Total Ring Positions | Load Std Dev |
|---|---|---|---|
| 10 | 100 | 1,000 | ~5% |
| 50 | 150 | 7,500 | ~2% |
| 150 | 200 | 30,000 | ~1% |
Tradeoff: More vnodes = better distribution but more metadata to maintain and transfer during rebalancing.
Tunable Consistency (Quorum)
The N, W, R Parameters
Common Configurations
Sloppy Quorum & Hinted Handoff
Request flows
Detailed Flow Diagrams
Write Flow
Read Flow
Read Repair
Performance & caching
Compaction Strategies
Advanced design
Data Partitioning & Replication
Partition Assignment
Replication Strategy
Conflict Resolution
When multiple replicas accept concurrent writes to the same key (during a partition or with W < N), conflicts arise.
Vector Clocks
Conflict Resolution Strategies
Anti-Entropy: Merkle Trees
For replicas that haven't served reads recently, stale data can go undetected. Merkle trees solve this.
Edge cases
Suppose two replicas accept different values for the same key during a partition. A later read must either resolve the conflict according to a declared rule or return enough version information for the application to resolve it. Now delete that key: the deletion needs a durable marker so an older replica cannot resurrect the value during repair.
Tradeoffs
Tradeoffs & Design Decisions Summary
| Decision | Option A | Option B | Chosen | Why |
|---|---|---|---|---|
| CAP | CP (strong consistency) | AP (high availability) | AP (tunable) | Write availability is critical; clients can choose consistency level |
| Partitioning | Range-based | Consistent hashing | Consistent hashing + vnodes | Even distribution, minimal redistribution on scale |
| Replication | Leader-based | Leaderless (quorum) | Leaderless | No single point of failure, tunable W/R |
| Conflict resolution | LWW (simple) | Vector clocks (accurate) | Vector clocks | Preserves concurrent writes, no silent data loss |
| Storage engine | B-Tree | LSM-Tree | LSM-Tree | Write-heavy workload, sequential I/O |
| Failure detection | Heartbeat (centralized) | Gossip (decentralized) | Gossip | No single failure detector, scales to thousands of nodes |
| Anti-entropy | Full sync | Merkle tree diff | Merkle trees | O(log N) comparisons, minimal data transfer |
| Compaction | Size-tiered | Leveled | Leveled (default) | Better read performance, lower space amplification |
| Consistency model | Fixed (always strong) | Tunable (N/W/R) | Tunable | Different use cases need different guarantees |
| Membership | Static config | Dynamic (gossip) | Dynamic gossip | Nodes join/leave without manual config changes |
Reliability & fault tolerance
Phase 4 of 4 · Operations & resilience
Add operational safeguards
With the request paths and tradeoffs established, add the controls that help detect trouble and keep the system operating. The final diagram retains every layer introduced so far.
- Gossip
- Distribute membership information without a central bottleneck; failure detection is suspicion, not proof.
- Anti-entropy
- Compare replica ranges and repair divergence in the background.
Failure Detection: Gossip Protocol
Node Join / Leave / Failure Handling
Adding a New Node
Handling Node Failure
Production architecture
High-Level Architecture
Production Architecture
Further exploration
Comparison with Real Systems
Interview playbook
Interview Tips
Start with CAP — "Before diving into the design, let me clarify the consistency model. For a KV store, we usually want AP with tunable consistency." This frames the entire design.
Consistent hashing is the centerpiece — Draw the ring, explain vnodes, show why naive modulo hashing fails. This is the core algorithmic insight.
Walk through a write + read flow — Show how data flows through WAL → MemTable → SSTable, and how quorum reads work. This demonstrates you understand the full data path.
Discuss vector clocks carefully — Use a concrete example with two concurrent writes. Show how conflicts are detected and how the client resolves them.
Mention Merkle trees for anti-entropy — This shows you think about long-term consistency, not just the happy path. "How does a replica that was down for 2 hours catch up?"
Don't forget the storage engine — LSM-Tree with bloom filters is the standard answer. Explain write amplification as a tradeoff.
Gossip protocol for membership — Shows you understand decentralized failure detection. Mention the convergence time (O(log N) rounds).
Name real systems — "This is essentially the Dynamo paper architecture" or "Cassandra uses a similar approach but with LWW instead of vector clocks." Shows you've read the literature.
Tombstones for deletes — A subtle but important point. "You can't just delete from one replica — the other replicas will think the key still exists and resurrect it."