Monitoring System
Design an end-to-end monitoring and alerting platform with dashboards, thresholds, and on-call routing.
Problem Statement
Design a Monitoring System like Datadog, New Relic, or Azure Monitor. The platform receives telemetry from applications, hosts, containers, cloud services, and synthetic probes, then turns that telemetry into dashboards, alerts, SLOs, error budgets, traces, logs, and incident notifications.
At interview scale, assume a multi-tenant SaaS product with thousands of customer teams, hundreds of thousands of monitored resources, and large bursts during incidents. The core challenge is not pure metrics collection; this system consumes metrics, logs, traces, and health checks, correlates them, evaluates alert rules over time windows, suppresses noise, and routes actionable incidents to the right on-call owner.
The default design should optimize for reliable ingestion, bounded query latency, correct alert evaluation, durable incident history, and alert fatigue reduction. Optional advanced features such as anomaly detection, service maps, synthetic monitoring, and SLO burn-rate alerts should layer on top of the same telemetry and alerting foundations.
Business use case
Engineering and operations teams use monitoring platforms to detect outages before customers complain, understand regressions after deploys, and measure whether services are meeting reliability targets. A strong platform shortens mean time to detect and mean time to recover by bringing metrics, logs, traces, dashboards, and paging workflows into one place.
For businesses, observability protects revenue and trust. It helps teams enforce SLOs, prioritize reliability work using error budgets, reduce noisy alerts, and give executives a shared view of service health across products, regions, and customer tiers.
Functional Requirements
Ingest metrics from agents, OpenTelemetry collectors, application SDKs, and cloud integrations.
Ingest logs and traces, preserve correlation fields such as service, environment, host, trace ID, and request ID.
Store and query time-series data with tags, rollups, retention tiers, and dashboard-friendly aggregations.
Search logs and traces by time range, service, severity, labels, trace ID, and sampled request attributes.
Let users create dashboards with charts, service health panels, trace waterfalls, log widgets, and SLO widgets.
Evaluate alert rules over sliding windows for thresholds, missing data, anomaly signals, synthetic checks, and burn-rate conditions.
Deduplicate, group, silence, acknowledge, and resolve alerts to reduce alert fatigue and flapping.
Route notifications through escalation policies to email, Slack, Teams, webhooks, and PagerDuty-style on-call systems.
Non-Functional Requirements
Ingestion latency
Fresh telemetry should become queryable within 10 to 30 seconds for metrics and within 30 to 60 seconds for logs and traces. Alert evaluation should detect most threshold violations within one evaluation interval after the relevant window closes.
Alert availability
The alerting path must be more reliable than dashboards. During a partial outage, ingestion and alert evaluation for critical metrics should continue even if expensive log search, trace exploration, or non-critical dashboards are degraded.
Scalability and cardinality control
The system must handle millions of samples per second while protecting itself from unbounded tag cardinality. Tenant quotas, label allow lists, rollups, sampling, and backpressure are required so one customer or one bad deploy cannot exhaust shared storage.
Query performance
Common dashboard queries over recent rollups should complete in under 1 second p95. Wider exploratory queries over raw logs, high-cardinality metrics, or long trace windows can be slower but should have timeouts, result limits, and progressive loading.
Durability and retention
Telemetry can be lossy under extreme pressure, but alert state, rule definitions, SLO definitions, audit logs, and incident history must be durable. Metrics, logs, and traces need configurable retention tiers with replication and backups for customer commitments.
Correctness and idempotency
The ingestion path should tolerate retries without double-counting counters or creating duplicate log batches. Alert evaluation must use stable windows, handle late data explicitly, and avoid repeatedly paging the same incident unless escalation policy requires it.
Multi-tenancy and isolation
Tenants need strict data isolation, separate quotas, per-team RBAC, and noisy-neighbor protection. Query, ingestion, and alerting capacity should be partitioned enough that a large customer's incident does not delay smaller customers' critical alerts.
Capacity Estimation
Assumptions
Assume 100,000 monitored resources across hosts, containers, serverless functions, databases, and managed cloud services. Each resource emits 200 active metric series on average, sampled once every 10 seconds. Assume logs average 1 KB per second per resource after filtering, and traces are sampled to 500,000 spans per second across the fleet.
Assume 80 bytes per encoded metric sample before compression, 250 bytes per sampled trace span after basic attribute pruning, 30 days of log retention, 45 days of metric retention, 7 days of raw trace retention, 200,000 active alert rules, and a 10x burst multiplier during large incidents or regional outages.
Monitored resources
100,000 resources
Hosts, containers, services, databases, and synthetic checks
Active metric series
20M series
100,000 resources times 200 active series
Average metric ingest
2M samples per second
20M active series sampled once every 10 seconds
Peak metric ingest
20M samples per second
10x burst during incidents, autoscaling events, or bad deployments
Metric storage
155 TB compressed for 45 days
2M samples per second times 80 bytes, compressed about 4:1
Log ingest
100 MB per second
100,000 resources times 1 KB per second
Log storage
86 TB compressed for 30 days
8.6 TB raw per day compressed about 3:1
Trace ingest
500K spans per second
Sampled spans after head or tail sampling policies
Trace storage
76 TB compressed for 7 days
500K spans per second times 250 bytes for 7 days
Alert evaluations
3,300 evaluations per second
200,000 alert rules evaluated once per minute
Dashboard query traffic
5,000 peak QPS
Interactive dashboards, incident war rooms, and API consumers
Calculations
- Metric series: 100,000 monitored resources times 200 active series is 20M active series.
- Metric ingest: 20M series sampled once every 10 seconds is 2M samples per second on average. A 10x burst gives 20M samples per second.
- Metric storage: 2M samples per second times 80 bytes is 160 MB per second raw. That is about 13.8 TB per day raw. With 4:1 compression, store about 3.45 TB per day. Over 45 days, reserve about 155 TB before replicas and index overhead.
- Log ingest: 100,000 resources times 1 KB per second is 100 MB per second. That is about 8.6 TB per day raw. With 3:1 compression over 30 days, reserve about 86 TB before replicas and search index overhead.
- Trace ingest: 500,000 sampled spans per second times 250 bytes is 125 MB per second. That is about 10.8 TB per day. Over 7 days, reserve about 76 TB before replication.
- Alert evaluations: 200,000 active rules evaluated every 60 seconds means about 3,333 rule evaluations per second. The engine should evaluate from precomputed rollups rather than scanning raw samples.
- Notifications: if 1 percent of alert rules fire during a major outage, that is 2,000 initial alert events. Grouping by service, region, and severity should collapse this into far fewer incidents before paging humans.
API Design
/api/v1/telemetry/metricsReceives batches of metric samples from agents, collectors, SDKs, or cloud integrations. The gateway authenticates the tenant, validates label limits, and writes the accepted batch to the ingestion stream.
Request
{
"tenantId": "tenant_123",
"source": "otel-collector",
"samples": [
{
"metric": "http.server.duration.p95",
"timestamp": "2026-07-26T06:59:00Z",
"value": 183,
"tags": {
"service": "checkout",
"region": "us-east-1",
"environment": "prod"
}
}
]
}
Response
{
"accepted": 4800,
"rejected": 12,
"nextAllowedAt": "2026-07-26T06:59:10Z"
}
202— Batch accepted for asynchronous processing400— Malformed payload or invalid metric name401— Invalid ingestion token413— Batch too large429— Tenant ingestion quota exceeded
/api/v1/telemetry/logsAccepts structured log batches. Logs should already be redacted by agents where possible, but the server also applies schema validation, tenant quotas, and optional sampling.
Request
{
"tenantId": "tenant_123",
"logs": [
{
"timestamp": "2026-07-26T06:59:05Z",
"service": "checkout",
"severity": "ERROR",
"traceId": "tr_abc123",
"message": "Payment authorization timed out",
"attributes": {
"region": "us-east-1",
"deploy": "2026.07.26.4"
}
}
]
}
Response
{
"accepted": 1000,
"sampledOut": 200,
"redactedFields": ["card_number"]
}
202— Logs accepted400— Invalid log schema401— Invalid ingestion token429— Log ingestion quota exceeded
/api/v1/telemetry/tracesReceives trace spans in batches. The system preserves parent-child relationships, indexes trace IDs, and supports head or tail sampling depending on tenant policy.
Request
{
"tenantId": "tenant_123",
"spans": [
{
"traceId": "tr_abc123",
"spanId": "sp_1",
"parentSpanId": "sp_0",
"service": "checkout",
"operation": "POST /checkout",
"startTime": "2026-07-26T06:59:01Z",
"durationMs": 742,
"status": "error"
}
]
}
Response
{
"accepted": 520,
"sampleRate": 0.1
}
202— Spans accepted400— Invalid trace payload401— Invalid ingestion token429— Trace ingestion quota exceeded
/api/v1/queryRuns a dashboard or exploration query across time-series, logs, traces, or correlated telemetry. The query service enforces RBAC, tenant limits, timeouts, and maximum result sizes.
Request
{
"tenantId": "tenant_123",
"queryType": "timeseries",
"expression": "p95:http.server.duration{service=checkout,environment=prod}",
"from": "2026-07-26T06:00:00Z",
"to": "2026-07-26T07:00:00Z",
"resolutionSeconds": 60
}
Response
{
"series": [
{
"labels": {
"service": "checkout",
"environment": "prod"
},
"points": [
{ "timestamp": "2026-07-26T06:59:00Z", "value": 183 }
]
}
],
"partial": false
}
200— Query completed206— Partial result returned before timeout400— Invalid query expression403— Caller lacks access to the requested tenant or resource429— Query rate limit exceeded
/api/v1/alert-rulesCreates an alert rule using a time-series query, evaluation interval, window, threshold, grouping labels, no-data behavior, and routing policy.
Request
{
"tenantId": "tenant_123",
"name": "Checkout p95 latency too high",
"query": "p95:http.server.duration{service=checkout,environment=prod}",
"window": "5m",
"evaluationInterval": "1m",
"condition": {
"operator": ">",
"threshold": 500
},
"groupBy": ["service", "region"],
"silenceWindows": [],
"route": "payments-primary-oncall"
}
Response
{
"ruleId": "rule_456",
"state": "enabled",
"nextEvaluationAt": "2026-07-26T07:00:00Z"
}
201— Alert rule created400— Invalid rule or query403— Caller cannot create alert rules for this tenant409— Duplicate rule name in this scope
/api/v1/incidents/{incidentId}Acknowledges, resolves, reassigns, or silences an active incident. This endpoint changes alert state and escalation behavior, not historical telemetry.
Request
{
"action": "silence",
"durationMinutes": 60,
"reason": "Known deploy rollback in progress",
"actor": "user_789"
}
Response
{
"incidentId": "inc_789",
"state": "silenced",
"silencedUntil": "2026-07-26T08:00:00Z"
}
200— Incident state updated400— Invalid action403— Caller cannot modify this incident404— Incident not found409— State transition is no longer valid
Ingestion APIs are asynchronous because the gateway should validate, quota-check, and enqueue telemetry quickly. Query and configuration APIs are synchronous because users expect immediate feedback when saving dashboards, rules, silences, and SLO definitions.
The monitoring system should support OpenTelemetry, Prometheus remote write, syslog-style log forwarding, and cloud provider integrations at the edge, but the internal API should normalize all formats into a tenant-aware telemetry envelope.
Database Design
The system uses different storage engines for different access patterns. Metrics are optimized for time-range scans and rollups, logs are optimized for text and attribute search, traces are optimized for trace ID lookup and service graph analysis, and alert state is optimized for durable state transitions.
The relational-looking schema below describes logical entities. At production scale, the metric and event tables map to a distributed time-series store, columnar object storage, and search indexes rather than one monolithic relational database.
| series_id | uuid | Stable identifier derived from tenant, metric name, and normalized tag set |
| tenant_id | uuid | Tenant partition and authorization boundary |
| metric_name | varchar(255) | Canonical metric name after normalization |
| tag_hash | binary(16) | Hash of sorted tags for fast lookup and deduplication |
| tag_set | json | Bounded set of dimensions such as service, region, host, and environment |
| retention_tier | varchar(32) | Raw, hourly rollup, daily rollup, or premium long retention |
| first_seen_at | timestamp | First sample time |
| last_seen_at | timestamp | Most recent accepted sample time |
| tenant_id | uuid | Partition key prefix |
| series_id | uuid | Metric series identifier |
| bucket_start | timestamp | Start of the compressed time bucket |
| resolution_seconds | int | Raw or rollup resolution |
| encoded_samples | blob | Compressed samples using delta and Gorilla-style encoding |
| min_value | double | Precomputed minimum for faster aggregations |
| max_value | double | Precomputed maximum for faster aggregations |
| sum_value | double | Precomputed sum for rate and average calculations |
| sample_count | bigint | Number of samples in the bucket |
| tenant_id | uuid | Tenant partition |
| event_type | varchar(16) | Log or span |
| event_time | timestamp | Event timestamp |
| service | varchar(255) | Service name for filtering and correlation |
| severity | varchar(32) nullable | Log severity when event is a log |
| trace_id | varchar(64) nullable | Trace identifier for logs and spans |
| span_id | varchar(64) nullable | Span identifier when event is a trace span |
| attributes | json | Indexed bounded attributes |
| object_key | varchar(512) | Pointer to compressed raw payload in object storage |
| rule_id | uuid | Primary key |
| tenant_id | uuid | Tenant owner |
| name | varchar(255) | Human-readable rule name |
| query | text | Time-series or synthetic check expression |
| evaluation_interval_seconds | int | How often the rule evaluates |
| window_seconds | int | Lookback window for the condition |
| condition | json | Threshold, anomaly, no-data, or burn-rate condition |
| group_by | json | Labels used for alert grouping |
| route_id | uuid | Notification route or escalation policy |
| enabled | boolean | Whether the rule is active |
| incident_id | uuid | Primary key for an alert incident |
| tenant_id | uuid | Tenant boundary |
| rule_id | uuid | Alert rule that produced the incident |
| fingerprint | varchar(255) | Deduplication key derived from rule, tenant, and grouping labels |
| state | varchar(32) | Triggered, acknowledged, silenced, resolved, or suppressed |
| severity | varchar(32) | Page, ticket, warning, or info |
| started_at | timestamp | First time the condition became active |
| last_fired_at | timestamp | Most recent evaluation that matched |
| dedupe_count | bigint | Number of repeated firings collapsed into this incident |
| silenced_until | timestamp nullable | Silence expiry if active |
Indexes
- metric_series.tenant_id, metric_name, tag_hash supports series lookup during ingestion and query planning.
- metric_chunks.tenant_id, series_id, bucket_start is the primary access path for dashboard and alert window scans.
- telemetry_event_index.tenant_id, event_time, service supports recent log and trace exploration by service and time.
- telemetry_event_index.trace_id supports direct trace waterfall retrieval and log-to-trace correlation.
- alert_rules.tenant_id, enabled lets scheduler shards find active rules for a tenant.
- alert_incidents.tenant_id, fingerprint, state enforces deduplication of active incidents.
Relationships
Each tenant owns metric series, event indexes, alert rules, dashboards, notification routes, SLOs, and incidents. Alert rules reference metric queries or synthetic check streams and create alert incidents through a dedupe fingerprint. Logs and spans share correlation identifiers so a dashboard point can jump to related logs or a trace waterfall without joining on the ingestion hot path.
NoSQL alternatives
Metrics are usually stored in a specialized time-series database or columnar store such as M3, Cortex, Thanos, VictoriaMetrics, ClickHouse, Druid, Bigtable, or a custom LSM-backed TSDB. Logs fit object storage plus a search index or columnar lake. Traces fit a span store indexed by trace ID, service, operation, and time.
Rule definitions, silences, routes, SLO definitions, audit logs, and incident state can live in a strongly consistent relational database or a replicated key-value store because they are smaller but more correctness-sensitive than raw telemetry.
High-Level Architecture
Telemetry ingestion flows from monitored workloads through agents, gateways, queues, processors, and specialized stores. Dashboards query the stores, while alert evaluation reads rollups, updates durable alert state, and sends grouped incidents to notification routing.
The architecture separates the ingestion path, query path, and alerting path. Ingestion must absorb bursts, normalize formats, enforce tenant quotas, and fan telemetry into the right stores. Query APIs serve dashboards and investigations, but they should not sit on the critical alert paging path.
Metrics, logs, and traces have different storage needs. Metrics use a time-series store with compressed chunks and rollups. Logs and traces use object storage plus searchable indexes. Correlation comes from shared labels, trace IDs, service names, deploy versions, and timestamps rather than from forcing all telemetry into one database.
Alerting is a first-class subsystem. The alert engine reads precomputed rollups, evaluates rules on stable windows, deduplicates by fingerprint, respects silences and maintenance windows, and hands incidents to the notification router for escalation.
Request Flow
- 1
Telemetry is emitted by workloads
Applications, hosts, containers, managed services, and SDKs emit metrics, logs, and spans. Agents and OpenTelemetry collectors batch data locally, attach tenant and environment metadata, redact obvious secrets, and retry on transient network failures.
- 2
Synthetic checks create health signals
Regional probers run HTTP checks, TCP checks, browser flows, and API canaries against customer endpoints. Their results are converted into metrics and events so they can drive dashboards, alerts, and SLO calculations alongside application telemetry.
- 3
Ingestion gateway validates and queues
The gateway authenticates the ingestion token, checks tenant quotas, validates schema, rejects dangerous label explosions, and writes accepted batches to the telemetry bus. It returns 202 after durable enqueue rather than waiting for downstream indexing.
- 4
Stream processors normalize telemetry
Consumers parse formats, normalize units, compute derived fields, enforce sampling policies, drop or quarantine malformed payloads, and attach routing keys. They also compute metric rollups so dashboards and alert rules do not scan raw samples for common windows.
- 5
Metrics, logs, and traces land in specialized stores
Metric chunks are written to the time-series store by tenant, series, and time bucket. Logs and spans are compressed into object storage and indexed by time, service, severity, trace ID, and selected attributes for search and correlation.
- 6
Dashboards query recent rollups
An operator opens a dashboard. The query API authorizes access, plans queries against the right rollup resolution, fans out to metric and event stores when needed, and returns partial results if a large panel exceeds its timeout.
- 7
Alert rules evaluate on schedules
Scheduler shards assign active rules to alert workers. Each worker reads the relevant rollup window, applies threshold or burn-rate logic, handles no-data behavior, and writes a stable evaluation result with a rule and group fingerprint.
- 8
Alert state is deduplicated and grouped
The alert engine checks existing incidents, suppression rules, silences, maintenance windows, and flapping dampeners. Repeated firings update the same incident rather than paging every minute, while resolved evaluations close incidents after a recovery window.
- 9
Notifications route through escalation policies
Actionable incident events go to the notification router. It selects the on-call team, delivery channel, retry policy, escalation delay, and fallback contact, then sends pages, chat messages, tickets, or webhooks with links back to dashboards and traces.
Core Components
Agents and OpenTelemetry Collectors
Collect local telemetry and forward it reliably.
Agents scrape metrics, tail logs, receive spans, attach resource metadata, redact sensitive fields, batch payloads, and retry with backoff. They should buffer locally for short outages but shed low-priority telemetry when disk limits are reached.
Ingestion Gateway
Protects the platform from invalid, unauthenticated, or excessive telemetry.
The gateway verifies tokens, maps requests to tenants, enforces rate and cardinality quotas, validates schemas, applies backpressure, and durably appends accepted data to the telemetry bus. It keeps customer-facing ingestion stable even when processors scale up or recover.
Telemetry Bus
Decouples ingestion spikes from downstream processing.
Kafka, Kinesis, Pulsar, or Pub/Sub buffers metric, log, trace, and synthetic streams by tenant and telemetry type. Retention on the bus provides replay during processor failures and allows separate consumers for storage, alerting, anomaly detection, and billing.
Metrics Pipeline and Time-Series Store
Stores compressed samples and rollups for dashboards and alerts.
The metrics pipeline deduplicates retries, computes rates and percentiles where appropriate, down-samples old data, and stores compressed chunks. The store is partitioned by tenant, metric, series hash, and time to support efficient range scans.
Log and Trace Store
Supports search, trace waterfall retrieval, and cross-telemetry correlation.
Logs and spans are written to compressed object storage and indexed for recent search. Trace IDs provide direct lookup, while service, operation, severity, deployment, and request attributes support incident investigation.
Alert Evaluation Engine
Evaluates rules, SLO burn rates, missing data, and synthetic check failures.
Alert workers evaluate rules on fixed schedules using precomputed rollups, handle late data policies, compute incident fingerprints, suppress flapping, and update durable alert state. They must be deterministic enough that retries do not create duplicate pages.
Notification Router and Escalation Manager
Turns alert incidents into targeted human notifications.
The router applies routing rules, team ownership, severity policies, on-call schedules, retries, escalation chains, and channel preferences. It integrates with PagerDuty-style systems, Slack, Teams, email, SMS, tickets, and customer webhooks.
Dashboard and Query API
Provides interactive visualization and investigation workflows.
The API plans queries, chooses raw or rollup resolution, merges metrics with related logs and traces, enforces RBAC, caches common panels, and returns progressive or partial results for expensive investigations.
Deep Dive
Metrics, logs, and traces are complementary
A monitoring system should explain the three pillars clearly. Metrics are compact numerical time series that answer what changed and when. Logs are detailed event records that explain why a specific error happened. Traces connect work across services and show where latency or errors occurred in a distributed request.
The platform should not treat them as interchangeable. Metrics power alerting, dashboards, and SLO burn-rate calculations because they are cheap to aggregate. Logs support debugging and audit trails, but they are more expensive to index and query. Traces provide request-level causality, but sampling is usually required. Correlation fields such as trace ID, service, region, deploy version, customer tier, and timestamp let users move from an alert to the relevant logs and traces quickly.
Time-series ingestion, rollups, and cardinality
The time-series store is optimized around tenant, metric name, tag set, and time bucket. Compression works well when samples for the same series arrive in order and have similar timestamps or values. Rollups at 1 minute, 5 minutes, 1 hour, and 1 day resolutions keep long-range dashboards affordable.
The biggest operational risk is cardinality explosion. A label such as request_id, user_id, session_id, or raw URL can create millions of unique series and make both storage and queries fail. The ingestion gateway and processors should enforce label allow lists, drop unbounded labels, limit unique series per tenant, and surface cardinality reports so customers can fix instrumentation.
Alert rule evaluation over time-series windows
Alert rules should evaluate on stable, bounded windows instead of querying arbitrary raw data every second. A threshold rule might evaluate the last 5 minutes every 1 minute. A burn-rate rule might evaluate both 5-minute and 1-hour windows to catch fast outages without paging on tiny blips.
Rule workers should shard by tenant and rule ID, read rollup chunks, apply no-data semantics, and write an idempotent evaluation result. For counters, the engine must handle resets. For percentiles, it should use histogram summaries or sketches rather than averaging p95 values incorrectly. Late data should be either ignored after a grace period or handled through explicit re-evaluation policies.
Deduplication, grouping, silencing, and escalation
Paging one person for every matching time series is a common failure mode. The system should compute an incident fingerprint from tenant, rule, service, environment, region, and chosen group labels. Repeated firings update the same incident with dedupe_count and last_fired_at rather than creating a new page every minute.
Grouping collapses related alerts into one incident view. Silences and maintenance windows suppress known work. Escalation policies decide when to page primary on-call, secondary on-call, managers, or a customer webhook. Recovery windows prevent flapping by requiring the condition to remain healthy for several evaluations before resolving.
SLOs, SLIs, and error budgets
An SLI is a measured reliability signal such as successful requests divided by total requests, p99 latency under a threshold, or synthetic checkout success rate. An SLO is the target for that SLI over a window, such as 99.9 percent success over 30 days. The error budget is the allowed badness left before the team violates the SLO.
The monitoring system should compute SLO windows from metrics and synthetic checks, show remaining budget, and alert on burn rate instead of only absolute error count. Multi-window burn-rate alerts reduce noise by paging when the service is burning budget both quickly and consistently.
Logs, traces, sampling, and cost control
Logs and traces can dwarf metric volume. The system should encourage structured logs, field redaction, per-tenant quotas, dynamic sampling, and retention tiers. High-value error traces can be kept at a higher rate than routine successful traces.
Tail sampling can keep traces with errors, high latency, or rare routes after seeing the full request. Head sampling is cheaper and simpler but may drop the one trace needed during an incident. A premium design lets teams configure sampling by service, route, status, tenant tier, and incident mode.
Scaling
Prototype: one region and managed stores
Start with OpenTelemetry collectors, a managed queue, a managed time-series database, a search service for logs, and a relational database for alert rules and incidents. Evaluate rules every minute and send notifications through one provider. This proves the product workflow without building every storage engine.
Growth: partition by tenant and telemetry type
Split ingestion topics by tenant tier and telemetry type. Add per-tenant quotas, metric rollups, query caching, log retention tiers, and alert scheduler shards. Keep dashboards and alerting separate so expensive investigations do not delay paging.
Large scale: custom TSDB and distributed alert engine
Move high-volume metrics into a horizontally sharded time-series store. Partition by tenant, metric, series hash, and time. Run alert workers as a distributed scheduler with leases, idempotent evaluations, and durable alert state. Use object storage for older logs and traces.
Multi-tenant SaaS scale: isolation and cost controls
Introduce dedicated ingestion lanes for enterprise tenants, quota-aware query schedulers, cardinality analysis, storage tiering, sampled traces, and customer-visible usage reports. Add regional data residency options and stronger RBAC for regulated customers.
Global scale: regional ingestion and replicated control plane
Ingest telemetry in the nearest region, keep alerting local for low latency, and replicate rule definitions, silences, dashboards, and incidents through a control plane. Global dashboards can aggregate rollups asynchronously while regional alerts continue during cross-region failures.
Bottlenecks & Optimizations
High-cardinality metric labels
Reject or down-rank labels such as request ID, user ID, session ID, and raw URL before they create unbounded series. Enforce tenant cardinality quotas, provide label usage reports, and move high-cardinality debugging data into logs or traces instead of metrics.
Alert evaluation scans too much raw data
Evaluate rules from precomputed rollups and histogram sketches. Shard rules by tenant and rule ID, cache query plans, limit lookback windows, and precompute SLO burn rates for popular services.
Incident storms and alert fatigue
Group alerts by service, region, and severity; deduplicate by fingerprint; enforce recovery windows; add maintenance silences; and route warnings to tickets or chat while reserving pages for user-impacting symptoms.
Log and trace storage explosion
Use structured logging, indexing allow lists, compression, object storage, sampling, retention tiers, and dynamic incident-mode sampling. Do not index every arbitrary field by default.
Expensive dashboard queries
Use rollups, query result caching, panel-level timeouts, progressive loading, and resolution selection based on time range. Limit ad hoc high-cardinality group-bys and surface partial results instead of blocking the whole dashboard.
Hot tenants during major incidents
Isolate ingestion and query capacity by tenant tier, apply fair scheduling, reserve alerting capacity, and allow non-critical telemetry to be sampled or delayed while critical SLI metrics continue to flow.
Failure Handling
Agent or collector cannot reach the service
Agents buffer locally within disk limits, retry with exponential backoff, and expose self-monitoring metrics. When buffers fill, drop low-priority logs or traces before dropping critical health metrics, and mark gaps clearly in dashboards.
Telemetry bus lag grows
Autoscale consumers, shed non-critical telemetry, increase sampling, and prioritize metric and alert-relevant streams over bulk logs. Alert operators on ingestion freshness so teams know when dashboards may be stale.
Time-series store shard is unavailable
Route reads to replicas when possible, serve partial dashboard results with clear warnings, and keep alert rules from paging on missing data unless the rule explicitly treats no data as bad. Replay queued samples after recovery.
Alert engine workers fail
Use scheduler leases so another worker can claim rules after a timeout. Store evaluation checkpoints and incident fingerprints durably so retries are idempotent and do not create duplicate notifications.
Notification provider is down
Retry with backoff, fail over to secondary channels, escalate through alternate providers, and show delivery status on incidents. The alert state should remain active even if a downstream paging integration is temporarily unavailable.
Bad alert rule causes a storm
Apply per-rule and per-route notification rate limits, automatic grouping, circuit breakers, and emergency tenant-wide silences. Keep audit logs so teams can identify who changed the rule and roll it back.
Regional outage affects ingestion
Agents should fail over to another regional ingestion endpoint when policy allows. Control-plane data such as rules and silences should replicate across regions, while regional alert engines continue evaluating the telemetry they can still receive.
Security
Tenant isolation
Every sample, log, span, rule, dashboard, and incident must carry a tenant boundary. Enforce tenant IDs in storage partition keys, authorization checks, query planning, cache keys, and notification routing.
Ingestion authentication
Agents and collectors should use scoped ingestion tokens or mutual TLS. Rotate tokens, bind them to tenants and environments, and reject telemetry that attempts to spoof another tenant or reserved system labels.
PII and secret protection
Logs and spans often contain customer data, tokens, emails, or payment fragments. Redact at the agent when possible, apply server-side detectors, support field-level retention policies, and prevent sensitive fields from being indexed broadly.
RBAC and auditability
Limit who can view production telemetry, modify alert routes, silence incidents, or change SLO definitions. Record audit events for rule edits, silence creation, escalation changes, dashboard sharing, and token rotation.
Encryption and retention controls
Encrypt telemetry in transit and at rest. Support customer-defined retention, legal hold where required, deletion workflows, and data residency controls for regulated tenants.
Abuse and cost controls
Protect public ingestion endpoints from token abuse, replay storms, and deliberate cardinality attacks. Apply quotas, anomaly detection on ingestion patterns, and emergency disablement of compromised tokens.
Tradeoffs
Pros
- +Separating metrics, logs, and traces lets each storage engine match its access pattern.
- +Asynchronous ingestion through a queue absorbs bursts and protects downstream stores.
- +Rollups make dashboards and alert evaluation predictable at scale.
- +Durable alert state enables deduplication, grouping, silencing, and escalation without duplicate pages.
- +SLO and burn-rate alerts focus teams on user impact rather than raw symptom noise.
Cons
- −Multiple storage systems increase operational complexity and consistency challenges.
- −Sampling and retention policies can drop the exact log or trace needed for a rare incident.
- −Strict cardinality limits can surprise customers if instrumentation is not explained well.
- −Alert grouping reduces noise but can hide distinct root causes if fingerprints are too broad.
- −Global multi-region ingestion and data residency requirements complicate rule replication and query planning.
Alternatives
Alternative one is a metrics-only monitoring system. It is simpler and cheaper, and it may be enough for infrastructure health, but it cannot provide full incident investigation because logs, traces, SLO context, and notification workflows are missing.
Alternative two is a log-first observability system that stores all telemetry as events in a search engine. It gives flexible debugging, but metric alerting and long-range dashboards become expensive unless rollups and specialized time-series indexes are added.
Alternative three is to rely on separate best-of-breed tools for metrics, logs, tracing, paging, and dashboards. This reduces platform scope but weakens correlation, ownership, consistent RBAC, and alert fatigue controls.
When not to use this design
Do not build a full Datadog-style platform for a small application with a few services and modest reliability needs. Managed monitoring, cloud provider metrics, and a simple paging integration are usually enough until telemetry volume, cross-service debugging, compliance, or alert fatigue justify the complexity.
Follow-up Questions
How is this different from a metrics collection system?
A metrics collection system focuses on scraping, receiving, storing, and querying numerical time series. A monitoring system consumes those metrics and combines them with logs, traces, health checks, dashboards, alert evaluation, deduplication, silencing, notification routing, SLOs, and incident workflows.
How do you reduce alert fatigue?
Alert on symptoms and SLO burn rates instead of every internal cause. Group related firings, deduplicate by fingerprint, add recovery windows, route warnings to tickets, silence maintenance, suppress known dependencies, and review noisy alerts with ownership and metrics.
What happens when telemetry arrives late?
Define a grace period per telemetry type. Dashboards can backfill late samples, but alert evaluation should use stable windows and avoid re-paging old incidents unless policy requires it. Late data can update history and SLO reports without rewriting already-delivered notifications.
How should high-cardinality labels be handled?
Block or drop unbounded labels at ingestion, enforce tenant series quotas, keep allow lists for indexed dimensions, and guide users toward logs or traces for per-request identifiers. Also provide cardinality dashboards so teams can fix instrumentation before it becomes an outage.
How do you design SLO burn-rate alerts?
Compute an SLI such as good requests divided by total requests, compare it with the SLO target, and calculate how quickly the error budget is being consumed. Use multi-window alerts, for example a short window plus a longer window, to page on sustained user impact while filtering transient spikes.
How do you ensure alert evaluation is reliable?
Shard rules deterministically, use leases, write idempotent evaluation results, read from rollups, persist incident fingerprints, and monitor the monitoring system itself. Critical rule evaluation should have reserved capacity and should not depend on expensive dashboard queries.
How do logs and traces connect to a metric alert?
Use shared labels and correlation IDs. A metric alert includes tenant, service, environment, region, deploy version, and time window. The UI uses those fields to pre-filter logs and traces, then trace IDs link individual logs to request waterfalls.
Company Variations
Amazon
Amazon interviewers may emphasize CloudWatch-like scale, multi-tenant quotas, DynamoDB or S3-backed storage, operational alarms, cost controls, and keeping alerting alive during regional failures. Be ready to explain partition keys and noisy-neighbor isolation.
Microsoft
Microsoft may frame this around Azure Monitor, enterprise tenants, RBAC, audit logs, data residency, Teams integration, and hybrid cloud agents. Discuss control-plane durability, customer-managed retention, and strict tenant isolation.
Netflix
Netflix often cares about large-scale service health, streaming reliability, adaptive alerting, chaos testing, and reducing pages from cascading failures. Emphasize SLOs, regional dashboards, trace sampling, and alert grouping by customer-impacting symptoms.
Google may push on SRE concepts: SLIs, SLOs, error budgets, burn-rate alerts, Borg-style fleet scale, and the difference between symptoms and causes. Be precise about multi-window alerting and avoiding pages for non-actionable noise.
LinkedIn may tie the design to large microservice graphs, member-facing reliability, Kafka-based pipelines, and on-call ownership across many teams. Discuss service dependency mapping, tenant-like product isolation, and incident collaboration workflows.
Interview Tips
Start by saying this is an observability and alerting platform, not just a metric scraper. Draw telemetry ingestion first, then split into metric storage, log and trace storage, query APIs, alert evaluation, alert state, and notification routing.
When discussing tradeoffs, keep tying decisions back to human outcomes: fast detection, useful context, fewer noisy pages, and reliable escalation. Interviewers reward candidates who understand that the hardest part of monitoring is turning huge telemetry volume into a small number of actionable incidents.
What interviewers expect
- ✓Clearly separate collection, ingestion, storage, query, alerting, and notification routing.
- ✓Explain why metrics, logs, and traces use different storage and retention strategies.
- ✓Use rollups and stable evaluation windows for alert rules.
- ✓Discuss SLOs, SLIs, error budgets, and burn-rate alerts.
- ✓Show how deduplication, grouping, and silencing reduce alert fatigue.
- ✓Cover failures of the monitoring system itself, especially alerting and notifications.
Common mistakes
- !Designing only metric scraping and forgetting logs, traces, dashboards, SLOs, deduplication, and paging.
- !Letting alert rules scan raw telemetry instead of using bounded windows and rollups.
- !Ignoring cardinality limits until one label creates millions of series.
- !Paging on every low-level cause instead of user-impacting symptoms and burn rates.
- !Treating notification delivery as fire-and-forget without retries, escalation, and delivery state.
- !Assuming logs, metrics, and traces belong in one generic database.
Red flags
- ×No concrete capacity math for samples per second, storage per day, or rule evaluations.
- ×No distinction between ingestion freshness, query latency, and alert detection latency.
- ×No plan for alert grouping, silencing, maintenance windows, or flapping control.
- ×No tenant isolation or quota model for a multi-tenant observability platform.
- ×No answer for high-cardinality labels, trace sampling, or log retention cost.
Revision Notes
- A full monitoring system ties together metrics, logs, traces, synthetic checks, dashboards, alerting, notification routing, SLOs, and incident history.
- It consumes metrics collection rather than stopping there. The domain crux is alert evaluation, correlation, deduplication, silencing, escalation, and reducing alert fatigue.
- Metrics are compact time series for alerting and dashboards. Logs provide detailed event context. Traces show distributed request causality.
- Use asynchronous ingestion through a durable telemetry bus so agents and gateways are decoupled from storage and indexing.
- Use a time-series store with compressed chunks, rollups, and retention tiers for metrics. Use object storage plus search indexes for logs and traces.
- Control metric cardinality aggressively. Unbounded labels such as user IDs, request IDs, raw URLs, and session IDs can break ingestion and queries.
- Alert rules should evaluate from rollups over stable windows. Avoid raw scans in the paging path.
- Deduplicate by incident fingerprint, group related firings, respect silences, and use recovery windows to avoid flapping.
- Notification routing needs escalation policies, retries, delivery tracking, and integrations with PagerDuty-style systems, chat, email, SMS, tickets, and webhooks.
- SLOs use SLIs and error budgets. Burn-rate alerts page when the service is consuming budget too quickly and consistently.
- The monitoring system must monitor itself: ingestion lag, alert evaluation lag, dropped telemetry, notification failures, and query latency are all first-class signals.
Flashcards
Quiz
0/7 answered
1.What is the best description of this monitoring system's scope?
2.Why should alert rules usually evaluate over rollups instead of raw samples?
3.Which label is most likely to cause high-cardinality problems if used on every metric?
4.What does alert deduplication primarily prevent?
5.What is a good default strategy for logs and traces at high volume?
6.What is the purpose of SLO burn-rate alerting?
7.During a notification provider outage, what should the monitoring system do?
Cheat Sheet
Goal: build an observability and alerting platform that connects metrics, logs, traces, synthetic checks, dashboards, SLOs, incidents, and notification escalation.
Workload: with 100,000 monitored resources and 200 active metric series each, expect about 20M active series and 2M metric samples per second. Logs at 1 KB per second per resource are about 100 MB per second. Sampled traces at 500K spans per second are another high-volume stream.
Ingestion: agents and collectors batch telemetry, gateways authenticate and enforce quotas, and a durable telemetry bus decouples ingestion from processing.
Storage: metrics go to a time-series store with compressed chunks and rollups. Logs and traces go to object storage plus search indexes. Alert rules, silences, routes, SLOs, and incidents go to a durable control-plane store.
Query: dashboards should use rollups, caching, timeouts, and progressive loading. Correlate telemetry using service, environment, region, deploy, timestamp, trace ID, and request ID.
Alerting: evaluate rules on stable windows from rollups. Handle no-data semantics, counter resets, late data, and idempotent retries.
Noise control: deduplicate by fingerprint, group related firings, silence maintenance, use recovery windows, and route only actionable severity to human pages.
SLOs: define SLIs, SLO targets, and error budgets. Use multi-window burn-rate alerts to page on sustained user impact.
Failures: monitor ingestion lag, processor lag, alert evaluation lag, dropped telemetry, query latency, notification delivery, and the monitoring system's own health.
Security: enforce tenant isolation, scoped ingestion tokens, RBAC, audit logs, encryption, retention controls, redaction, and cost protection against cardinality attacks.
References
- BookSite Reliability Engineering — Google
- DocsOpenTelemetry Documentation — OpenTelemetry
- DocsPrometheus Documentation — Prometheus
- PaperThe Tail at Scale — Jeffrey Dean and Luiz Andre Barroso
- BookDesigning Data-Intensive Applications — Martin Kleppmann