Free preview

Storage: Time-Series, Blob, and Rules

In one line: "store the metrics" hides three genuinely different workloads with three different access patterns. Separating them is what keeps the hot path fast and the retention bill affordable.

The three stores

StoreHoldsWhy separate
Time-series database (TSDB)Metrics, stored locally on the monitoring serverFast write and read operations — this is the hot path
Blob storageMetric data for long-term retentionA separate storage node; cheap capacity, slow access, unbounded size
Rules databaseAlert configurations and their corresponding actionsSmall, rarely written, read by the alert manager — a different workload entirely

The rules database

Worth dwelling on, because it is the component that turns a passive data store into a system that does something.

For example, if CPU usage exceeds 90%, the system must trigger an alert to the administrator. This requires a rules database to store rules and their corresponding actions.

So a rule is a pair:

condition                     ->  action
CPU usage > 90% for 5 min     ->  page the on-call engineer
Disk free < 10%               ->  open a ticket
Error rate > 1% for 2 min     ->  post to the incident Slack channel

The hot/cold split

The TSDB is described as local to the monitoring server and the blob store as a separate node. That is the same head/tail split CDNs used, applied to time rather than popularity:

Time-series databaseBlob storage
Data ageRecent — minutes to daysHistorical — weeks to years
Query frequencyConstant — dashboards, alertsRare — post-incident review, capacity planning
Latency requirementLow — alerts depend on itTolerant
Cost per byteHighLow
ResolutionFullOften downsampled

Retention is not optional — it's a design requirement

Storing high-resolution data indefinitely is resource-intensive. The system requires retention policies to periodically delete or downsample old data.

The arithmetic is unforgiving. Ten metrics per second per server, across 100,000 servers, is a million samples per second — billions per day, forever, growing with the fleet. No storage budget survives that at full resolution.

Downsampling is the usual answer: keep per-second data for a day, per-minute for a month, per-hour for a year. You lose the ability to see a two-second spike from last March, which is almost always an acceptable trade — and stating the tiers explicitly is what makes it a decision rather than an oversight.

Cardinality, and how to actually stop it

A TSDB stores each unique combination of metric name and labels as its own series. Add a label with unbounded values — a user ID, a request ID, a full URL path — and you create a new series per distinct value.

This is cardinality explosion, and it is the most common way a monitoring system falls over — taking out the infrastructure it was watching, at the moment that infrastructure was already in trouble.

Two properties make it nastier than an ordinary capacity problem. It grows multiplicatively: adding one label with a hundred values multiplies the series count rather than adding to it. And it is usually introduced by someone else — an application team adding a label they consider obviously useful, with no visibility into what it costs the platform.

The usual advice — keep labels low-cardinality, status code yes and user ID no — is correct and insufficient, because a convention is not an enforcement. A design answer needs a mechanism:

Two pieces of state make that work. A policy store mapping each metric name to its permitted label keys and a maximum series count — which can live alongside the alert rules, since it is the same kind of small, read-heavy, admin-edited configuration. And a fast series tracker, typically a set per metric in a key-value store, answering "have I seen this combination before?" on the ingest path.

The trade is real: a membership check per data point adds latency to the hottest path in the system. Batching the checks, or putting a local Bloom filter in front as a first pass, is the usual mitigation — a negative from a Bloom filter is definitive, so only possible hits need the round trip.

And the judgement worth stating: caps too strict drop useful data; caps too loose do not prevent the problem. Which is why the counter matters as much as the cap — dropping silently is how you discover during an incident that the metric you need has been rejected for a month.

High-cardinality identifiers still have a home; it is just not here. A request ID belongs in a log or a trace, where the storage model expects one record per event rather than one series per distinct value.

Key takeaway

Three stores, three workloads: fast and recent, cheap and historical, small and configurable. The retention policy between the first two is a first-class design decision, not an operational afterthought. And cardinality is the failure mode that actually kills these systems — it grows multiplicatively and is usually introduced by another team, so "keep labels low-cardinality" has to be enforced rather than advised: an allowlist of label keys, a per-metric series cap, and a dropped-series counter that itself alerts, because dropping silently is how you find out during an incident.

Interview signal by level

LevelWhat a strong answer sounds like
L4"Store the metrics in a database."
L5Splits the tiers: "a TSDB for recent data, blob storage for long-term retention, and somewhere to keep alert rules."
Staff+Prices retention and names the trap: "per-second data forever is unaffordable at fleet scale, so I'd downsample by age — seconds for a day, minutes for a month, hours for a year. And I'd cap label cardinality, because unbounded labels are the standard way a monitoring system kills itself."

Next: the decision that shapes the whole architecture.

Enjoying the preview?

Create a free account to unlock the rest of this course, the in-browser judge, and live AI mock interviews.

Sign up free to continue