Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Topic 30 — Time-Series Engines

Metrics workloads are the most regular data any database sees — and TSDBs are what you get when every design decision exploits that regularity: append-mostly, time-ordered, compress-by-predicting, partition-by-time, delete-by-dropping-partitions.

0. The shape of the problem

  write path                              read path
  ~1M samples/s, tiny (ts, f64) points    "cpu > 90 for job=api over 6h"
  99.9% arrive in time order              always a TIME RANGE
  per-series arrival ~10s apart           always a LABEL SELECTOR
                                          usually an AGGREGATION
        │                                        │
        ▼                                        ▼
  amortize:  batch into per-series       index labels, not values:
  compressed chunks (Gorilla),           inverted index (name,value) -> series,
  flush to immutable time-blocks         then min/max-prune time blocks

Baseline measured here (see notes.md): the obvious codec — delta+varint timestamps, raw f64 values — lands at 11.00 B/sample regardless of value shape, because the 8-byte values dominate. The entire point of Gorilla’s XOR trick is attacking those 8 bytes.

1. The design space

graph TD
    G["Gorilla (VLDB '15)<br/>in-memory cache: dod timestamps<br/>+ XOR floats = 1.37 B/sample"]
    P["Prometheus TSDB<br/>Gorilla chunks + head/WAL +<br/>2h immutable blocks +<br/>MemPostings label index"]
    VM["VictoriaMetrics<br/>same shapes, LSM-ier:<br/>nearest-delta2 + partitions +<br/>tagFilters cache"]
    IOX["InfluxDB 3 / IOx<br/>TSDB rebuilt on topic 28:<br/>WAL -> Arrow buffer -> Parquet<br/>on object storage, DataFusion SQL"]
    M["Monarch (VLDB '20)<br/>planet-scale: in-memory,<br/>push-based, query pushdown"]
    B["BtrDB (FAST '16)<br/>nanosecond telemetry: tree of<br/>time-partitioned AGGREGATES,<br/>queries hit precomputed summaries"]
    G -->|"chunk format"| P
    G -->|"chunk format"| VM
    P -->|"columnar + object store"| IOX
    G -->|"scale out"| M
    G -.->|"different regime:<br/>100M Hz telemetry"| B

2. The one-table summary

systemvalue codectime organizationlabel indexout-of-order
GorillaXOR floats, dod ts2h in-memory blocks(delegated to HBase layer)rejected
Prometheussame, xor.gohead + 2h blocks, exponential compactionMemPostings inverted indexbounded OOO window, separate buffer
VictoriaMetricsnearest-delta2 + optional lossy precisionBitsmonthly partitions of LSM partsindex_db + tagFilters cachebuffered in raw-rows shards
InfluxDB 3Parquet encodingsWAL → buffer → Parquet files by timecatalog + last/distinct cachesabsorbed by sort at persist
BtrDBdelta treetime-partitioned octree-ish tree(few, fat streams)version-annotated inserts

3. Experiments (experiments/)

cargo run --release --bin tsdb_bench

filewhatstatus
gen.rsscrape timestamps + gauge/counter/constant/random shapes, OOO arrivals, label setsPROVIDED
bits.rsMSB-first BitWriter/BitReader + sign_extendPROVIDED
baseline.rszigzag varint delta codec — the thing to beat, measured at 11.00 B/samplePROVIDED
gorilla.rsdod timestamp buckets + XOR-float codecSTUB
head.rsin-order fast path + bounded OOO window + LWW merge flushSTUB
index.rsMemPostings-style (name,value)→series inverted index + k-way intersectSTUB
bin/tsdb_bench.rsbaselines (provided) + gorilla ratios + OOO tax sweep + selector latencylanes

Contract highlights: gorilla roundtrip must be bit-exact for every f64; constant series ≤ ~2 bits/sample while full-entropy values must fail to compress (>8 B/sample — the codec wins on regularity, not magic); head rejects samples older than the OOO window and flush output is sorted so it feeds the in-order-only encoder; index intersection matches brute force and the unique-per-series label demonstrably explodes the postings map (the cardinality bomb, counted).

4. Reading guides

5. Cross-topic threads

  • Topic 4 (LSM): a TSDB is an LSM where the key is time — head=memtable, blocks=SSTs, compaction merges by time range, retention = drop the oldest level. VictoriaMetrics says “parts” and means it literally.
  • Topic 12 (columnar): IOx is the thesis that a TSDB is just a columnar store with a time-partitioned catalog — Parquet + DataFusion replace the custom chunk format.
  • Topic 23 (FTS): MemPostings IS an inverted index; label selectors are boolean term queries; high cardinality = unbounded vocabulary.
  • Topic 28 (cloud-native): InfluxDB 3’s WAL→object-store pipeline is topic 28’s landing-zone lesson applied to metrics.
  • Topic 29: the OOO window is a watermark — the same bounded-disorder-then-seal move as streaming watermarks (topic 27).

6. Capstone M30 (FalkorDB)

Temporal graph: edge/property history as (entity, attribute, ts) series; MATCH ... AT TIME t = snapshot read at t over history chunks. Design notes in notes.md §M-log.