SaaS & data, an interactive architecture walkthrough

Time-series database: how the architecture works

How a time-series database appends samples behind a write-ahead log, compresses them into immutable blocks, and answers range queries by label.

This is a learning explainer based on public engineering writing and general design practice. It is simplified, it is not the actual architecture of any company, and it may be inaccurate.

25 components, 22 connections, 12 steps. Updated 4 November 2026.

Interactive diagram

Overview

A time-series database stores numeric samples, each a timestamp and a value belonging to a series identified by a name and labels, and answers queries over time ranges. Writes are nearly always appends of the newest point, and reads mostly cover the recent past, so the storage is organised by time: recent samples live in memory behind a write-ahead log, and older ones are cut into immutable blocks.

Compression does most of the work. Timestamps are stored as delta-of-delta and values as XOR with the previous value, so a sample often takes one or two bytes. An inverted index maps each label pair to series ids so queries can find series without scanning. Series are hashed across shards and replicated three ways, older blocks move to object storage, and downsampled rollups serve long ranges. This is a simplified learning design, not any specific database's implementation.

Trade-offs and what changes at scale

The design is fast for appends and time-range reads but poor at updating or deleting individual points, which are handled through tombstones and compaction. Memory grows with the number of active series, not the number of samples, so high-cardinality labels are the usual cause of outages and are capped at ingest. Downsampling keeps long-range queries cheap but loses detail such as short spikes. At larger scale shards are rebalanced as series grow, and queries that span many shards are split by time and cached.

How it works, step by step

The walkthrough above, as text. Each step lights up the components named under it.

  1. Metric Agents send batches of samples, each a series identified by labels plus a timestamp and a value, to the Write Gateway. Writes are almost all appends of the newest point, which is what the storage format is built around.

    Components Write Gateway, Metric AgentsVia remote write

  2. The Distributor hashes each series' labels to a shard and asks the Cardinality Limiter before accepting a series it has not seen. One bad label, such as a user id, can create millions of series and exhaust memory on every shard.

    Components Distributor, Cardinality LimiterVia batches

  3. Samples are appended to the Write-ahead Log on three replicas, and the write succeeds once a quorum has them. A crashed node replays its log to rebuild what was in memory.

    Components Write-ahead Log, Storage Replica 2, Storage Replica 3Via append

  4. The Head Block keeps the last couple of hours of every active series in memory, grouped per series. Most queries ask about the recent past, so they are answered without touching disk.

    Components Head BlockVia apply

  5. A new series gets an id, and the Series Index records it under each label pair, such as service="api" and status="500". Queries intersect these posting lists to find matching series without scanning them all.

    Components Series IndexVia new series id

  6. The Chunk Encoder stores timestamps as the change in the gap between points and each value XORed with the previous one. Regular scrape intervals and slowly changing values make most of these differences zero or tiny, so a sample takes one to two bytes instead of sixteen.

    Components Chunk EncoderVia encode chunk

  7. Every two hours the head is cut into an immutable block with its own chunks and index in Local Blocks. Immutable files are simple to cache, copy and delete as a whole.

    Components Local BlocksVia cut block

  8. The Compactor merges small blocks into larger ones and applies deletions, and the Retention Worker removes blocks older than the retention period. Deleting whole blocks is far cheaper than deleting individual samples.

    Components Compactor, Retention WorkerVia merge

  9. Older blocks are uploaded to the Object Store, and the Downsampler writes 5-minute and 1-hour rollups of them. A one-year graph then reads a few thousand points per series instead of millions.

    Components Object Store, DownsamplerVia upload

  10. A dashboard range query goes to the Query Engine, which resolves label matchers through the Series Index and reuses cached results for ranges it has already computed. Aligning ranges to the query step is what makes those cached results reusable.

    Components Query Engine, Series Index, Query CacheVia range query

  11. The engine reads the newest samples from the Head Block, older ones from local blocks and long ranges from object storage, choosing downsampled data when the range is wide. It then aggregates across shards, for example summing request rates per service.

    Components Head Block, Local Blocks, Object StoreVia recent samples

  12. Observability watches active series per shard, ingest rate and disk use. Active series count, more than sample rate, decides how much memory each node needs.

    Components Observability, DistributorVia active series

Sequence diagram

The same walkthrough as a UML sequence diagram, one lane per layer or group (19 components in all), left to right in the order a request reaches them, numbered by step. A loop on a lane is a step that stays inside that group. Solid arrows are synchronous calls, dashed are asynchronous.

Time-series database: sequence diagram of the walkthroughClientsIngestStorage Node(one shard)ReplicasLong-term TierQueryObservabilityremote write1batches2check new series2append3replicate3apply4new series id5encode chunk6cut block7merge8delete old8upload9rollups9range query10match labels10cached ranges10recent samples11older samples11long ranges11active series12ingest metrics12

Key components

Clientslayer

  • Dashboards: range queries over series
  • Metric Agents: send samples: series labels, time, value

Ingestdomain

  • Cardinality Limiter: caps new series per tenant
  • Distributor: hashes series to shards, replicates
  • Write Gateway: auth, per-tenant rate limits

Replicaslayer

  • Storage Replica 2: second copy of the shard
  • Storage Replica 3: third copy of the shard

Storage Node (one shard)boundary

each shard owns a subset of series

  • Chunk Encoder: delta-of-delta time, XOR values
  • Compactor: merges blocks, applies deletes
  • Head Block: last ~2 hours of samples in memory
  • Local Blocks: immutable 2-hour blocks on disk
  • Retention Worker: drops blocks past retention
  • Series Index: label -> series ids (inverted index)
  • Write-ahead Log: every sample before it is acknowledged

Long-term Tierdomain

  • Downsampler: 5-minute and 1-hour rollups
  • Object Store: older blocks (s3)

Querydomain

  • Query Cache: results by step-aligned range (redis)
  • Query Engine: plans, fans out, aggregates

Observability

  • Observability: active series, ingest rate, disk

Sources

Public writing this explainer draws on. Read these for what the real systems actually do.

Make it yours

Copy this diagram and its walkthrough into your own workspace, then change anything: swap Postgres for DynamoDB, add a cache, rewrite the narration. Free, and it uses no AI credits.

This is a learning explainer based on public engineering writing and general design practice. It is simplified, it is not the actual architecture of any company, and it may be inaccurate. Real systems differ in the details. Browse the library.