Live Viewers, Live Billing

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
L4

Problem

Advertisers keep disputing our impression invoices, and creators complain their live viewer count trails the chat. A top stream draws 200k concurrent viewers, and an ad break fires every viewer's impression within the same second. Design the pipeline for our livestream events.

The stream is live. The data cannot wait.

Business requirements

Worked solution and explanation

Two readers, two correctness budgets

This is one event stream split for two readers whose budgets pull in opposite directions, dressed up as livestream analytics. Creators want a viewer count that feels live and can be a little off. Advertisers pay only for ads a viewer watched, counted once. Anyone draws a Kafka topic and a stream processor. The trap is the single aggregator both read: sized to feel live, it double-counts on player retries; sized to be exact, it lags behind chat. Either way the next invoice gets disputed.

The default reach is one streaming job that writes a shared metrics table that the creator dashboard and ads billing both query. Creators feel the lag of the heavier billing logic running beside their counts. Billing inherits the dashboard's tolerance for approximation, so a retried impression counts twice and a requested ad that never played gets invoiced. One table, two unhappy readers.


Walk the requirements

Step 1: Stream creator metrics live and accept approximation

creator_metrics_stream runs on Flink at real-time freshness and writes rolling viewer and chat counts into creator_metrics_store, a Redis serving layer that creator_dashboard polls every few seconds. Approximate is fine here: a viewer mid-reconnect counted twice for one heartbeat costs nothing. Holding counts until they are exact is the version where creators notice the lag on air.

Step 2: Bill each watched ad once, on a stable id

Every impression carries a stable impression id from the player. dedup_watched_impressions drops repeats of that id, keeps only ads the viewer actually watched, and writes to billable_impression_store, which ads_billing reads. Mobile players retry on flaky networks, so without the dedup one ad break bills several times. Without the watched check, an ad that never played gets invoiced. Flink's checkpointed state lets the dedup survive a restart without dropping or repeating an id.


The shape that fits

One Kafka source feeds two paths: a live, approximate stream for creators and a deduplicated, watched-only path that bills each ad exactly once.
  • livestream_events (source, Kafka)
  • creator_metrics_stream (transform, Flink); freshness: real-time
  • creator_metrics_store (storage, Redis)
  • creator_dashboard (consumer, Grafana)
  • dedup_watched_impressions (transform, Flink)
  • billable_impression_store (storage, PostgreSQL)
  • ads_billing (consumer)
  • livestream_events -> creator_metrics_stream
  • livestream_events -> dedup_watched_impressions
  • creator_metrics_stream -> creator_metrics_store
  • creator_metrics_store -> creator_dashboard
  • dedup_watched_impressions -> billable_impression_store
  • billable_impression_store -> ads_billing

Only creator_metrics_stream carries a freshness chip. The billing side runs on Flink for its exactly-once state, not for speed: finance reads one report a day, and the store it reads is durable and queryable rather than the Redis layer the creator view hits.


Common follow-up questions