Distributed Cache
Large-scale applications — social feeds, e-commerce product pages, ride-sharing dispatch, gaming leaderboards — share a common problem: their databases cannot absorb the full read load at acceptable latency. A distributed in-memory cache sits between the application tier and the database, holding the hot working set of data in RAM and serving it in under a millisecond. Facebook’s Memcached fleet serves trillions of items across thousands of nodes; Redis Cluster powers similar workloads at GitHub, Twitter/X, and Shopify. The design looks trivial — “put a hash map in front of your database” — but the engineering challenges around sharding keys across nodes, handling node failures without a miss storm, preventing stampedes when a popular key expires, and keeping cache and source of truth consistent are where the real depth lives.
We are designing a horizontally-scalable, multi-node in-memory key-value cache cluster. Application services embed a client library that routes each key to the right cache node, falls back to the backing database on a miss, and populates the cache for subsequent reads. The cluster must handle 1 million read operations per second at sub-millisecond latency.
Functional Requirements
- GET: Return the stored value for a key, or a cache-miss signal if the key is absent or has expired.
- SET: Store a key-value pair with an optional TTL (time-to-live in seconds). Overwrites any existing value.
- DELETE: Explicitly remove a key and its value from the cache.
- Batch reads (MGET): Fetch multiple keys in a single network round-trip; missing keys return null in the response array.
- Batch writes (MSET): Store multiple key-value pairs atomically per-node (best-effort across nodes).
- TTL and expiry: Keys expire automatically after their TTL elapses; the client is responsible for choosing an appropriate TTL based on acceptable staleness.
- Eviction: When a node’s memory is full, evict keys according to a configurable policy (LRU or LFU).
Out of Scope
- Durable persistence. Data loss on a node restart is acceptable; the backing database is the source of truth.
- Pub/sub and event streaming. Messaging features are out; this is a cache, not a message broker.
- Server-side scripting (e.g., Lua, stored procedures). Computation belongs in the application tier.
- Cross-datacenter replication. Each datacenter operates its own independent cache cluster; cross-DC consistency is an application concern.
- Authentication and multi-tenancy. Assumed handled at the network boundary (e.g., mTLS, VPC isolation, or an API gateway).
- Strong consistency guarantees. The cache and the database will be inconsistent within the TTL window — this is by design.
Non-Functional Requirements
- Scale: 1,000,000 read QPS and 100,000 write QPS at peak (10:1 read:write ratio).
- Latency: p50 < 0.3 ms, p99 < 1 ms for GET. The cache engine must not be the bottleneck; network RTT will dominate.
- Availability: 99.99% (< 53 minutes downtime/year). A cache cluster outage must degrade performance — not correctness. The backing database remains authoritative.
- Working set: ~1 TB of hot data in RAM across the cluster. Average key: 100 B; average value: 1 KB; average entry: ~1.1 KB.
- Consistency: Cache-aside best-effort; staleness is bounded by TTL. The cache and backing database may diverge within the TTL window — acceptable for the use cases in scope.
- No durability requirement: A cache miss is a performance penalty, not a correctness failure. Node restarts may flush data cold.