Scenarios
Longer problems, like those in senior system design interviews. A strong answer estimates with numbers first, names the mechanism, then recommends a fix and says what it costs.
1A nightly job has 10,000 tasks, and on every run at least some of them fail. How do you make it finish reliably?Show the answer
Accept
At this scale, some failure is certain. Design for it.
Design
Re-run failed tasks, for example up to three attempts. Blacklist bad nodes, with a decay time and a quota (at most about 10% of nodes), so failures stay independent rather than repeating on the same machine. Keep shuffle files, so a lost partition does not force re-running a whole stage.
Trade-off
Blacklisting too many nodes shrinks capacity.
Read moreTask re-execution and blacklistingThe cost is the fan-in
2A pipeline normally runs at 90% utilization and was down for 3 hours. How long until it catches up, and what should you change?Show the answer
Estimate
Its spare capacity is 10% of maximum throughput, a ninth of the arrival rate, so catching up takes about 3 h × 9 = 27 hours.
Change
To recover from a 3-hour outage in 1 hour, capacity must be (3 + 1) × the arrival rate, so the pipeline should run at about 25% utilization at peak.
The principle
Utilization should be set by the recovery budget, not the latency budget. The cost is more machines.
Read moreThe catch-up condition
3Should the primary database fail over automatically?Show the answer
Compare
P(real failure) × downtime saved, against P(false alarm) × cost of an unneeded failover. With one real failure a year saving 20 minutes, and four false alarms a year each costing 180 minutes, manual failover wins.
Recommend
Keep automatic failover for stateless services and read replicas.
Either way
Enforce fencing at the storage layer with an epoch number, and expect to lose data equal to the replication lag at the worst moment.
Read moreAutomatic or manual is arithmeticFailover converts RTO into RPO
4Replaying yesterday's data gives different totals from the original run. How do you diagnose and fix it?Show the answer
Diagnose
Look for non-determinism: calls to now(), random numbers, lookup tables that have since changed, iteration over unordered collections.
Fix
Carry event time through the log instead of reading the clock, and version reference data by date.
Prevent
Add a test that runs the job twice on the same input and compares output hashes.
Read moreDeterminism is the precondition
5A service charges a card through a payment API and writes to a database. Which recovery mechanism applies to each step, and where should the irreversible step go?Show the answer
Classify
The database write can be rolled back, or protected with a write-ahead intent. The card charge is visible to the outside world and cannot be undone, only compensated by a refund.
Design
Restructure so the single irreversible act comes last. Record the intent first, then act, and run a recovery scan that reconciles intents left unfinished by a crash.
Read moreThe nine ways to recoverWrite-ahead intentUndo stops at the first visible effect
6Design the payment step so that a retry can never charge a customer twice.Show the answer
Design
Use the order ID as the idempotency key, generated by the client and sent on the first attempt and every retry. The provider, or your own table, stores key → outcome, committed in the same transaction as the charge, and returns the stored outcome on a repeat.
Check the window
If the provider keeps keys for 24 hours and your longest recovery has been 3 hours, the bet holds with margin. Write it in the runbook.
Without provider support
Keep your own table keyed by order ID, and add a reconciliation job against the provider's records.
Trade-off
Deduplication state grows without bound, hence the window: at 100,000 keys per second with 32-byte entries, one hour is about 11 GB.
Read moreImposed idempotenceDedup windows
7A Kafka-style consumer writes to PostgreSQL, and after crashes some events are counted twice. Fix it.Show the answer
Diagnose
The consumer's position lives in the broker and the effect lives in the database, so no order of committing the two is correct.
Fix
Store the offset in a database row, updated in the same transaction as the effect, and on startup seek to the stored offset. Fence writes by consumer generation, so a stale owner after a rebalance cannot write.
Failure modes
The consumer's position is now tied to one sink, and rebalances can race.
If the sink were an HTTP API
Use deterministic idempotency keys instead.
Read moreConsumer offsets and commit orderingOffset commits must be fenced
8A stream-processing job claims exactly-once, yet patients receive the same SMS twice. Explain.Show the answer
Explain
The engine's guarantee covers only its own state and its transactional sinks. Sending an SMS from user code is a side effect outside every checkpoint, so it repeats when the job recovers.
Options
Send through an outbox with an idempotency key, if the SMS provider supports one. If not, accept at-least-once and say so; or make the message harmless to repeat ("your results are ready" twice).
Decide
With the business owner.
Read moreThe exactly-once category error
9A transactional sink lost data after a 40-minute outage. Diagnose.Show the answer
Diagnose
The sink's timeout for prepared transactions, say 15 minutes, was shorter than failure detection + restart + draining the backlog. The sink aborted the prepared transaction before the engine could commit it, and the data in it was lost.
Fix
Raise the timeout above the worst-case recovery time, and alert on the age of pending transactions.
Remember
Latency through such a sink is quantized to the commit interval.
Read moreThe prepared transaction has a timeoutLatency is quantized
10One malformed message blocked a partition all night. Design the policy for such messages.Show the answer
Count attempts durably
Keep the retry count in the message headers; a counter in memory resets on restart.
Classify at the source
A parse error is permanent; a refused connection is transient. That way a downstream outage does not quarantine good data.
Choose by data
For a ledger, stop the line and page someone. For a clickstream, skip the message to a dead-letter queue.
Close the loop
Reconcile counts, or alert on dead-letter queue depth, with an owner.
Read morePoison pills and skip-and-logThe classifier is asymmetric
11For two data centres, choose between three full copies of the data and two copies plus a witness.Show the answer
The witness
It saves a third of the storage (2× instead of 3×) and gives the odd vote. But losing one site leaves a single copy for the hours of rebuild, and the witness must be in a third site, not inside either data site.
Recommend
Three full copies for critical data, such as patient records; the witness design for less critical data.
12A stream job keeps running totals per seller. After a failure and restart, the totals are slightly off. Why, and what would you change?Show the answer
Diagnose
State and input positions were most likely restored from different moments, or offsets are committed by a different mechanism from the state.
Fix
Checkpoint state and input positions together, with a barrier-based snapshot. Use aligned barriers if checkpoint size is the concern and unaligned if checkpoint time under backpressure is, and note that aligned checkpoints slow down exactly when the job is stressed. Use incremental checkpoints for large state, planning retention, since each checkpoint depends on the previous one.
External sinks
Use idempotent upserts, or a two-phase-commit sink whose transaction timeout exceeds the checkpoint interval.
Verify
Crash tests, and comparing the totals with a batch recomputation.
13Your service must send a legally required letter, as physical mail through a vendor's API, exactly once per event. The vendor has no idempotency support. Design it.Show the answer
Choose
Exactly-once is impossible for an external effect that cannot be deduplicated. Choose with the business owner: a duplicate letter is annoying and costs money; a missing one may be a legal breach. So choose at-least-once, with tight control of duplicates.
Design
The consumer upserts a row in pending_letters keyed by event_id, an idempotent intermediary. A separate sender records its intent ("about to send"), calls the vendor, then marks the row sent. Make the letter the last effect in the sequence.
After a crash
Rows left in "about to send" are known unknowns. Reconcile them against the vendor's report, or with a human check, before sending again. Monitor how many such rows exist.
Trade-off
An extra table and a small manual process, in exchange for ambiguity that is visible and bounded.