// Register a click
POST /api/v1/clicks
{
impressionId,
userId, // optional
sourceIP,
userAgent,
...
}
// Ad campaign management API is omitted
// Get ad clicks
GET /api/v1/clicks?adIds=[]&window={}&startTime={}&pageSize={}&pageToken={}
// Analytics
GET /api/v1/clicks?query={}
Capacity estimation
Aggregation table size
On the ingestion path, when an ad is clicked, the APIGateway sends data to an IngestionService, which validates the request and forward to IngestionQueue. From there, the PersistenceService takes the messages and store them in blob storage, partitioned by time. This is our raw data.
The Real time aggregation service (Flink or Spark Streaming) performs aggregation, and stores the aggregation result in Aggregation Database. When new results are available, it pushes the result onto notification queue. The NotificationService picks it up, and updates VendorClient via SSE.
The BatchProcess service periodically queries raw data, redo the aggregation, and generate higher level aggregations for analytics (e.g. per hour, per day/week/month). It will also be able to process data that came outside the watermark.
On the query path, vendor client might query for pre-aggregated data, in which case QueryService can simple serve from AggregationDatabase and/or Aggregation Cache. If the query is complicated and needs to perform adhoc aggregation, it can leverage the same aggregation service used in real-time path, although the priority will be lower and latency more.
Data schema
Merchant table
Campaign table
Impression table
Click table
Real-time aggregation
Real-time updates to the client
Deduping clicks
Traffic spikes
Reliability, scalability