Scaling Ad Impressions
Impression counting is critical for advertising platforms. Advertisers pay per 1,000 impressions (CPM), so accuracy translates directly to revenue. Unlike standard view counting, losing an ad impression means losing money.
The Data Pipeline
When an ad renders, a pixel fires a request. The ingest servers must acknowledge this immediately. They write the event to a highly durable write-ahead log (like Kafka) before returning 200 OK. From there, Stream Processing engines (Spark Streaming, Flink) group events by Ad ID, Campaign ID, and time window.
Handling Late Data
In mobile environments, users go offline. An impression might happen at 2:00 PM, but the device only connects and sends the event at 4:00 PM. The system must support Event Time processing. It maintains "watermarks" to decide when a time window can be closed, but allows late-arriving data to update historical tables.
Database Choice
Real-time OLAP (columnar analytical) databases like Druid, Pinot, or ClickHouse are the industry standard for this; they are distinct from time-series databases such as Prometheus, InfluxDB or TimescaleDB, though they handle time-partitioned event data well. They ingest massive streams of data, organize it by time, and support sub-second analytical queries (e.g., "Show impressions for Campaign X broken down by hour").
Worked example: volumes
| Quantity | Assumption | Result |
|---|---|---|
| Impressions per day | 10 billion | about 116,000 per second average, 300,000 or more at peak |
| Raw event size | About 200 bytes | about 2 TB per day |
| Minute aggregates | 1 million active ads, sparse, only minutes with traffic | at most 1.44 billion rows per day, typically far fewer |
| Revenue at 2 dollars CPM | 10 billion divided by 1,000 x 2 | 20 million dollars per day; a 1 percent double count is 200,000 dollars per day of wrongful billing |
Pipeline
Late data policy
| Arrival delay | Handling |
|---|---|
| Within watermark, for example 5 minutes | Counted in the live window |
| Within allowed lateness, for example 24 hours | Emitted as a correction to the historical bucket |
| Beyond allowed lateness | Routed to a late-data table; included in batch reconciliation or dropped per contract |
Definitions matter
The Media Rating Council viewability guideline counts a display ad as viewable when at least 50 percent of its pixels are in view for at least one continuous second, and a video ad at 50 percent for two continuous seconds. Systems usually store both "served" and "viewable" impressions as separate measures, because advertisers may buy on either.
Dedup at scale: keep impression IDs in keyed state with a TTL a little longer than the allowed lateness; at hundreds of thousands of events per second over 24 hours that is billions of keys, so state lives in RocksDB-backed Flink state, not heap memory.
Interview framing
Lead with correctness requirements: at-least-once ingest plus dedup equals effectively-once counting; event-time windows; and a batch reconciliation that is the source for invoices. Then discuss the query side: advertisers want per-campaign, per-hour breakdowns with sub-second latency, which is why pre-aggregated rollups live in an OLAP store rather than being computed from raw events on each dashboard load.