Read Replicas
ScalingProblem: Read traffic saturates the primary database.
Solution: Replicate the primary asynchronously to N read-only copies and route reads to them, keeping writes on the primary.
20 reusable building blocks, each framed the way an interview answer should be: the problem it solves, the mechanism, and - the part that actually distinguishes candidates - when not to reach for it.
Problem: Read traffic saturates the primary database.
Solution: Replicate the primary asynchronously to N read-only copies and route reads to them, keeping writes on the primary.
Problem: A single primary can no longer absorb the write volume, or the data no longer fits on one machine.
Solution: Split rows across independent databases by a shard key, mapping keys to logical shards and logical shards to physical servers.
Problem: `hash(key) % N` remaps almost every key whenever the node count changes.
Solution: Map keys and nodes onto a ring with virtual nodes; a key belongs to the first node clockwise, so resizing moves only ~1/N of keys.
Problem: A single bug, poison record or bad deploy can affect every user at once.
Solution: Partition users into independent cells, each a complete vertical stack, so failures are contained to one cell's population.
Problem: The database answers the same expensive question repeatedly.
Solution: The application checks the cache, reads the database on a miss and populates the cache; on write it updates the database then deletes the key.
Problem: An expensive aggregation is recomputed on every read.
Solution: Compute the result once on write or on a schedule, store it, and serve reads from the stored result.
Problem: Derived stores - search indexes, caches, warehouses - must stay in sync without dual writes.
Solution: Tail the database's write-ahead log and publish every change as an event stream that derived stores consume.
Problem: Expensive lookups are wasted on keys that do not exist at all.
Solution: A compact probabilistic bitmap answering 'definitely not present' or 'possibly present', with no false negatives.
Problem: Calls to a failing dependency pile up, consuming resources and preventing its recovery.
Solution: Trip open after N failures, fail fast for a cooldown, then probe with a half-open state before closing.
Problem: One slow dependency consumes the whole thread or connection pool and takes down unrelated endpoints.
Solution: Give each dependency a bounded, separate resource pool sized by its importance.
Problem: A non-essential dependency failing takes down the whole user experience.
Solution: Define, per feature, what happens when its dependency is unavailable - hide it, use a default, serve stale, or fail closed if correctness demands.
Problem: A single abusive or buggy client can consume capacity intended for everyone.
Solution: Enforce a token bucket per client, endpoint and global dimension, returning 429 with Retry-After when exceeded.
Problem: You must commit a state change and publish an event atomically, but they live in different systems.
Solution: Insert the event as a row in the same transaction as the state change; a relay reads the outbox and publishes at-least-once.
Problem: A business operation spans several services or external APIs and cannot use one transaction.
Solution: A sequence of local transactions, each with a compensating action, coordinated by an orchestrator or by events.
Problem: A message that can never succeed blocks the queue or is retried forever.
Solution: After N attempts, move the message plus its failure context to a separate queue for inspection and replay.
Problem: Work arrives faster than a single worker can process it.
Solution: Multiple workers consume from one queue; the broker delivers each message to exactly one of them.
Problem: Authentication, rate limiting and routing are duplicated in every service.
Solution: A single edge component handling TLS, authentication, rate limiting, validation and routing for all external traffic.
Problem: One shared API forces over-fetching on some clients and under-fetching on others.
Solution: A thin per-client-type backend, owned by the client team, that aggregates and reshapes responses.
Problem: A legacy system must be replaced, but a big-bang rewrite is unacceptably risky.
Solution: Route traffic through a facade, extract one capability at a time behind it, shift traffic gradually, and delete the old path.
Problem: One model cannot be optimal for both writes and reads.
Solution: Separate the write model from one or more read models, kept in sync asynchronously.