Who Saw the Ad 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
L6

Problem

Our TV and digital ad measurement pipelines are completely separate: TV exposure arrives as second-level logs from set-top boxes, and digital arrives as impression logs from connected apps. Design a pipeline that combines them into cross-platform audience metrics for campaigns that ran on both broadcast TV and connected apps.

TV and digital. Same viewer, two measurement worlds.

Business requirements

Worked solution and explanation

What this really is

This is a household-level set union dressed up as ad measurement, with a latency split hiding inside it. Anyone can count distinct viewers. What separates candidates is seeing that TV and digital carry different identifiers for the same household, so the union only works through an identity graph keyed on hashed ids. The second tell is that frequency capping is a different pipeline: the ad server asks on every impression and cannot wait for a warehouse. Miss the graph and you double-count every household that saw both. Miss the hashing and raw PII sits in one dataset when the CCPA assessor looks.

Walk the requirements

Step 1: Count households, never sum platforms

A set-top box id and a mobile device id never match on equality. identity_graph maps both hashed ids to one household key, and cross_platform_reach counts distinct on that key. Summing TV reach and digital reach is exactly the double count the advertiser is paying to remove.

Step 2: Hash in place, move only hashes

tv_hasher and digital_hasher salt and hash identifiers before anything crosses a dataset boundary. Only hashed ids, the graph mapping and aggregates move. Centralising raw emails and device ids to make the join easy is the design that fails the audit.

Step 3: Give the ad server its own store

frequency_stream tails digital_impressions and updates per-household counters in cap_store, a Redis key-value store. The ad server reads it on every request in milliseconds. Pointing it at the warehouse costs hundreds of milliseconds, the cap arrives late, and campaigns over-deliver.

Step 4: Order the night and alert early

The orchestrator runs identity refresh, then measurement, then the clean-room publish. Its late-vs-SLA alert fires at 2am with hours to recover, not at 9am when the contract is already breached.

Ids are hashed at the source. `cross_platform_reach` joins through `identity_graph` and publishes to a warehouse clean room. Frequency caps take a separate streaming path into `cap_store`.
  • tv_exposures (source, object storage)
  • digital_impressions (source, Kafka)
  • tv_hasher (transform, Spark)
  • digital_hasher (transform, Spark)
  • identity_graph (storage, PostgreSQL)
  • frequency_stream (transform, Flink); freshness: real-time
  • cap_store (storage, Redis); freshness: real-time
  • ad_server (consumer, API)
  • orchestrator (transform, Airflow); alerting: Identity refresh or measurement job late vs morning SLA
  • cross_platform_reach (transform, Spark); freshness: < 24h
  • clean_room (storage, warehouse); freshness: < 24h
  • advertisers (consumer, dashboard)
  • tv_exposures -> tv_hasher
  • digital_impressions -> digital_hasher
  • tv_hasher -> identity_graph
  • digital_hasher -> identity_graph
  • digital_impressions -> frequency_stream
  • frequency_stream -> cap_store
  • cap_store -> ad_server
  • orchestrator -> tv_hasher
  • orchestrator -> cross_platform_reach
  • orchestrator -> clean_room
  • identity_graph -> cross_platform_reach
  • cross_platform_reach -> clean_room
  • clean_room -> advertisers
  • Sources: tv_exposures, digital_impressions
  • Hashing + identity: tv_hasher, digital_hasher, identity_graph
  • Frequency-cap path: frequency_stream, cap_store, ad_server
  • Measurement + delivery: orchestrator, cross_platform_reach, clean_room, advertisers
The whiteboard answer
Copy TV logs into the digital warehouse, join on email, add the two reach counts, and let the ad server query the same warehouse for caps.
The design that survives
Hash per source, count distinct on the household key from identity_graph, publish to a clean room, and serve caps from cap_store on a streaming path.

Common follow-up questions