Historian
The historian stores tag values over time and answers trend queries. It runs as its own service
(QBQ-Historian) and writes to a QuestDB store.
From 1.0.8 the historian depends on QuestDB 9.4 features. Multi-tag charts use them directly. Upgrade QuestDB before you upgrade QUBIQ. A store older than 9.4.0 is refused when you designate it, and the refusal names the version it found. A store that is already the historian keeps ingesting, so an upgrade notice never costs you data. Its charts are not guaranteed to work, though, and every start logs a critical error until QuestDB is upgraded. → Configuration
The path a value takes
- Scaling is applied at the edge, so stored values are in engineering units.
- The historian stores only tags with history enabled.
- Each reading is stored at its event time: the device's own timestamp when the device sends a plausible one, otherwise the time the server received it. The row also records its quality, and where the time came from. → Quality and time
- The tag's storage rules decide which readings become rows: the deadband (absolute or a percent of range), the minimum time between samples, and the store interval. A change of quality is always stored. → Storage rules
- Writes go through the store-and-forward buffer. An unreachable QuestDB buffers and replays in order rather than losing data.
- Three rollup tiers (1 minute, 1 hour and 1 day) pre-aggregate for long-range trends. → Rollup tiers
- A live tail buffer holds the most recent samples in memory and merges them into live chart queries. It covers the sub-second window between ingestion and QuestDB's flush.
Enabling history
Per tag, on the tag's History tab, or for many tags at once from the tree's Enable History… menu:
| Property | Effect |
|---|---|
| Enable History | Record this tag's values |
| Deadband | Store a value only when it moved by at least this much, in units or as % of range. The biggest lever on volume. |
| Min time between samples (ms) 1.0.8 | Store at most one value per this interval |
| Store interval (ms) | Store the current value again after this long without a stored row, so a value that holds still keeps a recent sample |
| Interpolation 1.0.8 | How the stored values connect on trends: Auto, Linear or Step |
→ Storage rules · Tag properties
Quality is stored with the value New in 1.0.8
Every row carries its quality, Good, Uncertain or Bad, and a non-Good row also keeps
the device's reason (Unknown: Connection down). When a device goes Bad, history shows it as Bad.
It does not show the last value carried on as if nothing had happened.
- Bad rows are left out of every value and every aggregate: the line, the averages, the rollups and the statistics. A Bad row holds the last known value, not a measurement.
- Charts shade the stretches that were not Good and name the reason under the crosshair.
- An
Unknownquality, which is what a lost connection reports, is stored as Bad.
Outages are marked, not drawn over New in 1.0.8
A tag stored on change writes nothing while its value holds still. On a chart, the stored value is therefore carried forward. That is right while the plant is running, and wrong across an outage nobody recorded. The historian marks two kinds of outage with one Bad row per affected tag. Each marker stops the line and shades the gap with its reason:
| Marker | Written when | Reason shown |
|---|---|---|
| Shutdown | The whole platform was down | Shutdown: QUBIQ was not running, no data was collected |
| Collector down | A collector service (QBQ-Bridge, QBQ-Expression) died or was restarted while the rest kept running | Collector down: <service> was not running, no data was collected |
When only the historian or QuestDB was down, nothing is marked. The collectors kept publishing into the store-and-forward buffer, which replays the real data.
Rollup tiers Changed in 1.0.8
| Tier | Rows per tag per year | Serves | Partitioned by |
|---|---|---|---|
Raw (historian) | Every stored sample | Short windows, raw export, exact statistics | The connection's Partition By (Day by default) |
1 minute (historian_1m) | 525,600 | Windows of 12 hours or more | Rollup Partition (Hour by default) |
1 hour (historian_1h) 1.0.8 | 8,760 | Weeks to years | Month |
1 day (historian_1d) 1.0.8 | 365 | Multi-year trends | Year |
A trend reads the coarsest tier that answers it exactly: one whose buckets fit whole inside the chart's buckets on the viewer's time zone. A UTC day chart reads the 1-day tier. A Jakarta day starts at 17:00 UTC, so it reads the 1-hour tier instead. A tier that is missing or behind is skipped, and the chart reads the next finer source.
Each tier is built from the raw table and has its own retention. Repairing one never invalidates another, and the coarse tiers can keep years of trend after the raw rows have expired.
One designated store
Exactly one QuestDB connection is the system historian. A single system setting names it; there is no per-connection flag, so "two historians" cannot be represented at all.
Only that connection, plus any still holding historised tags, gets a historian table, rollups, WAL settings, an ingestion worker and retention. A QuestDB connection added purely as a data source is left alone.