Skip to content
About
Main content

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.

Try each one before you open the answer.
1Design a streaming job that joins 1 million orders per second with a clickstream, attributing each order to clicks within the previous 10 minutes. Estimate the state, and say what can go wrong.Show the answer

Estimate

At about 200 bytes per record, with both sides buffered for 600 seconds: 1,000,000 × 200 B × 600 s × 2 = 240 GB of join state, more if both streams run at that rate.

Design

That does not fit on a heap, so use an embedded LSM state store with , and expect traffic to follow the changes, not the 240 GB. Use an keyed by user, with an explicit bound, a per partition, and handling for idle partitions.

What can go wrong

  1. Events later than the bound are lost silently: measure the real distribution of arrival delays, and count records dropped because their match was evicted.
  2. A hot user key cannot be split: check key skew.
  3. causes write stalls, which show up as .

Recommend

Run at about 50–60% of capacity, so a 10-minute outage is caught up in about as long. And decide whether a 20-minute bound, which doubles the state, is worth 1–2% more matches.

Read moreTime-bounded stateStream-stream joinsIncremental checkpointingThe failure is silent

2A streaming job holds 600 GB of state and suffers a 15-minute outage. How long until it is current again, and how do you shorten that?Show the answer

Restore

Restore time = state ÷ bandwidth: at 1 GB/s, 600 seconds, 10 minutes. Incremental restore from local disk can be faster; from remote storage, slower.

Catch up

The backlog is the arrival rate × 900 seconds. A job running at 90% of capacity drains at only 10% of capacity, so , 135 minutes; at 60%, it drains at 40%, taking 1.5 times the outage, 22.5 minutes. In total, .

Shorten it

More headroom (the recovery budget); faster restore (local recovery on the same machine where possible, and smaller state through TTLs and tighter ); or replaying faster than normal by scaling out temporarily. Scaling out moves state too, so decide on it before the incident.

Check

The input log's must be longer than the outage plus the catch-up.

Read moreRecovery time and headroomDynamic scalingWhat the design assumes

3Finance requires that charges reach the billing database exactly once. Compare a transactional sink with idempotent writes.Show the answer

Transactional sink

driven by checkpoints. Output is invisible until the checkpoint completes, so is at least the checkpoint interval. The broker's transaction timeout must exceed the longest checkpoint plus the worst recovery, or a long outage silently loses pre-committed data.

Idempotent writes

Derive a deterministic key, such as a charge ID from the order ID, and , so overwrite the same row. No timeout coupling, output visible at once; it needs a that supports upserts.

Both need

Deterministic replay: no now(), no random(), no lookups whose results depend on timing.

Recommend

upserts for the charges table. Use transactions only for sinks that cannot upsert, such as append-only logs, and then set the timeout above checkpoint duration plus recovery, and document the dependency.

Read moreExactly-once: checkpoint plus transactional sink

4You need to scale a stateful streaming job from 8 to 16 instances without a long outage, and the team wants autoscaling. Is that a good idea?Show the answer

What rescaling costs

It is a stop and restore with state moved (downtime ≈ state moved ÷ bandwidth), followed by a catch-up that usually dominates.

Check the maximum parallelism

It must be at least 16, and ideally divisible by both counts (for example 128 or 720), or ranges get split unevenly.

Autoscaling

An actuator that is expensive, slow and itself increases lag will oscillate. Use hysteresis (scale up at 60%, down at 30%), cooldowns, and no scaling during a backlog. Prefer scaling ahead of known peaks, during quiet hours.

Longer term

State stored remotely makes rescaling cheap, at the price of a round trip per access and uncertain performance for hot loops.

Read moreDynamic scalingWhy autoscaling is hardThe permanent decision

5Developers want to add a field to the per-user state and deploy daily. How do you keep deployments safe?Show the answer

Principle

The state's schema is a public interface: treat changes to it like API changes.

Practice

  1. Explicit IDs from day one.
  2. Only compatible changes, such as adding a field with a default; never remove key fields or change types without a migration.
  3. A before every deployment, and a test restore in staging with a copy of real state.
  4. For incompatible changes, run in parallel and cut over: a second job started from an earlier , double compute during the replay, source retention long enough, and idempotent sinks so the overlap is harmless.
  5. Avoid starting from empty state for jobs with long , which would give, say, 30 days of degraded output.

The law

Long-lived state turns every change into a migration.

Read moreSavepoints and state schema evolutionThe escape hatches and their prices

6A dashboard shows hourly counts 2% lower than the warehouse, with no errors anywhere. Diagnose.Show the answer

Check, in order

  1. Every bound chosen to save memory: the join interval, , allowed lateness, maximum session length. Measure the real distribution of event delays against each.
  2. Watermarks: idle partitions that wake up with old events create late records, which are dropped.
  3. Determinism: did something non-deterministic make a replay after a crash change the counts?
  4. The sink: an append-only sink receiving a , or an aborted transaction.

Fix

Widen the bound that is cutting data, add counters and alerts for records dropped as late or evicted, and reconcile against the warehouse as a routine check.

Read moreTTL is a silent correctness changeThe failure is silentIdle channelsWhat streaming forbids

7A job counting orders per restaurant is dominated by one restaurant with 30% of the traffic, and throughput does not grow with more instances. What do you do?Show the answer

Diagnose

A key's state cannot be split, so is capped at about the capacity of one instance ÷ 0.3 ≈ 3.3 instances' worth, however many you add.

Fix

Counting is and , so aggregate in two phases: salt the key into, say, 8 sub-keys, count locally, strip the salt, then sum. It costs an extra and a short additional stage of latency.

When that fails

For order-sensitive or non-decomposable operations, such as a pattern match or an exact median: reduce the work for that key at the source (pre-aggregate in the producer), or accept the cap.

Monitor

Per-key skew, so the problem shows up before it hits the limit.

Read moreThe indivisible key

8Product asks for real-time, exactly-once, 24-hour sliding counts of distinct users per restaurant, refreshed every second. Which parts are expensive?Show the answer

Sliding 24 hours, every second

That is , impossible naively. of 1 second help, but combining 86,400 panes per output is costly, so use larger panes (say 1 minute) and a coarser refresh, or a structure that merges cheaply.

Distinct users

Not decomposable exactly; use , which merge.

Exactly-once

A transactional sink adds latency of at least the checkpoint interval, which conflicts with refreshing every second, unless the sink is an idempotent upsert on (restaurant, refresh time).

Recommend

Approximate distinct counts, 1-minute panes, idempotent upserts, and a clear statement of which numbers are approximate.

Read moreSliding windowsExactly-once