DB
Back homeUse case

Time-series & monitoring

Ingest high-rate time-series, downsample with tumbling-window queries, and expire old data with lazy TTL and age-based tiering — one engine, no rollup pipeline.

The problem

Why this is hard today

Time-series workloads have two conflicting needs: keep raw resolution long enough to investigate, but do not let raw data grow without bound. Meeting both usually means a hot store for recent high-resolution data, a rollup job that downsamples into a warehouse, and a retention script that deletes — three moving parts around one stream.

The rollup job is a stream processor in disguise, and the retention script is a background scanner that competes with ingest for I/O. Both are operational surface that exists only because the store cannot age and downsample its own data.

What time-series needs is an engine that ingests at rate, downsamples continuously into coarser tables, and expires raw data lazily as part of normal compaction — with the retention policy attached to the table, not run beside it.

Architecture

How NYXDB does it

Raw readings append; tumbling-window queries downsample them into rollup tables; a ttl predicate expires raw data lazily during compaction and age-based movement tiers older parts to colder pools (ADR-019) — the whole lifecycle is table policy, not a pipeline.

  1. 01

    Ingest raw

    High-rate readings append to a columnar table.

  2. 02

    Downsample

    A tumble() window rolls raw into 1-minute (then hourly) buckets — avg/max or open/high/low/close; a transform can materialize the rollup.

  3. 03

    Expire

    A ttl predicate drops raw rows lazily at compaction — no background scanner competing with ingest.

  4. 04

    Tier

    Age-based part movement relocates older parts to colder pools.

Real SQL

In practice

Raw ingest with lazy TTL
CREATE TABLE metrics (
series String NOT NULL,
value Float64,
ts DateTime
) SETTINGS mode='append', layout='columnar',
ttl='ts < now() - INTERVAL 30 DAY';
Downsample with a tumbling window
STREAM SELECT series,
window_start,
avg(value) AS avg_v,
max(value) AS max_v
FROM tumble(metrics, ts, INTERVAL 1 minute)
GROUP BY series, window_start
EMIT AFTER WINDOW CLOSE;
OHLC candles from a trade stream
STREAM SELECT window_start,
arg_min(price, ts) AS open,
max(price) AS high,
min(price) AS low,
arg_max(price, ts) AS close
FROM tumble(trades, ts, INTERVAL 1 minute)
GROUP BY window_start
EMIT AFTER WINDOW CLOSE;

Every statement follows the engine’s own test SQL shapes. See the SQL reference for full syntax.

Capabilities

What you get

High-rate ingest

Columnar append absorbs high-rate series.

Tumbling & hopping windows

tumble() / hop() downsample by time bucket.

OHLC in SQL

arg_min / arg_max / max / min build candles over a window.

Lazy TTL

A ttl predicate expires raw rows at compaction — no background scanner (ADR-019).

Age-based tiering

Older parts move to colder pools by age (ADR-019).

Continuous rollups

Transforms materialize downsampled tables (ADR-080).

Proof

Measured on the vetted benchmark lane

Measured on Apple M4 Max (dev), macOS — server-class validation pending. Release build, median of 5, commit-pinned (d4a3885b, 2026-07-07). Ingest figures are engine-side. See the full benchmark suite.

One engine for the whole time-series lifecycle

Ingest, downsample, and expire — all in SQL.