system-design/design-ad-system.md

45. Design an Ad Click / Counting System

Tests: extreme write throughput, deduplication, fraud detection, real-time aggregation, billing accuracy.

~4 min read·updated 5/29/2026

45. Design an Ad Click / Counting System

Tests: extreme write throughput, deduplication, fraud detection, real-time aggregation, billing accuracy.

45.1 Requirements

Functional

  • Serve ads to users (auction in real-time).
  • Track impressions, clicks, conversions.
  • Bill advertisers based on outcomes.
  • Detect click fraud.
  • Real-time and batch reporting.

Non-functional

  • Massive scale: 100B+ events/day.
  • Sub-100ms ad serve latency.
  • Strong correctness for billing (over-count = lost trust; under-count = lost revenue).
  • Real-time reporting (~minutes lag).
  • Fraud-resistant.

45.2 Estimates

  • 100B events/day = ~1.2M events/sec average; peak 5M/sec.
  • Per event: 200 bytes = ~250 GB/sec at peak.
  • Storage per day after compression: 5-20 TB.
  • Ad serve QPS: less (depends on traffic), but still 100K-1M+/sec.

45.3 Architecture (high level)

[ User browser ]──→[ Ad server ]──→ ad creative + tracking pixel
                          │
[ Tracking endpoint ]──→ Kafka (impressions, clicks)
                          │
                  [ Stream processor (Flink) ]
                          │
                  ┌───────┼───────┬───────────────┐
                  ↓       ↓       ↓               ↓
            [ Real-time   [ Fraud  [ Hot       [ Long-term
              dashboards ]  filter] counters ]   warehouse (Druid/BQ) ]

[ Auction svc ] (real-time bidding)
[ Budget svc ]
[ Billing svc ]

45.4 Ad serving

When a user visits a publisher page with an ad slot:

  1. Browser → ad server with context (URL, user, device).
  2. Ad server runs an auction:
    • Identify eligible campaigns (targeting, budget remaining).
    • Each bidder bids (real-time bidding, RTB) or campaign has fixed price.
    • Highest CPM wins.
  3. Ad creative + tracking URL returned.
  4. Browser renders ad → fires impression pixel.

Latency budget: < 100 ms total. Auction itself < 50 ms.

45.5 Click & impression tracking

Tracking pixel = tiny image (or beacon API call):

  • Browser GET /track?event=impression&ad_id=X&...
  • Server: validate signature → enqueue event → return 1×1 pixel.

Endpoint must be:

  • Sub-10ms (fire and forget).
  • Handle massive QPS (CDN edge ideally).
  • Sign URLs to prevent forgery.

45.6 Stream processing

Kafka holds events. Flink (or Spark Streaming) consumes:

  • Dedup by event_id (Bloom filter + state).
  • Filter fraudulent clicks (rules + ML).
  • Aggregate per (campaign, hour) → counters.
  • Decrement budget.
  • Update real-time dashboards.

45.7 Counters

Counters are the heart. Multiple stores:

Real-time (last hour)

Redis sorted sets, Druid, ClickHouse. Sub-second queries.

Aggregated (daily)

Materialized in OLAP store (BigQuery, ClickHouse).

Source (event log)

Kafka retention + S3 / GCS archive. Replayable forever.

Sharded counters

Per campaign, multiple counter shards. Hot campaigns (high QPS) get more shards. Read = sum across shards.

45.8 Click fraud detection

Click fraud is endemic. Sources:

  • Bots clicking competitors' ads.
  • Click farms.
  • Self-click fraud by publishers.
  • Ad stuffing (multiple ads in 1×1 px).

Detection layers:

  • Rules: too many clicks from same IP, same user-agent, same time delta.
  • Behavioral: mouse movement, dwell time, click coordinates.
  • Network: known bad IPs (datacenter, proxy lists).
  • ML: anomaly detection on click patterns.
  • Post-hoc: re-check after clicks; reverse charges if fraud detected later.

45.9 Budget management

Each campaign has daily / total budget. Must stop serving when exhausted.

  • Real-time budget tracker: sliding sum of spend.
  • On every auction: check budget remaining; skip if exhausted.
  • Stale budget (eventually consistent) → over-spend; mitigation: budget pacing (rate limit spend per hour).

45.10 Billing accuracy

Two questions:

  1. Did the click happen? (impression / click recorded)
  2. Was it valid? (not fraud)

Process:

  • Real-time provisional billing (campaign metrics update).
  • Daily reconciliation:
    • Replay last 24h of events through fraud filter.
    • Confirm valid clicks.
    • Final invoice based on validated counts.
  • Disputed clicks → manual review.

45.11 Reporting

Two tiers:

Real-time (minutes lag)

For advertisers monitoring active campaigns. Druid / Pinot / ClickHouse over recent events.

Batch / final

For invoicing and analytics. BigQuery / Snowflake on historical events.

45.12 Schema example

events (
  event_id (UUID),  -- for dedup
  event_type (impression / click / conversion),
  ts,
  campaign_id, ad_id, creative_id,
  publisher_id, user_id (hashed), device, geo,
  url, referrer,
  ip, user_agent (hashed for privacy)
)

Stored as Avro/Protobuf in Kafka; Parquet/ORC in warehouse.

45.13 Pricing models

  • CPM (cost per mille): pay per 1000 impressions. Brand awareness.
  • CPC (cost per click): pay per click. Performance ads.
  • CPA / CPI (cost per action / install): pay only on conversion. Highest performance bar.
  • CPV (cost per view): video ads.

Aggregations differ per model; counter pipeline supports all.

45.14 Privacy

  • No raw PII in event log.
  • Hash IP, user IDs.
  • Honor Do Not Track / GPC.
  • Consent management (TCF, IAB).
  • Differential privacy in some aggregations.

Post-cookie world: contextual targeting, privacy sandbox APIs.

45.15 Failure modes

  • Ad server down: publisher page shows fallback (house ad / blank).
  • Tracking lost: under-count; reconcile via raw access logs.
  • Counter divergence: reconcile from event log (source of truth).
  • Fraud filter false positives: legit clicks discarded; advertiser support sees discrepancy.

45.16 Sketch for interview

[Browser]──→[Ad server]──→returns creative
                          │
                  [Auction svc] ←── eligible campaigns from [Campaign DB]
                  [Budget svc]
[Browser fires pixel]──→[Tracking endpoint]──→Kafka(events)
                                                  │
                                          [Flink stream proc]
                                                  │
                                  ┌───────────────┼───────────────┐
                                  ↓               ↓               ↓
                            [Druid: real-time]  [Fraud filter] [Counter store]
                                                                  ↓
                                                          [Budget update]
                                                                  ↓
                                                       [Reconciliation job]
                                                                  ↓
                                                          [Billing svc]
[BQ / Snowflake] for analytics

45.17 What an interviewer wants

  • Pixel-tracking + Kafka for ingest at scale.
  • Stream processor (Flink) for dedup, fraud, aggregation.
  • Sharded counters for hot campaigns.
  • Reconciliation: real-time provisional + batch final.
  • Awareness of click fraud and ML detection.

45.18 Common pitfalls

  • Synchronous DB writes per event → service melts.
  • No dedup → over-charging.
  • Single counter per campaign → hot key.
  • Over-trust real-time counters for billing → revenue loss to fraud.
  • No event log to replay → can't recover from bugs.

Key takeaways

  • Event log + stream processing handles the volume.
  • Sharded counters tolerate hot campaigns.
  • Real-time provisional billing + batch final reconciliation balances UX and accuracy.
  • Click fraud is endemic; layer rules + behavioral + ML.
  • Privacy + consent are not optional in 2026.

// 2 views

main
UTF-8·typescript