Listens From Everywhere, Counted Once

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
L7

Problem

Our users stream music on phones, desktop apps, smart speakers, and web browsers. We want to show artists and labels near-real-time stats on how their tracks are performing, and we need a historical record for royalty calculations. Design a data pipeline that collects listening events from all our device types.

Phones, tablets, laptops. And some of them report late.

Business requirements

Worked solution and explanation

The trap

This is a two-reader correctness split dressed up as music analytics. The artist dashboard wants minutes and tolerates a little noise. Royalties want every qualifying listen counted exactly once, under the moment it actually happened, and erasable on request. Anyone can draw Kafka into Flink into a dashboard. What separates candidates is refusing to let royalties read that streaming aggregate. Do it and a phone that buffered March listens offline drains them in April: April's payout inflates, March's shrinks, and the rolled-up counts have no user_id left to delete.

Walk the requirements

Step 1: Split by correctness budget

device_listens fans out twice: to event_time_windows (Flink) feeding dashboard_store for minutes-fresh counts, and to event_archive for the durable record. One source with two freshness tiers means the paths can differ only in how soon they report, never in what happened.

Step 2: Make listen_id the dedup contract

The client stamps a stable listen_id. royalty_dedup_batch drops repeats on it before counting, then writes through a staging table, so a retry, a double-send or a rerun lands the same number every time.

Step 3: Attribute to event_time, never arrival time

Smart speakers replay hours of buffered listens. The Flink windows use the device timestamp, and the archive is partitioned by it, so a late drain reopens an older partition that the nightly overwrite recomputes. Attribute on arrival and every drain moves money between months.

Step 4: Route deletion to every place the user lives

deletion_orchestrator removes the user's rows from event_archive, triggers recompute of the affected partitions in royalty_warehouse, and clears the user's contribution from dashboard_store. Deleting at the raw layer alone passes the demo and fails the audit.

The shape that fits

One `event_archive` feeds a real-time Flink path and a nightly Spark path; `deletion_orchestrator` reaches the archive and both aggregates.
  • device_listens (source, Kafka)
  • event_archive (storage, object storage)
  • event_time_windows (transform, Flink); freshness: real-time
  • dashboard_store (storage, PostgreSQL); freshness: < 15min
  • royalty_dedup_batch (transform, Spark); freshness: < 24h; backfill: partition_overwrite; idempotency: staging_table
  • royalty_warehouse (storage, warehouse)
  • deletion_orchestrator (transform, Airflow)
  • artist_dashboard (consumer, Grafana)
  • royalty_team (consumer, Metabase)
  • device_listens -> event_archive
  • device_listens -> event_time_windows
  • event_time_windows -> dashboard_store
  • event_archive -> royalty_dedup_batch
  • royalty_dedup_batch -> royalty_warehouse
  • dashboard_store -> artist_dashboard
  • royalty_warehouse -> royalty_team
  • deletion_orchestrator -> event_archive
  • deletion_orchestrator -> royalty_warehouse
  • deletion_orchestrator -> dashboard_store

Common follow-up questions