AWS Real-Time Dashboards: Druid, ClickHouse, or Lambda?
CK 3788 Created at Updated at
62000
Advertisers and publishers demand live dashboards showing metrics updated within minutes: impressions, clicks, conversions, spend, CPM. Real-time reporting requires an OLAP (Online Analytical Processing) engine that ingests high-volume event streams, aggregates them on the fly, and serves slice-and-dice queries to dashboards. The challenge is balancing freshness (sub-minute latency), cardinality (thousands of dimensions per event), and query flexibility (arbitrary drill-down) while keeping costs manageable.
Three approaches dominate on AWS: Druid (columnar OLAP purpose-built for high-cardinality, roll-up aggregation, deployed on EC2 ASG or EKS), ClickHouse (raw-event columnar store with Kafka native support and powerful SQL, deployed on EC2 ASG), and Lambda architecture (Kinesis micro-batch into pre-aggregated cubes with Pinot or Aurora/Redshift). Druid excels at metrics slicing (impressions by date, country, campaign, audience, device); ClickHouse at raw-event queries; Lambda at hybrid (real-time summary + archived detail). This case examines the three, their operational trade-offs, and guidance on schema design, Kinesis ingestion, and GDPR compliance.
Key Requirements
Functional Requirements
Metric freshness: Dashboard metrics updated within 1–5 minutes of event occurrence.
Arbitrary drill-down: slice metrics by any combination of dimensions (date, country, campaign, audience, creative, device, OS, browser).
Sub-second query latency: p99 query response <2 seconds even at 100M events/day scale.
Cardinality support: handle 1000+ distinct values per dimension (e.g., 50,000 unique campaigns + 200 countries + 100 audiences = high cardinality).
Schema evolution: add new dimensions without downtime or full re-index.
Audit: who accessed which reports; track metric changes.
Simplified Architecture
Architecture Proposals
Option 1: Druid (Time-Series OLAP with Roll-Up, EC2 ASG)
Druid is a columnar OLAP datastore designed for metrics aggregation. Events flow into Kinesis Data Streams (or Apache Kafka on Amazon MSK); Druid real-time ingestion tasks (deployed on EC2 ASG or EKS) consume from Kinesis and aggregate events into immutable segments (e.g., 1-minute segments). Historical segments are stored in S3 deep storage. Query engine (Druid broker) slices aggregated metrics by dimensions without touching raw events. Druid's roll-up feature pre-aggregates (COUNT, SUM, AVG) at ingestion time, drastically reducing storage.
Pros:
Roll-up efficiency: pre-aggregate COUNT, SUM at ingestion; 100M raw events collapse to 10M rolled-up rows (90% compression).
Fast queries: querying aggregates (already summed) much faster than raw-event scan.
High cardinality: designed for 1000+ dimensions; query planner optimizes to avoid full table scan.
Mature ecosystem: widely used in ad-tech (Airbnb, Twitter); native integrations with Kafka, S3.
AWS Kinesis native: Kinesis Data Streams integration via custom firehose consumer; automatic scaling.
Approximate for percentiles: COUNT aggregation exact; MEDIAN/p95 not native (requires approximation or HyperLogLog).
Cardinality limits: if dimension has 10M unique values (e.g., user IDs), roll-up cardinality explodes; storage not practical.
Schema change friction: adding new dimension requires new segment version; re-ingestion for backfill.
Operational complexity: managing segment indexing, handoff from real-time to historical tier, balancing across EC2 ASG nodes.
Failure Modes & Mitigations:
Roll-up key missing: query asks for metric not included in roll-up config. Mitigate: pre-define all likely metrics; publish roll-up schema to team; CI/CD validation.
Cardinality explosion: dimension has 50M unique values; roll-up becomes huge. Mitigate: exclude high-cardinality dimensions from roll-up; store raw or use approximate count distinct (HyperLogLog).
Schema evolution: new dimension added; backfill required. Mitigate: design schema with 20 custom dimensions reserved for future use; store as JSON.
S3 slowness: historical segment queries to S3 timeout. Mitigate: cache frequently-accessed segments in local SSD via EC2 instance store; S3 Select for pushdown filtering.
When to Choose:
Metrics pre-aggregated (SUM impressions by date/country/campaign) is primary use case.
Cardinality moderate (1,000–100,000 combinations of dimensions).
Team familiar with Druid operations (Apache ecosystem maturity, EKS experience).
ClickHouse stores raw events as-is in a columnar format, making it ideal for flexible analytics. Kafka table engine (or Kinesis-via-Lambda) allows direct ingestion; MergeTree table engine automatic sorting and compression. Queries execute against raw data; no pre-aggregation required. Cost per event higher than Druid, but schema changes trivial (add column, resume ingestion). Deployed on EC2 ASG with i4i NVMe instances for fast local storage; replicated across AZs via MergeTree replication.
Pros:
Raw flexibility: store raw events; query any column combination without pre-aggregation.
Schema evolution: add column to table, resume ingestion; no re-indexing.
Query timeout: scanning 100M rows for single-day query takes >2 seconds. Mitigate: date-based partitioning (scan only relevant date); pre-materialized views for common aggregates; Prewhere optimization.
Schema evolution mismatch: new event format breaks old queries. Mitigate: add new columns as nullable; handle null in application query.
Storage growth: 500 GB raw events fill disk. Mitigate: TTL policies (delete 90+ day data); archive to cold S3 via Tiered storage.
Need to answer ad-hoc questions (pivot arbitrary dimensions).
Team comfortable managing large datasets; has ClickHouse expertise.
Can tolerate higher cost per event for flexibility.
Option 3: Lambda + Cube Pre-Aggregation (Kinesis Micro-Batch to Aurora/Redshift)
Kinesis Data Streams routes events to two paths: real-time micro-batch Lambda (every 1–2 minutes) pre-aggregates into a "metrics cube" (Aurora PostgreSQL or Redshift materialized view), and raw events archived to S3 (Parquet via Kinesis Firehose). Query layer chooses: fresh metric from cube (p50 <100ms latency), or detailed drill-down from S3 (higher latency, but fully flexible). Cube updated every micro-batch cycle; raw archive updated hourly or daily via Glue jobs.
Pros:
Hybrid best-of-both: real-time dashboard from cube (fast), detailed analysis from S3 (flexible).
Flexible schema: raw S3 data stores all events; add custom dimensions without concern.
Decouples latency: dashboard refresh independent from drill-down data availability.
Easy GDPR compliance: delete user from cube (re-aggregate next cycle) + delete from S3 (Parquet rewrite via Glue).
Serverless (Lambda): no EC2/Kubernetes management; auto-scales with Kinesis shard count.
Cons:
Operational complexity: managing two data paths (cube + S3), two query engines (fast + flexible).
Eventual consistency: cube updates every 1–2 minutes; during cycle, stale data.
Cardinality constraints: cube still has dimension limits; if too many dimensions, cube explodes.
Data duplication: raw events in Kinesis, cube, S3; increases storage 2–3x.
Query routing logic: application must decide: use cube or S3? adds latency to decision; client-side logic or API gateway routing.
Failure Modes & Mitigations:
Cube aggregate lag: micro-batch Lambda fails; metrics stale for 2+ minutes. Mitigate: retry logic via Lambda DLQ; fall back to previous cycle's data with "as-of" indicator.
S3 query too slow: drill-down query on 100M+ parquet rows takes 10+ seconds. Mitigate: partition S3 by date/campaign; Athena projection-based partitioning; pre-cache common drill-downs in ElastiCache.
Cube + S3 divergence: metrics in cube don't match detail in S3 (e.g., due to replay/retransmit). Mitigate: daily reconciliation Lambda; alert on mismatches >0.1%; automated recompute on divergence.
When to Choose:
Need fast dashboard + flexible drill-down simultaneously.
Budget allows dual storage (cube + S3).
Can manage two query engines (Aurora SQL + Athena/Presto).
GDPR compliance simple (separate user records from metrics).
Prefer serverless (Lambda) over managing EC2/EKS clusters.
Comparison Table
Dimension
Druid (EC2 ASG)
ClickHouse (EC2 ASG)
Lambda + Cube
Query Latency (p50)
100 ms
300–500 ms
100 ms (cube), 5s (S3)
Query Latency (p99)
500 ms
1–2 sec
200 ms (cube), 20s (S3)
Storage per Event
~1 byte (rolled-up)
~50 bytes (raw)
~1 byte (cube) + ~40 bytes (S3)
Schema Flexibility
Low (fixed roll-ups)
High (raw events)
High (raw S3)
Aggregation Cardinality
100K–1M combos
No limit (scan)
100K–500K (cube)
Operational Complexity
Medium (EC2 ASG, coordinator)
Medium-High (ZooKeeper, merges)
High (Lambda + 2 engines)
Cost per Event
$0.0001
$0.0005
$0.0003 (hybrid)
Ad-Hoc Query Support
Low (pre-defined)
High
Medium (cube or slow S3)
Real-Time Freshness
1–5 min
5–30 sec
1–2 min (cube)
AWS Service Integration
Kinesis, S3, EC2 ASG
Kinesis, EC2 ASG, ZooKeeper
Lambda, Kinesis, Aurora, S3, Athena
Recommendation
For advertiser dashboards (performance metrics): Use Lambda + Cube architecture (Option 3). Cube (Aurora or Redshift) serves hot metrics (impressions, clicks, spend by date/campaign/country) updated every minute; S3 cold tier stores raw events for detailed user-level drill-down via Athena. Advertisers get <100ms dashboard refresh and can still debug campaign performance ad-hoc. Serverless Lambda scales automatically; no EC2 management.
For publisher ad-ops dashboards: Use Druid (Option 1) if reporting is stable (standard metrics, fixed dimensions). Roll-up ensures sub-100ms query latency and low storage cost. Deploy on EKS for simplified operations; Kinesis auto-scales shards. Invest in upfront schema design; changes are painful but rare.
For product analytics / data science: Use ClickHouse (Option 2) if iterative analysis common. Raw-event storage (500 GB for 100M daily events) acceptable cost for schema flexibility. Team can afford slower queries (p50 500 ms) for one-off exploration. Materialized views can accelerate common aggregations.