Home › Blog › Metrics Monitoring System

Design a Metrics Monitoring and Alerting System

A Datadog- and Prometheus-style walkthrough: how metrics are collected, stored, rolled up, queried and turned into alerts, plus the trade-offs interviewers probe.

A metrics monitoring and alerting system collects numeric time series (CPU, request rates, latencies, error counts) from thousands of hosts, stores them in a compressed time-series database, rolls older data up into coarser resolutions, and continuously evaluates alert rules that page the right on-call engineer. In an interview, strong answers focus on three things: a write path that absorbs hundreds of thousands of samples per second, storage that fits because of compression, and an alerting pipeline that sends one useful page instead of two hundred duplicates.

Specific numbers come from Prometheus, Datadog and Thanos documentation and Facebook's Gorilla paper; where a number is our own assumption, it is labeled as one.

1. Clarify the requirements and estimate the scale

Pin down scope first. A reasonable set to propose:

Say explicitly that logs and traces are out of scope. Then name the non-functional requirements that drive every later decision:

Back-of-the-envelope numbers

Our assumptions, for illustration only: 10,000 hosts, 500 active series per host, and a 15-second collection interval.

QuantityCalculationResult
Active series10,000 × 5005 million
Samples per second5M ÷ 15 s~333,000
Samples per day333,000 × 86,400~28.8 billion
Raw size per day16 bytes per sample (8-byte timestamp + 8-byte float)~460 GB
Compressed size per day1–2 bytes per sample~29–58 GB

The takeaway to say out loud: compression keeps storage reasonable, and the number of active series, not raw disk, tends to be the first real limit, because every active series carries index and memory overhead. Prometheus's storage docs put compressed samples at about 1–2 bytes each and size disk as retention seconds × samples per second × bytes per sample.

2. Collection: push vs pull

This is the first real fork, and interviewers want the trade-off, not a verdict.

Pull (Prometheus-style)Push (agent-style, e.g. DogStatsD)
How it worksServer scrapes each target's HTTP metrics endpoint on an interval (Prometheus's global default is 1 minute)Apps send to a local agent; DogStatsD listens on UDP port 8125 by default, aggregates and forwards
Health signalBuilt in: a failed scrape shows up in the up metricDead host or quiet host? Needs heartbeats
Short-lived jobsCan finish before the next scrapeNatural fit
Firewalls and NATServer must reach every targetTargets only need outbound access
Load controlServer sets the paceClients can flood ingestion; needs rate limits

Two details that show depth:

A strong answer is often a hybrid: an agent on each host collects local metrics, pre-aggregates, sends its own heartbeat, and pushes compressed batches to a central ingestion tier.

3. Ingestion and time-series storage

The write path

  1. Ingestion gateway. Stateless, behind a load balancer; authenticates the tenant, validates names and labels, and enforces per-tenant rate and series limits.
  2. Buffer. A partitioned log such as Kafka absorbs bursts (a deploy restarting every pod at once), decouples ingestion from storage, and allows replay after a crash.
  3. Partition by series. Hash the metric name plus sorted label set into a series ID and shard on it, so every sample of a series lands on the same storage node. Compression depends on consecutive samples sitting together.
  4. Storage writers consume their partitions and append to the time-series database, replicated to two or three nodes.

Why a time-series database

The data is append-only, rarely updated, and read as range scans over a few series. A row-per-sample relational table wastes space on row overhead and index maintenance. Time-series databases group samples by series and by time block instead:

On replication, Gorilla wrote every series to two regions without guaranteeing consistency between them, accepting small discrepancies for availability. That fits here: metrics tolerate a lost point far better than a stalled write path. If the interviewer pushes on quorum and repair, the reasoning mirrors a distributed key-value store.

4. Downsampling and retention

A one-year chart over raw 15-second data would scan millions of points per series. Downsampling (rollup) stores coarser summaries of older data. An example plan:

TierResolutionKept for (example)Used for
Raw10–15 s1–2 weeksLive debugging, alert evaluation
Medium5 minutesSeveral monthsWeekly and monthly dashboards
Coarse1 hourA year or moreCapacity planning, long-term trends

For reference, plain Prometheus keeps 15 days when no retention flag is set, and the Thanos compactor downsamples raw data older than 40 hours to 5-minute resolution, then 5-minute data older than 10 days to 1-hour resolution. Real retention periods vary widely by team and budget.

Rollups can run as a batch job over closed blocks or as a streaming job over the ingestion log; the windowing trade-offs match the ad click aggregator walkthrough.

5. The query layer

6. Alerting pipeline and deduplication

Treat alerting as its own pipeline with four stages.

  1. Rule evaluation. An evaluator runs each rule on a fixed interval; Prometheus's global default is 1 minute. Example: 5xx error ratio for checkout above 5% for 10 minutes.
  2. State machine. A breach puts the alert in pending; it becomes firing only if the condition holds for the whole for duration, which filters blips. Prometheus's keep_firing_for keeps an alert firing for a set time after the condition was last met, which helps prevent flapping and false resolutions caused by missing data.
  3. Alert manager. A separate service deduplicates, groups, inhibits and silences firing alerts, then routes each group to a receiver. Prometheus's Alertmanager is the standard reference.
  4. Delivery. Send to email, chat and paging integrations with retries and idempotency keys, essentially a small notification system.

How deduplication works

Monitor the monitor. If the alerting pipeline dies, nothing pages, and silence looks exactly like health. Add a heartbeat alert that always fires and route it to an independent checker that pages when the heartbeat stops. Also alert on missing data, not only on bad values.

7. Cardinality limits

Cardinality is the number of unique time series. Prometheus's naming guide puts it directly: every unique combination of key-value label pairs is a new time series. Datadog counts custom metrics the same way; its example is a metric with an endpoint tag taking 2 values and a status tag taking 2 values, which counts as 4.

The multiplication is what hurts. A request counter with 1,000 endpoints, 5 status classes and 200 pods can reach 1 million series in the worst case. Add a user_id label with 100,000 users and the ceiling becomes 100 billion. Controls worth naming:

8. Trade-offs to say out loud

DecisionOption AOption BWhat to say
CollectionPullPush via agentPull for long-lived, discoverable services; push for batch jobs and firewalled hosts
ReplicationStrong consistencyBest-effort to 2+ replicasMetrics tolerate a lost point; favor write availability
Alert evaluationQuery the store on an intervalEvaluate on the streamInterval queries are simpler; streaming detects faster but complicates windowing
CardinalityAccept everythingHard limitsLimits protect a shared cluster; allow per-tenant exceptions

A reasonable pacing for a 45-minute round: about 5 minutes on requirements and numbers, 10 on collection and ingestion, 10 on storage and rollups, 10 on alerting, and the rest on cardinality and the interviewer's follow-ups. Rehearse it out loud at least once; explaining delta-of-delta encoding under time pressure is harder than it looks. A free AI mock interview in CoPilot Interview lets you include system design in the question mix, answer out loud, and get a score on each answer with what worked and what to improve.

Rehearse this design out loud

Paste a job description and your résumé, add system design to the question mix, and the AI interviewer reads each question aloud and scores your answer. The session report lists likely next-round questions and practice drills, and the free plan gives you 15 minutes a day with no credit card.

Try a free AI mock interview

FAQ

Should a metrics monitoring system use push or pull?

Either is defensible. Pull, as in Prometheus, gives you a built-in health signal and lets the server control load, while push through a local agent, as with DogStatsD, suits short-lived jobs and hosts behind firewalls. Many designs combine them with a per-host agent that collects locally and pushes batches upstream.

Why not store metrics in a relational database?

Metrics are append-only, arrive at hundreds of thousands of samples per second, and are read as range scans over a few series. A time-series database groups samples by series and compresses them to roughly 1-2 bytes each, which a row-per-sample table cannot match.

What is cardinality in metrics monitoring?

Cardinality is the number of unique time series, where each unique combination of metric name and label values counts as one series. Unbounded labels such as user IDs or request IDs multiply it quickly, so many production systems enforce series limits at ingestion.

How does an alerting system avoid sending duplicate pages?

Each alert is identified by its label set, so identical alerts from redundant rule evaluators collapse into one. Grouping, inhibition and silences then cut related noise, and a repeat interval throttles re-sending an unchanged notification.

Does downsampling reduce storage costs?

Only if raw data is deleted sooner than the rollups. The Thanos documentation notes that keeping every resolution adds blocks rather than saving space; the main benefit is fast queries over long time ranges.