Your country

Tools that support it use your country for local currency, number formats, units and paper size. Your choice is saved only in this browser.

Type a name or a two-letter code. Use the up and down arrow keys to move through the countries, Enter to choose one and Escape to close.

System Design (High-Level Design)  Module 20 – Building blocks: control, safety and platform services

Design a metrics and alerting system

Design a metrics and alerting system from a series estimate to scraping, compressed time-series storage, rollups, cardinality limits and alerts.

  • Advanced
  • 30 minutes
  • Examples run with Python 3.14.8, Pyodide 314.0.7, Node.js 24.21.0 and quickjs 0.32.0
  • By MySmartCoPilot
Free intro, Pro: 5 of 30 minutes free
Free: Requirements and estimate: series, not bytes, set the size. With a pass: the other 16 sections and 3 interview answers, about 25 minutes.

What you will learn

  • Estimate active series, samples a second, storage and ingest memory from a fleet's targets
  • Choose between pull and push collection, and set limits per scrape and per tenant
  • Encode timestamps with delta-of-delta, and decode a compressed block
  • Plan retention with rollups so that queries over months stay fast and cheap
  • Design alert rules and notifiers that group alerts, drop duplicates and report their own failure

Before you start

What you’ll learn · Before you start

What you will learn

  • Estimate active series, samples a second, storage and ingest memory from a fleet's targets
  • Choose between pull and push collection, and set limits per scrape and per tenant
  • Encode timestamps with delta-of-delta, and decode a compressed block
  • Plan retention with rollups so that queries over months stay fast and cheap
  • Design alert rules and notifiers that group alerts, drop duplicates and report their own failure

Before you start

On this page

A metrics and alerting system collects the numbers services expose, such as request counts, error ratios and queue lengths, stores each as a time series and pages a person when a rule over them stays true too long. The fleet in this lesson exposes 1.61 million series, each sampled every 15 seconds; to design a metrics and alerting system for it, size it by series, not by traffic. Scrapers collect the samples, a sharded store keeps recent series in memory and older blocks in object storage, compression cuts each sample to one or two bytes, and rule evaluators send alerts to notifiers that group and deduplicate them.

Requirements and estimate: series, not bytes, set the size

A shopping and payments company runs 40 services on 2,000 pods and 250 nodes. Each team runs its own scraper with 15 days of data, nobody can query across clusters, and an alert once stayed silent through an outage because the scraper evaluating it had died. Before you design a metrics and alerting system for it, ask:

  • How many targets, how many series each, and how often are they scraped?
  • How far back must queries reach, and at what resolution?
  • How many dashboards and rules query it, and how fresh must they be?
  • Who may add labels, and what stops one team from flooding the store?
  • What must still work when part of the system fails, above all the alerting?

The answers here: 2,250 targets with about 1.6 million active series, scraped every 15 seconds; 13 months of history; about 400 rules evaluated every 30 seconds; teams add their own metrics; and an alert must reach a person even when a scraper, an ingester or a notifier is down.

Functional requirements: pull from services and accept pushes from short jobs; store labelled series; query ranges with functions; precompute aggregates; route alerts with grouping, silences and inhibition. Non-functional requirements: queries over the last hour in under a second; no samples lost when one storage node fails; one tenant’s label mistake never slows another; and an alerting path that reports its own failure.

The first estimate:

Series, samples, storage and ingest memory for the fleet Python · plan/metrics_plan.py
# A first estimate for the metrics system of this lesson. Every input is an assumption to replace with
# measurements: series per target from one scrape of each kind of target, and bytes per sample from the store's
# own statistics after a day of real data. The sizing rules for the ingest tier are Grafana Mimir's published
# capacity-planning minimums; other systems publish their own.
PODS = 2_000
SERIES_PER_POD = 660          # 10 routes x 3 status classes x 17 histogram series, plus 150 runtime series
NODES = 250
SERIES_PER_NODE = 1_000       # each node's CPU, memory, disk and network series
CLUSTER_SERIES = 40_000       # one cluster-wide exporter describing pods and deployments
SCRAPE_SECONDS = 15
BYTES_PER_SAMPLE = 1.5        # compressed; Prometheus documents an average of 1 to 2 bytes
REPLICATION = 3               # copies of every recent series in the ingest tier
GB_PER_300K = 2.5             # ingester memory for every 300,000 series held in memory
CORES_PER_300K = 1
SAMPLES_PER_DISTRIBUTOR_CORE = 25_000
HEADROOM = 1.5                # 50 % extra for peaks
LONG_DAYS = 395               # 13 months kept in object storage, at full resolution

series = PODS * SERIES_PER_POD + NODES * SERIES_PER_NODE + CLUSTER_SERIES
samples = series / SCRAPE_SECONDS
day = samples * 86_400 * BYTES_PER_SAMPLE
in_memory = series * REPLICATION

print(f"active series: {series:,}")
print(f"samples: {samples:,.0f} a second, {samples * 86_400 / 1e9:.2f} billion a day")
print(f"compressed samples: {day / 1e9:.1f} GB a day; {LONG_DAYS} days = {day * LONG_DAYS / 1e12:.1f} TB")
print(f"ingesters: {in_memory:,} series in memory with {REPLICATION} copies -> "
      f"{in_memory / 300_000 * GB_PER_300K * HEADROOM:,.0f} GB of memory and "
      f"{in_memory / 300_000 * CORES_PER_300K * HEADROOM:.0f} cores, with headroom")
print(f"distributors: {samples / SAMPLES_PER_DISTRIBUTOR_CORE * HEADROOM:.1f} cores for {samples:,.0f} samples a second")
histogram = PODS * 10 * 3 * 17
print(f"a label with 100 values added to the request histogram: {histogram:,} series become {histogram * 100:,}")

Output

active series: 1,610,000
samples: 107,333 a second, 9.27 billion a day
compressed samples: 13.9 GB a day; 395 days = 5.5 TB
ingesters: 4,830,000 series in memory with 3 copies -> 60 GB of memory and 24 cores, with headroom
distributors: 6.4 cores for 107,333 samples a second
a label with 100 values added to the request histogram: 1,020,000 series become 102,000,000

Recorded with Python 3.14.8 on macOS 26 arm64. To run it yourself: mise exec python@3.14.8 -- python3 metrics_plan.py

Bytes are the small number: 13.9 GB of samples a day is 5.5 TB over 13 months. Series are the large one: 1.61 million A time series that is still receiving samples, so the store keeps it in memory. Such a store is sized by its active series rather than by bytes, because each series costs memory however small its samples are., held three times in memory, need about 60 GB of ingest memory with headroom, and one label with 100 values on the request histogram would multiply its series a hundredfold.

Checkpoint

Check the estimate

  1. Question 1 of 3 A service has 300 pods, each exposing 800 series, scraped every 30 seconds. How many samples a second does it send?

    Type a number.

    A hint

    Every series gives one sample per scrape.

    Show the answer: Question 1 of 3

    Answer: 8000 samples a second (anything from 7200 to 8800 counts)

    300 × 800 = 240,000 series, each giving one sample every 30 seconds: 240,000 ÷ 30 = 8,000 samples a second.

  2. Question 2 of 3 At 1.5 bytes a compressed sample, about how many GB a day is that?

    Type a number.

    A hint

    A day has 86,400 seconds.

    Show the answer: Question 2 of 3

    Answer: 1.04 GB (anything from 0.94 to 1.14 counts)

    8,000 × 86,400 seconds = 691 million samples a day, × 1.5 bytes = 1.04 GB.

  3. Question 3 of 3 The ingest tier keeps 3 copies of every series and needs 2.5 GB of memory for every 300,000 series it holds. How many GB do 400,000 active series need, before headroom?

    Type a number.

    Show the answer: Question 3 of 3

    Answer: 10 GB (anything from 9 to 11 counts)

    3 copies make 1,200,000 series in memory, and 1,200,000 ÷ 300,000 × 2.5 GB = 10 GB. The series, not the bytes, set the size.

Scrapers pull targets and remote-write to distributors and ingesters; blocks go to object storage; rules alert through a notifier cluster.Targets: 2,000 pods and 250 nodeseach exposes /metricsScrapers in each cluster, a pair for redundancyfind targets by service discovery, pull every 15 s,enforce per-target limitsIngest tierObject storage2-hour blocks, compacted;5-minute and 1-hour rollupsQueriers and dashboardsrecent data from ingesters, the rest from blocksRule evaluatorsrecording and alerting rules, every 30 sNotifier cluster of 3 peersgroup, deduplicate, silence, routePager, chat, emailand an outside dead man's switchDistributorsvalidate, apply per-tenant limits,shard by seriesIngestersrecent series in memory,3 copies, a write-ahead logpullremote writeupload every 2 hoursqueriesalerts, to every peer

The first design of the metrics and alerting system

Text description of the diagram

The diagram shows the first design from top to bottom.

  • At the top are the targets: 2,000 pods and 250 nodes, each exposing its metrics on a /metrics endpoint.
  • Scrapers in each cluster, run as a pair for redundancy, find the targets through service discovery, pull them every 15 seconds and enforce limits per target.
  • The scrapers remote-write every sample to the ingest tier. Its distributors validate the samples, apply limits per tenant and shard series across the ingesters; the ingesters keep recent series in memory in three copies, protected by a write-ahead log.
  • Every two hours the ingesters upload blocks to object storage, where they are compacted and rolled up to 5-minute and 1-hour resolution.
  • Queriers serve dashboards, reading recent data from the ingesters and older data from the blocks.
  • Rule evaluators run recording and alerting rules every 30 seconds through the queriers and send alerts to every peer of a notifier cluster of three.
  • The notifiers group, deduplicate, silence and route the alerts to a pager, chat and email, and an outside dead man's switch expects an alert that always fires.

Follow one sample through it, from a pod to a page:

Interactive · stage by stage One sample through the first design Step through one sample of checkout's request counter, from the pod that exposes it to the person an alert pages.
One sample through the first design, step 8 of 8. Showing Checkout pod, Scraper pair, Distributors, Ingesters (in memory, three times), Object storage, Queriers, Rule evaluators, Notifier peers. In focus: Notifier peers.recent dataCheckout podexposes /metricsScraper pairpulls every 15 sDistributorslimits, then shardIngestersin memory, three timesObject storage2-hour blocksQueriersdashboardsRule evaluatorsevery 30 sNotifier peersgroup, deduplicate

The drawing shows the whole scene; its 8 steps are listed below.

Every step as text
  1. A counter goes up. A checkout pod counts every request it serves and exposes the counter, with labels for route and status, on its /metrics endpoint, as each of the 2,000 pods and 250 nodes does.
  2. Pulled on a schedule. One of its cluster's two scrapers found the pod through service discovery, and every 15 seconds it pulls the endpoint and takes one sample of every series the pod exposes.
  3. Written centrally. The scraper remote-writes the samples to the distributors, which check the tenant's limits and pick the ingesters that own each series.
  4. Held in memory. Three ingesters keep the series in memory, each behind a write-ahead log, so the loss of one machine loses no sample.
  5. Kept for 13 months. Every two hours the ingesters cut a block and upload it to object storage, where it is compacted and rolled up to 5-minute and 1-hour resolution.
  6. Read by dashboards. A dashboard's query goes to the queriers, which read the last hours from the ingesters and anything older from the blocks.
  7. A rule turns true. Every 30 seconds the rule evaluators run their rules through the queriers. When checkout's error ratio passes its limit for long enough, they send the alert to every notifier peer.
  8. One page for one problem. The notifier peers group related alerts into one message, drop the copies, and page the person on call; an outside dead man's switch expects an alert that always fires, so silence pages someone too.

Open choices, one sentence each:

  • Pull: scrapers fetch each target’s metrics on a schedule, finding targets by service discovery.
  • Push: short jobs and agents send samples to a receiver instead of waiting to be scraped.
  • Forwarding every sample a scraper collects to a central store, which keeps the samples longer than the scraper could and answers queries across clusters.: a scraper forwards every sample to a central store that keeps it longer.
  • Delta-of-delta and XOR encoding: each timestamp and value stored by how little it differs from the previous one.
  • Rollups: old samples rewritten as 5-minute and 1-hour aggregates so that long queries stay fast.
  • The number of distinct series a metric has: one for every combination of its label values. A new label with 100 values multiplies the metric's series by 100. limits: caps on samples per scrape and series per tenant that contain a label explosion.
  • For-durations: a condition must hold for a set time before its alert fires.
  • Grouping and deduplication: the notifier merges related alerts into one message and drops copies.
  • A dead man’s switch: an alert that always fires, so an outside service notices when alerts stop.

The deep dives that decide whether the design holds:

  1. How do samples get from 2,250 targets into the store, and what happens when one target sends a million series?
  2. How does a sample fit in one or two bytes, and what makes some series compress badly?
  3. How are series sharded and replicated, and what does losing one storage node cost?
  4. How do queries over 13 months stay fast without keeping every raw sample?
  5. How does a rule become one message, without blips, duplicates or silence?
  6. Who watches the alerting system, and how does anyone learn that it is down?

Answers are judged on the estimate, collection and limits, storage and compression, sharding and replication, retention and rollups, alert evaluation and notification, and watching the watcher.

Each sample takes five steps and lands on three ingesters Pro: opens with a Pro, Premium or Ultimate pass

In this part:

  • Pull notices dead targets, push suits short jobs
  • A label explosion, and how each limit fails when it trips
  • Delta-of-delta and XOR make most samples one or two bytes
  • Three copies of every series, and what losing one ingester costs
  • Rollups and retention per resolution make 13 months cheap
  • Alerts wait out a for-duration before they fire
  • Notifiers group and deduplicate, and the alerting path watches itself
  • Failure drills: a dead ingester, a renamed metric and a split notifier
  • Bottlenecks at 10× and 100×
  • Alternatives rejected
  • What each level is expected to cover
  • A sample answer and a 45-minute self-mock
  • Practise: decode a compressed block
    • Decode delta-of-delta timestamps from a compressed block
  • Interview questions
    • A deploy adds a customer ID label to a request counter, and the metrics store's memory starts climbing by gigabytes a minute. What do you do now, and what would have stopped it before it reached the store?
    • Checkout was down for 20 minutes and no alert fired, although the rule exists and looks correct. List the ways an alerting path can stay silent, and design it so that each of them pages someone.
    • Product wants 13 months of every service's metrics, queryable from dashboards in under two seconds, on a budget. How do you store and serve that, and what do you give up?
  • Key takeaways

6 diagrams · 5 runnable examples (JavaScript, Python) · 1 exercise with sample tests · 3 interview answers · about 64 minutes

Opening this part with your pass…

This part opens with any MySmartCoPilot pass.

  • Pro pass · 1 week₹79$2.99
  • 30 days₹179$5.99
  • 1 year₹1,699$45

One payment; a pass never renews. The price shown is the total you pay: no fees or charges are added at checkout.

It also opens every other in-depth part in Learn and every Pro tool. Sales are final, except where the law gives you a right to withdraw, for example in the EU and the UK (refund policy).

Read a complete sample first: Rate-limiting algorithms compared

References

Related tools

Report a problem with this lesson

Quick answers and tool search

Type to search tools or to get a quick answer, for example 18% of 2500. Use the up and down arrow keys to move through the results, Enter to choose, and Escape to close.