Skip to main content
DB
Back homeUse case

IoT & telemetry

Durable history for every device and the current state of each one, in one engine.

The problem

Where the usual stack breaks

  1. 01

    A fleet never stops talking. Readings arrive in bursts, and the easy path, buffer them in a queue and batch them into a warehouse, trades freshness for durability and gives you a second system to run.

  2. 02

    Meanwhile the questions your operators ask are about right now: what is this device doing, which devices report a fault, what changed since the last reading. Scanning the whole history to answer them is the wrong shape.

  3. 03

    You want the history kept durably, one current row per device, and a way to follow changes the moment they commit.

Architecture

How NYXDB does it

Readings land in a compressed columnar table with indexes on the columns you filter by; device status lives in a table keyed by device; STREAM SELECT follows that status table live.

  1. 01

    Ingest

    Readings arrive through SQL, write batches or native bulk frames, and each one is in the write-ahead log before it is acknowledged.

  2. 02

    Compress

    History stays compact: codecs such as DoubleDelta and ZSTD are declared per column on a COLUMNAR table.

  3. 03

    Current state

    One row per device, always current, in an UPSERT table; a POSTINGS index answers “which devices report this status” directly.

  4. 04

    Follow

    Your operators see the current rows first, then each change as it commits, with STREAM SELECT.

Examples

In practice

Balances, as a versioned table with a slice per producer
CREATE TABLE balances (
chain_id UINT16 NOT NULL,
address STRING NOT NULL,
token STRING NOT NULL,
amount UINT256 NULL,
updated_at DATETIME64(3, 'UTC') NULL,
balance_index UINT128 NULL,
balance_version UINT64 NULL,
transfer_count UINT64 NULL,
last_transfer_at DATETIME64(3, 'UTC') NULL,
activity_index UINT128 NULL,
PRIMARY KEY (chain_id, address, token),
ATTRIBUTE GROUP holding (amount, updated_at, balance_index, balance_version)
VALID BY (balance_index)
REVISION BY (balance_version),
ATTRIBUTE GROUP activity (transfer_count, last_transfer_at, activity_index)
VALID BY (activity_index)
)
MODE VERSIONED
SHARD BY (address) SHARDS AUTO
ORDER BY (chain_id, address, token);
The transfer feed, as an append-only table
CREATE TABLE transfers (
chain_id UINT16 NOT NULL,
token STRING NOT NULL,
sender STRING NOT NULL,
recipient STRING NOT NULL,
amount UINT256 NOT NULL,
transferred_at DATETIME64(3, 'UTC') NOT NULL,
_index UINT128 NOT NULL,
INDEX transfers_ts (transferred_at) USING MINMAX SCOPE HISTORY GRANULE 8192
)
MODE APPEND
SHARD BY (token) SHARDS AUTO
PARTITION BY MONTH(transferred_at)
ORDER BY (chain_id, token, _index)
STORAGE PROFILE COLUMNAR;
Live volume per token, as a query that stays open
STREAM SELECT chain_id, token, SUM(amount) AS volume, COUNT(*) AS transfers
FROM transfers
GROUP BY chain_id, token;

The examples use the same two tables on every page. See the SQL reference for full syntax.

Capabilities

What you get out of the box

Bulk write routes

Write batches and native bulk frames keep up with high-volume device feeds.

Durable before ack

Writes are acknowledged after the write-ahead log has persisted them.

Keyed current state

One row per device, always the latest, in an UPSERT table.

Columnar history

Per-column codecs keep history compact; MINMAX and POSTINGS indexes keep it quick to filter.

Live follows

Follow a table without polling, with STREAM SELECT.

History + current

Device history and current state share one catalog and one SQL surface.

Learn more

Keep the fleet’s history and its current state together

Run a node and point your device feed at it.