Type: explanation · Last reviewed: 2026-07-17
Pulse Chronos is the storage engine inside Pulse Historian — the part that actually holds the bytes. It is not a general-purpose database bolted onto time-series; it is purpose-built around one idea: store a dense, fixed-interval grid of numbers, and let a value's position on disk be its timestamp. That single choice explains the file layout, the fixed precision, the compression tiers, and the query formats.
This page explains the anatomy — what's on disk and on the wire, and why. For the end-to-end journey of a reading (connect → store → serve → act), read The historian, end to end first. For the byte-level, code-cited detail, follow the links down to Storage engine (reference).
A conventional database stores time-series as rows of (timestamp, value). Chronos doesn't. Within a collection every tag has a fixed sampling interval (say, 60 000 ms), so the day is a known number of evenly-spaced slots. Chronos writes one 4-byte integer per slot, back to back, in slot order.
slot = (t − day_start) / interval and scans. There is no B-tree to traverse.i32 scaled ×1000 — a fixed 0.001 resolution, not a float. This is the deliberate trade: you get compact, exact-to-±0.0005 storage in exchange for fixed (not floating) precision. A null (missing) sample has its own sentinel.The consequence is that on-disk size is predictable — a pure function of tags × rate × time span — which is why Capacity planning can give you a formula instead of a guess.
A collection is a directory (data/{org}/{site}/{unit}/{grid}/) holding a handful of file types:
| File | Holds | Tier |
|---|---|---|
metadata.json |
tag names, descriptions, interval, ids | — |
<day>.bin |
the dense i32 grid for one day (today) | hot |
<day>.bin.czst |
the same day, zstd-compressed per column | cold |
<tag>.blob + <tag>.bref |
string/array/JSON values (a value heap + a reference grid) | blob |
Hot vs cold. Recent day-files are raw .bin, memory-mapped straight into RAM for zero-copy reads. An hourly sealer compresses any day older than the hot window (clarity.storage.seal.hot_days, default 0 — everything before today) into .bin.czst and deletes the raw file. Reads decompress the needed columns on demand; a write to a sealed day unseals it, writes, and reseals. In steady state only today is uncompressed.
Blob tags. Most tags are numeric, but a tag can carry strings or arrays (an alarm message, a vibration spectrum). Those live in a separate append-only .blob heap with a .bref reference grid pointing into it — the numeric grid stays pure.
Under the hood → Storage engine § Cold-tier compression · § Blob storage.
Because the bytes are so compact on disk, the bottleneck for a big query is often the wire format, not the scan. So the same query is available in three encodings — pick by size:
| Endpoint | Encoding | Per-point cost | Best for |
|---|---|---|---|
fast_query |
JSON | verbose text (~25 B/point) | one tag, small ranges, easiest to consume |
fast_query_binary |
binary v1 | 12 B/point (8-byte timestamp + 4-byte value, per tag) | single-tag bulk pulls |
fast_query_optimised |
binary v2 | approaches 4 B + ⅛ B/point as tag count rises | many tags over long ranges |
The v2 format shares one timestamp axis across all tags in the response and marks presence with a 1-bit-per-point validity bitmap — so the more tags you ask for, the closer the wire size gets to the raw on-disk size. At one tag it's marginally larger than v1 (nothing to amortize); its win is multi-tag queries. The SDKs decode all three automatically.
Under the hood → Storage engine § Binary response formats · exact wire layout in the Queries API reference.
If you ask for an hourly mean of a year of per-minute data, Chronos does not ship you 525 600 points to average client-side. A single foldable operation (mean, sum, min, max, count, first, last) is pushed down into the storage scan — computed as the bytes are read, so only the buckets come back. More complex pipelines (percentiles, rate, gap-filling, scoring) run as a post-processing stage. Either way you request it with a pipeline on the query.
Under the hood → Storage engine § Aggregation pipeline · Aggregation API reference.
Using the canonical dataset (acme-power / plant-1 / boiler-2 / default_grid, 60 000 ms grid):
steam_pressure written today lands in …/default_grid/<today>.bin at slot (now − midnight)/60000, memory-mapped and queryable instantly.<yesterday>.bin.czst.idfan_vibration's hourly mean over the last 30 days pushes the mean down into the scan across 29 cold day-files (decompressed on the fly) + today's hot grid, and returns ~720 hourly points — not 43 200 raw ones.The exact request/response JSON for reads is on Data model & scope § Worked example.
collection · mmap pool · cold tier · push-down op · write buffer · Pulse Chronos