The Same Stream Twice

A hard Pipeline Design interview practice problem on DataDriven. Design the pipeline on the canvas: sources, transforms, storage and checks, then get feedback on it.

Domain
Pipeline Design
Difficulty
hard
Seniority
senior
Asked in
The Walt Disney Company

Problem

A global streaming-video platform collects about 2 billion playback heartbeat events a day from 150 million subscribers, and two teams read the same feed: reliability needs rebuffering spikes per title and region detected within seconds and pushed straight to an on-call paging tool, while finance pays studios royalties on exact minutes-watched and cannot tolerate a single double-counted or dropped event. Design the pipeline so both teams consume the same durable ingest independently, with the live path detecting spikes fast and paging on-call while the daily royalty report counts each raw event exactly once instead of reusing the live aggregation. Keep a malformed heartbeat from a bad device build from stalling the live path.

Business requirements

Worked solution and explanation

Why this problem exists in real interviews

This is two consumers with opposite correctness budgets reading one event stream, disguised as a streaming dashboard. Reliability wants approximate-and-instant: a rebuffering spike per title and region in seconds, off-by-a-few is fine. Finance wants exact-and-patient: royalties on minutes-watched, where one double-counted retry is a contract dispute. The trap is the single aggregation serving both: too rough to bill on, and once you bolt exactness onto it, too slow to page on-call.

The whiteboard answer is one stream writing a minutes_watched table both the QoE dashboard and the royalty report read. It looks clean for a week. Then a producer retries during a deploy, both counts inflate, and finance overpays a studio. Or a malformed heartbeat crashes the consumer, it replays the same offset, and reliability goes blind for hours. Three requirements failed quietly because they were never separated.


Walk the requirements

Step 1: Land everything in one durable queue

Heartbeats land in a partitioned message queue first. The stream processor and the daily batch each read from their own committed offsets, so neither starves the other and either can replay alone. The subtle failure is chaining the royalty batch off the stream processor's output instead of the raw queue: royalties then inherit the live path's approximations and lose independent replay. Two separate collection paths drift just as badly, and the day they disagree nobody can say which number is right.

Step 2: Stream the QoE path; approximate is the budget

A stream processor windows heartbeats by title and region, flags rebuffering above a baseline, and emits to an alert destination inside < 1min. Counting a viewer mid-transition slightly wrong is fine; live-feel is the budget. The 'compute it exactly before we alert' version is where on-call learns of the incident from Twitter.

Step 3: Make royalty exactly-once at the sink, not in the stream

The daily batch reads raw heartbeats straight from the queue, where each event carries a stable event_id from the player. It dedups on that id and writes idempotently to the warehouse, so a producer retry is absorbed and a window-edge event is not dropped. Exactly-once is enforced at the sink with a key, not hoped for in the streaming layer. Billing from the approximate live table is the mistake that becomes an overpayment.

Step 4: Quarantine poison pills, keep draining

Validation lives on the consumer: a heartbeat that fails schema (the missing title_id from the bad firmware build) routes to a dead-letter quarantine and its offset is acked, so the main path keeps moving. A separate process triages the quarantine, and its depth is alerted on so a firmware regression is visible. If a bad event can halt the consumer, one device build blinds reliability during the incident it most needed to see.


The shape that fits

One partitioned queue (`Kafka`) feeds an approximate live path (`Flink`, `< 1min`) that pages on-call in seconds, and an exactly-once daily batch (orchestrated by `Airflow`, `< 24h`) that reads the same queue directly, dedups on `event_id`, and lands royalties in `Snowflake`. Bad events go to a dead-letter quarantine, not the floor.
  • Playback heartbeat ingest (source, Kafka)
  • Live QoE stream processor (transform, Flink)
  • Rebuffering alert path (consumer, PagerDuty)
  • Dead-letter quarantine (storage, S3)
  • Daily royalty batch (transform, Airflow)
  • Royalty warehouse (storage, Snowflake)
  • Finance royalty report (consumer, Tableau)
  • Playback heartbeat ingest -> Live QoE stream processor
  • Playback heartbeat ingest -> Daily royalty batch
  • Live QoE stream processor -> Rebuffering alert path
  • Live QoE stream processor -> Dead-letter quarantine
  • Daily royalty batch -> Royalty warehouse
  • Royalty warehouse -> Finance royalty report
  • Ingest: Playback heartbeat ingest
  • Two paths: Live QoE stream processor, Daily royalty batch
  • Sinks: Rebuffering alert path, Dead-letter quarantine, Royalty warehouse, Finance royalty report

Common follow-up questions