signalk-questdb 2.1.10 → 2.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.release-please-manifest.json +1 -1
- package/README.md +91 -28
- package/dist/change-gate.d.ts +26 -0
- package/dist/change-gate.js +118 -0
- package/dist/change-gate.js.map +1 -0
- package/dist/config/schema.d.ts +1 -0
- package/dist/config/schema.js +13 -1
- package/dist/config/schema.js.map +1 -1
- package/dist/container-env.d.ts +1 -0
- package/dist/container-env.js +26 -0
- package/dist/container-env.js.map +1 -1
- package/dist/delta-routing.d.ts +5 -0
- package/dist/delta-routing.js +40 -4
- package/dist/delta-routing.js.map +1 -1
- package/dist/history-v2.d.ts +1 -1
- package/dist/history-v2.js +180 -35
- package/dist/history-v2.js.map +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.js +42 -3
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/public/assets/AppPanel-Fvhb8xTK.js +1 -0
- package/public/assets/PluginConfigurationPanel-JR1rvS7f.js +9 -0
- package/public/assets/_virtual_mf-localSharedImportMap___mfe_internal__signalk_questdb__mf_owner__1-D-Le-Wxu.js +1 -0
- package/public/assets/_virtual_mf___mfe_internal__signalk_questdb__mf_owner__196689844091135__loadShare__shared-BRI06Lxu.js +8 -0
- package/public/assets/_virtual_mf___mfe_internal__signalk_questdb__mf_owner__196689844091135__prebuild__react__prebuild__-eQjVI-Oj.js +1 -0
- package/public/assets/{dist-BK97MX24.js → dist-BI-Ygd_Z.js} +1 -1
- package/public/assets/helpers-CsBAxMau.js +1 -0
- package/public/assets/hostInit-CdqUOHAJ.js +1 -0
- package/public/assets/{index-bERRzqQZ.js → index-mvK71Kw7.js} +2 -2
- package/public/assets/pendingShares-BI6kviiM.js +2 -0
- package/public/assets/utils-BRGuweMc.js +4 -0
- package/public/assets/vite-preload-helper-CRzLMJPS.js +1 -0
- package/public/remoteEntry.js +2 -2
- package/release-please-config.json +0 -1
- package/public/assets/AppPanel-p27aQlBS.js +0 -1
- package/public/assets/PluginConfigurationPanel-DWDBvXxG.js +0 -9
- package/public/assets/_virtual_mf-localSharedImportMap___mfe_internal__signalk_questdb__mf_owner__1-BiBqFhVp.js +0 -1
- package/public/assets/_virtual_mf___mfe_internal__signalk_questdb__mf_owner__275086164343916__loadShare__react__loadShare__.js-BWO9MGdR.js +0 -1
- package/public/assets/_virtual_mf___mfe_internal__signalk_questdb__mf_owner__275086164343916__prebuild__react__prebuild__-D2g10OtU.js +0 -1
- package/public/assets/helpers-Bizl8Raj.js +0 -1
- package/public/assets/hostInit-BFZIDSUI.js +0 -1
- package/public/assets/pendingShares-BS4kO2ec.js +0 -2
- package/public/assets/utils-jR9fY54i.js +0 -4
- package/public/assets/vite-preload-helper-uBIymjUX.js +0 -1
package/README.md
CHANGED
|
@@ -38,6 +38,9 @@ InfluxDB history can be imported.
|
|
|
38
38
|
### Requirements
|
|
39
39
|
|
|
40
40
|
- Node.js 22 or newer
|
|
41
|
+
- QuestDB 8.1 or newer (resolution queries use `SAMPLE BY … FROM … TO`). This
|
|
42
|
+
applies to an external QuestDB and to a pinned **QuestDB image version** in
|
|
43
|
+
managed mode; `latest` always qualifies
|
|
41
44
|
- Signal K server — 2.31 or newer to choose the default history provider in the
|
|
42
45
|
admin UI
|
|
43
46
|
- For managed mode: the
|
|
@@ -92,32 +95,33 @@ Export** and **Danger zone** sections. The Danger zone removes the container and
|
|
|
92
95
|
all of its data — including what Signal K's plugin uninstall cannot reach on
|
|
93
96
|
rootless Podman.
|
|
94
97
|
|
|
95
|
-
| Setting
|
|
96
|
-
|
|
|
97
|
-
| QuestDB image version
|
|
98
|
-
| Manage QuestDB container
|
|
99
|
-
| QuestDB host
|
|
100
|
-
| HTTP port
|
|
101
|
-
| ILP port
|
|
102
|
-
| PostgreSQL port
|
|
103
|
-
| QuestDB memory limit
|
|
104
|
-
| QuestDB CPU limit (cores)
|
|
105
|
-
| Default sampling rate (ms)
|
|
106
|
-
| Per-path sampling rates (ms)
|
|
107
|
-
|
|
|
108
|
-
|
|
|
109
|
-
| Record
|
|
110
|
-
|
|
|
111
|
-
| Restore
|
|
112
|
-
|
|
|
113
|
-
|
|
|
114
|
-
|
|
|
115
|
-
|
|
|
116
|
-
|
|
|
117
|
-
|
|
|
118
|
-
| Compression
|
|
119
|
-
|
|
|
120
|
-
|
|
|
98
|
+
| Setting | Default | Description |
|
|
99
|
+
| ----------------------------- | ------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
100
|
+
| QuestDB image version | `latest` | Image tag; the dropdown lists the latest release, pre-releases, and the last three stable releases |
|
|
101
|
+
| Manage QuestDB container | `true` | Let signalk-container run QuestDB; off = connect to an external QuestDB |
|
|
102
|
+
| QuestDB host | `127.0.0.1` | External mode only; in managed mode the address is resolved automatically |
|
|
103
|
+
| HTTP port | `9000` | External mode, or the host binding when "Bind to 0.0.0.0" is on; otherwise signalk-container allocates it |
|
|
104
|
+
| ILP port | `9009` | External mode, or the host binding when "Bind to 0.0.0.0" is on; otherwise signalk-container allocates it |
|
|
105
|
+
| PostgreSQL port | `8812` | Host binding for Grafana/psql when "Bind to 0.0.0.0" is on |
|
|
106
|
+
| QuestDB memory limit | `768m` | Hard cgroup cap on the container (empty = unlimited); the JVM heap gets half of it, so heap plus off-heap stays inside |
|
|
107
|
+
| QuestDB CPU limit (cores) | `1.5` | Fractional cores (0 = unlimited) |
|
|
108
|
+
| Default sampling rate (ms) | `2000` | Minimum ms between writes for any one path (0 = every update) |
|
|
109
|
+
| Per-path sampling rates (ms) | _(empty)_ | Glob → ms overrides, e.g. `{ "environment.wind.*": 200, "tanks.*": 10000 }` |
|
|
110
|
+
| Unchanged-value heartbeat (s) | `300` | A value equal to the last one recorded is written again only this often; changes are written at the sampling rate (0 = every sample, see Change-only recording) |
|
|
111
|
+
| Write batch interval (ms) | `5000` | How often buffered samples are committed — one WAL transaction per table per commit (see Performance) |
|
|
112
|
+
| Record own vessel | `true` | Record the `self` context |
|
|
113
|
+
| Record AIS targets | `true` | Record other vessels |
|
|
114
|
+
| Restore vessels on startup | `false` | Replay each vessel's last recorded position after a restart (see Startup restore) |
|
|
115
|
+
| Restore max age (minutes) | `9` | Only replay values recorded within this window |
|
|
116
|
+
| Allow `sourcePolicy=all` | `false` | Let History API callers split a path into one column per recording source (see History API) |
|
|
117
|
+
| QuestDB console webapp | `true` | Serve QuestDB's console inside the Signal K admin UI, admin only (see Console webapp) |
|
|
118
|
+
| Retention (days) | `0` | Drop daily partitions older than this (0 = keep forever) |
|
|
119
|
+
| Filter mode | `exclude` | `exclude` the matching paths, or `include` only the matching paths |
|
|
120
|
+
| Path patterns | _(empty)_ | Glob patterns, one per line, e.g. `environment.wind.*`; empty records everything |
|
|
121
|
+
| Compression codec | `lz4` | On-disk WAL segments and Parquet exports: `none`, `lz4` (fast) or `zstd` (smaller) |
|
|
122
|
+
| Compression level | `3` | ZSTD level 1–22, used only with `zstd` |
|
|
123
|
+
| Container network | `sk-network` | Shared Podman/Docker network QuestDB joins in managed mode, so the companion signalk-grafana reaches it by container DNS; host-port publication is separate ("Bind to 0.0.0.0") |
|
|
124
|
+
| Bind to 0.0.0.0 | `false` | Publish QuestDB's ports on all interfaces (see Connectivity) |
|
|
121
125
|
|
|
122
126
|
## Recording
|
|
123
127
|
|
|
@@ -162,12 +166,55 @@ objects — notifications, resource documents — are not descended into; a valu
|
|
|
162
166
|
table can hold is skipped and reported in the status endpoint and the panel
|
|
163
167
|
rather than dropped silently.
|
|
164
168
|
|
|
169
|
+
A number that means "no reading" is skipped and reported the same way: `NaN`
|
|
170
|
+
and `±Infinity`, and the NMEA 2000 "no data" marker on AC real, apparent and
|
|
171
|
+
reactive power (PGNs 65007–65029). Those fields cannot encode anything below
|
|
172
|
+
−2 000 000 000 W, but canboatjs decodes "no data" as −2 000 000 001 W, so real
|
|
173
|
+
and reactive power below that floor are dropped; real negative power, such as
|
|
174
|
+
export, is unaffected. Apparent power reaches the stream multiplied by the power
|
|
175
|
+
factor, which can move the marker anywhere below zero — and apparent power is
|
|
176
|
+
never negative, so any negative apparent power is dropped. Connections using the
|
|
177
|
+
canboat WASM decoder do not produce the marker at all.
|
|
178
|
+
|
|
165
179
|
**Sampling.** Each path is written at most once per **Default sampling rate**
|
|
166
180
|
(2000 ms), with per-path overrides by glob for the few paths that need finer
|
|
167
181
|
resolution and the many that need less. **Record own vessel** and **Record AIS
|
|
168
182
|
targets** switch the two kinds of context independently, and **Path patterns**
|
|
169
183
|
exclude (or include only) paths by glob.
|
|
170
184
|
|
|
185
|
+
**Change-only recording.** Most paths on a boat do not change from one sample
|
|
186
|
+
to the next: a switch that flips twice a day, an energy counter at rest, a tank
|
|
187
|
+
nobody is drawing from, a notification's state. Sampling alone would write each
|
|
188
|
+
of them at the sampling rate forever, and those repeats make up most of the
|
|
189
|
+
rows. So a value equal to the last one written for its path, vessel and source
|
|
190
|
+
is skipped until the **Unchanged-value heartbeat** is due, and then
|
|
191
|
+
written once more. Every change is written as it arrives, subject to the
|
|
192
|
+
sampling rate.
|
|
193
|
+
|
|
194
|
+
The heartbeat is what keeps the gaps readable. A path that is still being
|
|
195
|
+
updated has a row at least once per heartbeat, so a longer silence means the
|
|
196
|
+
data really stopped:
|
|
197
|
+
|
|
198
|
+
- **History API (v2).** A resolution bucket with no row takes the last sample
|
|
199
|
+
before it, for up to twice the heartbeat (see History API). A switch that has
|
|
200
|
+
been on for an hour reads as on for the whole hour, not as one point and
|
|
201
|
+
then nothing.
|
|
202
|
+
- **Stale data.** When Signal K marks a path stale it sends `null` for it.
|
|
203
|
+
Nothing is written for the `null`, so the history shows the last value for at
|
|
204
|
+
most twice the heartbeat and then a gap. The next reading after that is
|
|
205
|
+
always written, even if it equals the one before the gap.
|
|
206
|
+
- **Direct SQL and Grafana** see the rows as stored. Add `FILL(PREV)` to a
|
|
207
|
+
`SAMPLE BY` query to carry values across buckets.
|
|
208
|
+
- **Averages** weight rows, not time. A bucket in which a value changes often
|
|
209
|
+
and then holds steady averages mostly over the changes; `min`, `max`, `first`
|
|
210
|
+
and `last` are exact.
|
|
211
|
+
- **Startup restore** replays motion values recorded within **Restore max
|
|
212
|
+
age**. Keep the heartbeat below that, or a vessel lying still may come back
|
|
213
|
+
with its position but without its speed, course and heading.
|
|
214
|
+
|
|
215
|
+
`navigation.position` is never skipped. Set the heartbeat to 0 to record every
|
|
216
|
+
sample.
|
|
217
|
+
|
|
171
218
|
**Batching.** Samples are buffered and committed every **Write batch interval**
|
|
172
219
|
(5 s), one WAL transaction per table per commit. The buffer holds 100 000 lines;
|
|
173
220
|
if QuestDB is unreachable for longer than that covers, the oldest lines are
|
|
@@ -205,6 +252,17 @@ in the window: the average there is of what the window still holds, and a value
|
|
|
205
252
|
leaves it N buckets after it arrived. `ema` carries its last value across an
|
|
206
253
|
empty bucket instead.
|
|
207
254
|
|
|
255
|
+
Buckets start at the request's `from` and cover the whole range, empty ones
|
|
256
|
+
included. A bucket with no row is not always missing data. Change-only recording (see
|
|
257
|
+
Recording) writes an unchanged value only once per heartbeat, so an empty bucket
|
|
258
|
+
takes the last sample before it — in every aggregate, since every aggregate of a
|
|
259
|
+
steady value is that value — for up to twice the heartbeat after the sample was
|
|
260
|
+
taken, and never into a bucket that has not started yet. At the start of a
|
|
261
|
+
range the sample comes from just before it. Past that window, and with the
|
|
262
|
+
heartbeat set to 0, an empty bucket is `null`. This happens before `sma` and
|
|
263
|
+
`ema`, so a held value counts as a sample. `middle_index` and
|
|
264
|
+
`navigation.position` return recorded rows only.
|
|
265
|
+
|
|
208
266
|
A path recorded in radians — a heading, a course, a wind angle; `units: rad` in
|
|
209
267
|
the server's metadata for the path — is averaged as a vector: `average`, `sma`
|
|
210
268
|
and `ema` take the angle of the mean sine and cosine, so 359° and 1° average to
|
|
@@ -657,10 +715,15 @@ shown on the status card as **WAL suspended**. Two causes need two remedies:
|
|
|
657
715
|
seconds, keeping write volume modest on busy NMEA 2000 buses; per-path
|
|
658
716
|
overrides allow faster rates for critical paths
|
|
659
717
|
(`{ "environment.wind.*": 200 }`) while slow-changing ones stay throttled.
|
|
718
|
+
- **Change-only recording** skips a value that has not changed since it was last
|
|
719
|
+
written until the heartbeat is due. Paths that hold steady — which is
|
|
720
|
+
most of them — cost one row per heartbeat instead of one per sampling
|
|
721
|
+
interval, which cuts both disk use and WAL apply work (see Recording).
|
|
660
722
|
- **Resource caps** of 768 MB RAM and 1.5 CPU cores (cgroup limits via
|
|
661
723
|
signalk-container) keep QuestDB from squeezing co-resident containers like
|
|
662
|
-
Grafana, mayara, or signalk-backup. The JVM
|
|
663
|
-
|
|
724
|
+
Grafana, mayara, or signalk-backup. The JVM heap gets half of the memory
|
|
725
|
+
cap, so total footprint (heap plus off-heap) is bounded, and a JVM that runs
|
|
726
|
+
out of heap exits so the container restarts instead of hanging. Set the
|
|
664
727
|
memory limit to empty or the CPU limit to `0` to lift the cap on roomier
|
|
665
728
|
hosts.
|
|
666
729
|
- **Worker tuning** cuts idle CPU. QuestDB sizes its worker pools from the host
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
export declare class ChangeGate {
|
|
2
|
+
private readonly heartbeatMs;
|
|
3
|
+
private readonly maxEntries;
|
|
4
|
+
private readonly byContext;
|
|
5
|
+
private count;
|
|
6
|
+
constructor(heartbeatMs: number, maxEntries?: number);
|
|
7
|
+
get size(): number;
|
|
8
|
+
/**
|
|
9
|
+
* True when `value` is what this path, context and source last wrote and
|
|
10
|
+
* the heartbeat is not yet due — the value adds nothing a reader cannot
|
|
11
|
+
* carry forward. A non-positive heartbeat turns the gate off.
|
|
12
|
+
*/
|
|
13
|
+
isRepeat(path: string, context: string, source: string | undefined, value: unknown, now: number): boolean;
|
|
14
|
+
/** Records that `value` was written, so later equal values are repeats. */
|
|
15
|
+
wrote(path: string, context: string, source: string | undefined, value: unknown, now: number): void;
|
|
16
|
+
/**
|
|
17
|
+
* Forgets a path in a context, for every source, along with any leaves it
|
|
18
|
+
* was flattened into. Signal K reports a path going stale as a null value;
|
|
19
|
+
* without this, the reading that comes back after the gap would be skipped
|
|
20
|
+
* whenever it equals the one before it, and the gap would read as a value
|
|
21
|
+
* held straight through it.
|
|
22
|
+
*/
|
|
23
|
+
forget(path: string, context: string): void;
|
|
24
|
+
clear(): void;
|
|
25
|
+
private evict;
|
|
26
|
+
}
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
// Change-only recording gate.
|
|
2
|
+
//
|
|
3
|
+
// Most of a boat's paths do not change from one sample to the next: a switch
|
|
4
|
+
// that flips twice a day, an energy counter at rest, a notification's state,
|
|
5
|
+
// a tank that is not being used. The throttle alone writes every one of them
|
|
6
|
+
// at the sampling rate forever — measured on a live install, 45% of numeric
|
|
7
|
+
// rows and 76% of string rows were a path that held one value all day.
|
|
8
|
+
//
|
|
9
|
+
// So a value equal to the last one WRITTEN for its path, context and source
|
|
10
|
+
// is skipped until the heartbeat is due. Every change is written as it
|
|
11
|
+
// arrives (subject to the throttle), and the heartbeat bounds how old the
|
|
12
|
+
// newest row of a live path can be. That bound is what lets the history API
|
|
13
|
+
// carry a held value across the empty buckets in between without also
|
|
14
|
+
// carrying a dead sensor's last value forever — see holdMs in history-v2.
|
|
15
|
+
//
|
|
16
|
+
// Compared against the last value written, not the last one seen: a change
|
|
17
|
+
// the throttle drops leaves the gate on the old value, so the next delta
|
|
18
|
+
// carrying the new one is still a change and gets written.
|
|
19
|
+
export class ChangeGate {
|
|
20
|
+
heartbeatMs;
|
|
21
|
+
maxEntries;
|
|
22
|
+
// Keyed by context first, so forget() — run for every null on the stream,
|
|
23
|
+
// and some sources send nulls routinely — only walks one vessel's paths.
|
|
24
|
+
byContext = new Map();
|
|
25
|
+
count = 0;
|
|
26
|
+
// Same pressure valve as Throttle: transient AIS contexts churn for months,
|
|
27
|
+
// and every (path, context, source) would otherwise stay until shutdown.
|
|
28
|
+
// An entry older than the heartbeat gates nothing — the next value is due
|
|
29
|
+
// anyway — so those are swept first; a full clear only costs one extra
|
|
30
|
+
// row per path.
|
|
31
|
+
constructor(heartbeatMs, maxEntries = 20_000) {
|
|
32
|
+
this.heartbeatMs = heartbeatMs;
|
|
33
|
+
this.maxEntries = maxEntries;
|
|
34
|
+
}
|
|
35
|
+
get size() {
|
|
36
|
+
return this.count;
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* True when `value` is what this path, context and source last wrote and
|
|
40
|
+
* the heartbeat is not yet due — the value adds nothing a reader cannot
|
|
41
|
+
* carry forward. A non-positive heartbeat turns the gate off.
|
|
42
|
+
*/
|
|
43
|
+
isRepeat(path, context, source, value, now) {
|
|
44
|
+
if (this.heartbeatMs <= 0)
|
|
45
|
+
return false;
|
|
46
|
+
const entry = this.byContext.get(context)?.get(key(path, source));
|
|
47
|
+
return (entry !== undefined &&
|
|
48
|
+
entry.value === value &&
|
|
49
|
+
now - entry.at < this.heartbeatMs);
|
|
50
|
+
}
|
|
51
|
+
/** Records that `value` was written, so later equal values are repeats. */
|
|
52
|
+
wrote(path, context, source, value, now) {
|
|
53
|
+
if (this.heartbeatMs <= 0)
|
|
54
|
+
return;
|
|
55
|
+
const k = key(path, source);
|
|
56
|
+
let paths = this.byContext.get(context);
|
|
57
|
+
if (!paths?.has(k)) {
|
|
58
|
+
if (this.count >= this.maxEntries) {
|
|
59
|
+
this.evict(now);
|
|
60
|
+
paths = this.byContext.get(context);
|
|
61
|
+
}
|
|
62
|
+
if (!paths) {
|
|
63
|
+
paths = new Map();
|
|
64
|
+
this.byContext.set(context, paths);
|
|
65
|
+
}
|
|
66
|
+
this.count += 1;
|
|
67
|
+
}
|
|
68
|
+
paths.set(k, { path, value, at: now });
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* Forgets a path in a context, for every source, along with any leaves it
|
|
72
|
+
* was flattened into. Signal K reports a path going stale as a null value;
|
|
73
|
+
* without this, the reading that comes back after the gap would be skipped
|
|
74
|
+
* whenever it equals the one before it, and the gap would read as a value
|
|
75
|
+
* held straight through it.
|
|
76
|
+
*/
|
|
77
|
+
forget(path, context) {
|
|
78
|
+
const paths = this.byContext.get(context);
|
|
79
|
+
if (!paths)
|
|
80
|
+
return;
|
|
81
|
+
const leafPrefix = `${path}.`;
|
|
82
|
+
for (const [k, entry] of paths) {
|
|
83
|
+
if (entry.path === path || entry.path.startsWith(leafPrefix)) {
|
|
84
|
+
paths.delete(k);
|
|
85
|
+
this.count -= 1;
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
if (paths.size === 0)
|
|
89
|
+
this.byContext.delete(context);
|
|
90
|
+
}
|
|
91
|
+
clear() {
|
|
92
|
+
this.byContext.clear();
|
|
93
|
+
this.count = 0;
|
|
94
|
+
}
|
|
95
|
+
evict(now) {
|
|
96
|
+
for (const [context, paths] of this.byContext) {
|
|
97
|
+
for (const [k, entry] of paths) {
|
|
98
|
+
if (now - entry.at >= this.heartbeatMs) {
|
|
99
|
+
paths.delete(k);
|
|
100
|
+
this.count -= 1;
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
if (paths.size === 0)
|
|
104
|
+
this.byContext.delete(context);
|
|
105
|
+
}
|
|
106
|
+
// Cleared unless the sweep got well below the cap: a sweep that frees a
|
|
107
|
+
// handful would run again after that many new keys, walking every entry
|
|
108
|
+
// on nearly every new key of a busy AIS stream.
|
|
109
|
+
if (this.count >= this.maxEntries * 0.75)
|
|
110
|
+
this.clear();
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
// A Signal K path cannot contain `|`, so two (path, source) pairs never share
|
|
114
|
+
// a key; contexts are kept apart one level up, in byContext.
|
|
115
|
+
function key(path, source) {
|
|
116
|
+
return `${path}|${source ?? ""}`;
|
|
117
|
+
}
|
|
118
|
+
//# sourceMappingURL=change-gate.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"change-gate.js","sourceRoot":"","sources":["../src/change-gate.ts"],"names":[],"mappings":"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"}
|
package/dist/config/schema.d.ts
CHANGED
|
@@ -15,6 +15,7 @@ export declare const ConfigSchema: Type.TObject<{
|
|
|
15
15
|
defaultSamplingRate: Type.TNumber;
|
|
16
16
|
ilpFlushIntervalMs: Type.TNumber;
|
|
17
17
|
samplingRates: Type.TRecord<"^.*$", Type.TNumber>;
|
|
18
|
+
unchangedHeartbeatSeconds: Type.TNumber;
|
|
18
19
|
recordSelf: Type.TBoolean;
|
|
19
20
|
recordOthers: Type.TBoolean;
|
|
20
21
|
restoreOnStart: Type.TBoolean;
|
package/dist/config/schema.js
CHANGED
|
@@ -32,7 +32,7 @@ export const ConfigSchema = Type.Object({
|
|
|
32
32
|
questdbMemoryLimit: Type.String({
|
|
33
33
|
default: "768m",
|
|
34
34
|
title: "QuestDB memory limit",
|
|
35
|
-
description: "Hard cgroup memory cap (e.g. 512m, 1g, 2g). Empty = unlimited. The JVM
|
|
35
|
+
description: "Hard cgroup memory cap (e.g. 512m, 1g, 2g). Empty = unlimited. The JVM heap gets half of this, so capping here bounds total footprint including off-heap.",
|
|
36
36
|
}),
|
|
37
37
|
questdbCpuLimit: Type.Number({
|
|
38
38
|
default: 1.5,
|
|
@@ -67,6 +67,12 @@ export const ConfigSchema = Type.Object({
|
|
|
67
67
|
title: "Per-path sampling rates (ms)",
|
|
68
68
|
description: 'Override default rate for specific paths. e.g. { "environment.wind.*": 200, "tanks.*": 10000 }',
|
|
69
69
|
}),
|
|
70
|
+
unchangedHeartbeatSeconds: Type.Number({
|
|
71
|
+
default: 300,
|
|
72
|
+
minimum: 0,
|
|
73
|
+
title: "Unchanged-value heartbeat (s)",
|
|
74
|
+
description: "A value equal to the last one recorded for its path is written again only once per this many seconds instead of at every sampling interval; a change is written as it arrives, subject to the sampling rate like any value. History queries carry the last value across the buckets in between, for up to twice this long, so a path that stops updating shows as a gap after that. 0 = record every sample.",
|
|
75
|
+
}),
|
|
70
76
|
recordSelf: Type.Boolean({
|
|
71
77
|
default: true,
|
|
72
78
|
title: "Record own vessel",
|
|
@@ -139,6 +145,12 @@ export function normalizeConfig(config) {
|
|
|
139
145
|
paths: config.pathFilter?.paths ?? [],
|
|
140
146
|
},
|
|
141
147
|
samplingRates: config.samplingRates ?? {},
|
|
148
|
+
// A config saved before the option existed gets the schema default, so
|
|
149
|
+
// an update turns change-only recording on. An explicit 0 turns it off.
|
|
150
|
+
unchangedHeartbeatSeconds: typeof config.unchangedHeartbeatSeconds === "number" &&
|
|
151
|
+
config.unchangedHeartbeatSeconds >= 0
|
|
152
|
+
? config.unchangedHeartbeatSeconds
|
|
153
|
+
: 300,
|
|
142
154
|
// Missing recording toggles get the schema defaults (both ON). The
|
|
143
155
|
// runtime guards read these directly, so without this a hand-edited or
|
|
144
156
|
// pre-panel config silently disabled the recording the schema promises.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"schema.js","sourceRoot":"","sources":["../../src/config/schema.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAU,MAAM,SAAS,CAAC;AAEvC,MAAM,CAAC,MAAM,YAAY,GAAG,IAAI,CAAC,MAAM,CAAC;IACtC,WAAW,EAAE,IAAI,CAAC,MAAM,CAAC;QACvB,OAAO,EAAE,WAAW;QACpB,KAAK,EAAE,cAAc;QACrB,WAAW,EACT,oHAAoH;KACvH,CAAC;IACF,cAAc,EAAE,IAAI,CAAC,MAAM,CAAC;QAC1B,OAAO,EAAE,IAAI;QACb,KAAK,EAAE,mBAAmB;QAC1B,WAAW,EACT,4HAA4H;KAC/H,CAAC;IACF,eAAe,EAAE,IAAI,CAAC,MAAM,CAAC;QAC3B,OAAO,EAAE,IAAI;QACb,KAAK,EAAE,qBAAqB;QAC5B,WAAW,EACT,4HAA4H;KAC/H,CAAC;IACF,aAAa,EAAE,IAAI,CAAC,MAAM,CAAC;QACzB,OAAO,EAAE,IAAI;QACb,KAAK,EAAE,sBAAsB;QAC7B,WAAW,EACT,uHAAuH;KAC1H,CAAC;IACF,cAAc,EAAE,IAAI,CAAC,MAAM,CAAC;QAC1B,OAAO,EAAE,QAAQ;QACjB,KAAK,EAAE,uBAAuB;KAC/B,CAAC;IAEF,gBAAgB,EAAE,IAAI,CAAC,OAAO,CAAC;QAC7B,OAAO,EAAE,IAAI;QACb,KAAK,EAAE,gDAAgD;QACvD,WAAW,EAAE,oDAAoD;KAClE,CAAC;IAEF,kBAAkB,EAAE,IAAI,CAAC,MAAM,CAAC;QAC9B,OAAO,EAAE,MAAM;QACf,KAAK,EAAE,sBAAsB;QAC7B,WAAW,EACT,
|
|
1
|
+
{"version":3,"file":"schema.js","sourceRoot":"","sources":["../../src/config/schema.ts"],"names":[],"mappings":"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"}
|
package/dist/container-env.d.ts
CHANGED
package/dist/container-env.js
CHANGED
|
@@ -26,12 +26,38 @@ export const QUESTDB_COMMIT_MODE = "sync";
|
|
|
26
26
|
// enough to sleep promptly, high enough that a burst of ingest does not pay a
|
|
27
27
|
// wake-up per batch.
|
|
28
28
|
const WORKER_SLEEP_THRESHOLD = "100";
|
|
29
|
+
// Extra JVM flags; the image's entrypoint splices JVM_PREPEND into the java
|
|
30
|
+
// command line.
|
|
31
|
+
//
|
|
32
|
+
// ExitOnOutOfMemoryError: QuestDB survives a heap OOM inside one query, but
|
|
33
|
+
// not one inside a worker loop — the worker thread dies and the JVM stays up
|
|
34
|
+
// without it. Observed on a Pi: a "GC overhead limit exceeded" killed the
|
|
35
|
+
// shared-write worker, and the JVM then ran back-to-back full GCs at ~1.4
|
|
36
|
+
// cores for nine hours, recording nothing and failing its healthcheck. The
|
|
37
|
+
// container's restart policy only acts on an exit, so turn the first OOM into
|
|
38
|
+
// one: a restart costs seconds, which the ILP writer's buffer absorbs.
|
|
39
|
+
//
|
|
40
|
+
// MaxRAMPercentage: under a cgroup cap the JVM sizes its heap to the default
|
|
41
|
+
// 25% of it — 192 MiB under the 768m default, which that same install
|
|
42
|
+
// exhausted. QuestDB keeps column data off-heap (mmap and native buffers, the
|
|
43
|
+
// page cache for which the kernel reclaims under the cap), so half the cap
|
|
44
|
+
// for the heap still leaves the native side room while the total stays
|
|
45
|
+
// bounded by the cap. Only set under a cap: without one the percentage
|
|
46
|
+
// applies to the whole host, where the default 25% is already generous.
|
|
47
|
+
export function buildJvmPrepend(config) {
|
|
48
|
+
const flags = ["-XX:+ExitOnOutOfMemoryError"];
|
|
49
|
+
if (config.questdbMemoryLimit?.trim()) {
|
|
50
|
+
flags.push("-XX:MaxRAMPercentage=50");
|
|
51
|
+
}
|
|
52
|
+
return flags.join(" ");
|
|
53
|
+
}
|
|
29
54
|
export function buildContainerEnv(config) {
|
|
30
55
|
return {
|
|
31
56
|
QDB_TELEMETRY_ENABLED: "false",
|
|
32
57
|
QDB_HTTP_ENABLED: "true",
|
|
33
58
|
QDB_LINE_TCP_ENABLED: "true",
|
|
34
59
|
QDB_CAIRO_COMMIT_MODE: QUESTDB_COMMIT_MODE,
|
|
60
|
+
JVM_PREPEND: buildJvmPrepend(config),
|
|
35
61
|
// ── Low-power tuning (Pi, Cerbo) ──────────────────────────────────────
|
|
36
62
|
//
|
|
37
63
|
// QuestDB sizes most worker pools from the host core count and defaults
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"container-env.js","sourceRoot":"","sources":["../src/container-env.ts"],"names":[],"mappings":"AAAA,qDAAqD;AACrD,EAAE;AACF,wEAAwE;AACxE,8EAA8E;AAC9E,6EAA6E;AAC7E,0EAA0E;AAC1E,6EAA6E;AAC7E,yBAAyB;AAIzB,kEAAkE;AAClE,6EAA6E;AAC7E,0EAA0E;AAC1E,0EAA0E;AAC1E,yEAAyE;AACzE,0EAA0E;AAC1E,6EAA6E;AAC7E,2EAA2E;AAC3E,0EAA0E;AAC1E,QAAQ;AACR,EAAE;AACF,4EAA4E;AAC5E,8EAA8E;AAC9E,4EAA4E;AAC5E,MAAM,CAAC,MAAM,mBAAmB,GAAG,MAAM,CAAC;AAE1C,0EAA0E;AAC1E,2EAA2E;AAC3E,8EAA8E;AAC9E,qBAAqB;AACrB,MAAM,sBAAsB,GAAG,KAAK,CAAC;AAErC,MAAM,UAAU,iBAAiB,CAC/B,MAAuB;IAEvB,OAAO;QACL,qBAAqB,EAAE,OAAO;QAC9B,gBAAgB,EAAE,MAAM;QACxB,oBAAoB,EAAE,MAAM;QAC5B,qBAAqB,EAAE,mBAAmB;
|
|
1
|
+
{"version":3,"file":"container-env.js","sourceRoot":"","sources":["../src/container-env.ts"],"names":[],"mappings":"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"}
|
package/dist/delta-routing.d.ts
CHANGED
|
@@ -33,6 +33,11 @@ export declare class UnstorableTracker {
|
|
|
33
33
|
clear(): void;
|
|
34
34
|
}
|
|
35
35
|
export declare function extractVesselName(path: string, value: unknown): string | null;
|
|
36
|
+
/**
|
|
37
|
+
* True for a numeric value that is an NMEA 2000 "no data" marker leaked
|
|
38
|
+
* through the decoder rather than a reading. See N2K_AC_POWER_FLOOR.
|
|
39
|
+
*/
|
|
40
|
+
export declare function isNoDataSentinel(path: string, value: number): boolean;
|
|
36
41
|
export declare function routeDeltaValue(path: string, value: unknown): DeltaRoute;
|
|
37
42
|
/**
|
|
38
43
|
* Scalar leaves of an object value, as dotted paths.
|
package/dist/delta-routing.js
CHANGED
|
@@ -58,6 +58,38 @@ export function extractVesselName(path, value) {
|
|
|
58
58
|
const name = value.name;
|
|
59
59
|
return typeof name === "string" && name.trim() !== "" ? name : null;
|
|
60
60
|
}
|
|
61
|
+
// The AC power fields of NMEA 2000 PGNs 65007–65029 (utility and generator
|
|
62
|
+
// real, apparent and reactive power, total and per phase) carry an offset of
|
|
63
|
+
// -2 000 000 000 W, so the lowest value they can encode is exactly that. The
|
|
64
|
+
// canboat definition also marks them signed, and canboatjs reads them that
|
|
65
|
+
// way: a device reporting "no data" (0xFFFFFFFF) decodes as -1, misses the
|
|
66
|
+
// signed no-data check (0x7FFFFFFF), and has the offset added — arriving as
|
|
67
|
+
// -2000000001 W ("error" and the other reserved values land just below). The
|
|
68
|
+
// canboat WASM decoder gets this right; canboatjs connections do not. A
|
|
69
|
+
// handful of these on device start-up dominate every min and average over the
|
|
70
|
+
// path they land in.
|
|
71
|
+
//
|
|
72
|
+
// Real and reactive power: anything below the floor cannot have been
|
|
73
|
+
// encoded, so it is an artefact by construction, and a real negative
|
|
74
|
+
// (exported, capacitive) power passes untouched.
|
|
75
|
+
//
|
|
76
|
+
// Apparent power needs a different test, because n2k-signalk multiplies it by
|
|
77
|
+
// the power factor before it reaches the stream: the marker times a factor
|
|
78
|
+
// below 1 lands ABOVE the floor (-3725220001.86 was observed in the field with
|
|
79
|
+
// a factor above 1). But apparent power is a magnitude, V·I, and never
|
|
80
|
+
// negative — so any negative value is the marker, whatever it was scaled by.
|
|
81
|
+
const N2K_AC_POWER_FLOOR = -2_000_000_000;
|
|
82
|
+
const SIGNED_AC_POWER_LEAF = /\.(realPower|reactivePower)$/;
|
|
83
|
+
const APPARENT_POWER_LEAF = /\.apparentPower$/;
|
|
84
|
+
/**
|
|
85
|
+
* True for a numeric value that is an NMEA 2000 "no data" marker leaked
|
|
86
|
+
* through the decoder rather than a reading. See N2K_AC_POWER_FLOOR.
|
|
87
|
+
*/
|
|
88
|
+
export function isNoDataSentinel(path, value) {
|
|
89
|
+
if (APPARENT_POWER_LEAF.test(path))
|
|
90
|
+
return value < 0;
|
|
91
|
+
return value < N2K_AC_POWER_FLOOR && SIGNED_AC_POWER_LEAF.test(path);
|
|
92
|
+
}
|
|
61
93
|
export function routeDeltaValue(path, value) {
|
|
62
94
|
// NaN and ±Infinity have no ILP representation. QuestDB does NOT reject
|
|
63
95
|
// them — verified against a live instance, `value=NaN` is accepted and
|
|
@@ -65,9 +97,12 @@ export function routeDeltaValue(path, value) {
|
|
|
65
97
|
// column, and every aggregate over that path afterwards. A source
|
|
66
98
|
// reporting a non-finite number is reporting "no reading", which is what
|
|
67
99
|
// recording nothing means. flattenObjectValue already applied this to
|
|
68
|
-
// leaves; the top-level path has to agree.
|
|
100
|
+
// leaves; the top-level path has to agree. A decoder's "no data" marker
|
|
101
|
+
// is the same "no reading", in disguise.
|
|
69
102
|
if (typeof value === "number")
|
|
70
|
-
return Number.isFinite(value)
|
|
103
|
+
return Number.isFinite(value) && !isNoDataSentinel(path, value)
|
|
104
|
+
? "number"
|
|
105
|
+
: null;
|
|
71
106
|
if (typeof value === "string")
|
|
72
107
|
return "string";
|
|
73
108
|
// Booleans are everywhere in Signal K — switch and relay states, pump and
|
|
@@ -129,8 +164,9 @@ export function flattenObjectValue(path, value) {
|
|
|
129
164
|
const leafPath = `${path}.${key}`;
|
|
130
165
|
if (typeof leaf === "number") {
|
|
131
166
|
// NaN and ±Infinity have no ILP representation and would poison the
|
|
132
|
-
// column; a sensor reporting them is reporting "no reading"
|
|
133
|
-
|
|
167
|
+
// column; a sensor reporting them is reporting "no reading", and so is
|
|
168
|
+
// a leaked "no data" marker.
|
|
169
|
+
if (Number.isFinite(leaf) && !isNoDataSentinel(leafPath, leaf))
|
|
134
170
|
leaves.push({ path: leafPath, value: leaf });
|
|
135
171
|
else
|
|
136
172
|
skipped.push(leafPath);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"delta-routing.js","sourceRoot":"","sources":["../src/delta-routing.ts"],"names":[],"mappings":"AAsBA;;;;;;;;;;;;;;GAcG;AACH,MAAM,OAAO,iBAAiB;IAIC;IAHZ,KAAK,GAAG,IAAI,GAAG,EAAU,CAAC;IACnC,MAAM,GAAG,KAAK,CAAC;IAEvB,YAA6B,MAAM,EAAE;QAAR,QAAG,GAAH,GAAG,CAAK;IAAG,CAAC;IAEzC,oEAAoE;IACpE,IAAI,CAAC,IAAY;QACf,IAAI,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC;YAAE,OAAO;QACjC,IAAI,IAAI,CAAC,KAAK,CAAC,IAAI,IAAI,IAAI,CAAC,GAAG,EAAE,CAAC;YAChC,IAAI,CAAC,MAAM,GAAG,IAAI,CAAC;YACnB,OAAO;QACT,CAAC;QACD,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;IACvB,CAAC;IAED,IAAI,IAAI;QACN,OAAO,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC;IACzB,CAAC;IAED,IAAI,SAAS;QACX,OAAO,IAAI,CAAC,MAAM,CAAC;IACrB,CAAC;IAED,QAAQ,CAAC,KAAa;QACpB,OAAO,CAAC,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,KAAK,CAAC,CAAC;IACzC,CAAC;IAED,KAAK;QACH,IAAI,CAAC,KAAK,CAAC,KAAK,EAAE,CAAC;QACnB,IAAI,CAAC,MAAM,GAAG,KAAK,CAAC;IACtB,CAAC;CACF;AAED,+DAA+D;AAC/D,yEAAyE;AACzE,2EAA2E;AAC3E,sEAAsE;AACtE,wEAAwE;AACxE,uBAAuB;AACvB,MAAM,UAAU,iBAAiB,CAAC,IAAY,EAAE,KAAc;IAC5D,IAAI,IAAI,KAAK,EAAE;QAAE,OAAO,IAAI,CAAC;IAC7B,IAAI,KAAK,KAAK,IAAI,IAAI,OAAO,KAAK,KAAK,QAAQ;QAAE,OAAO,IAAI,CAAC;IAC7D,MAAM,IAAI,GAAI,KAA4B,CAAC,IAAI,CAAC;IAChD,OAAO,OAAO,IAAI,KAAK,QAAQ,IAAI,IAAI,CAAC,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC;AACtE,CAAC;AAED,MAAM,UAAU,eAAe,CAAC,IAAY,EAAE,KAAc;IAC1D,wEAAwE;IACxE,uEAAuE;IACvE,qEAAqE;IACrE,kEAAkE;IAClE,yEAAyE;IACzE,sEAAsE;IACtE,
|
|
1
|
+
{"version":3,"file":"delta-routing.js","sourceRoot":"","sources":["../src/delta-routing.ts"],"names":[],"mappings":"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"}
|
package/dist/history-v2.d.ts
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
import { QueryClient } from "./query-client.js";
|
|
2
2
|
import type { history as HistoryApi } from "@signalk/server-api";
|
|
3
|
-
export declare function createHistoryProviderV2(queryClient: QueryClient, selfContext: string, sourcePolicyAllEnabled?: boolean, debug?: (msg: string) => void, unitsOf?: (path: string, context: string) => string | undefined): HistoryApi.HistoryProvider;
|
|
3
|
+
export declare function createHistoryProviderV2(queryClient: QueryClient, selfContext: string, sourcePolicyAllEnabled?: boolean, debug?: (msg: string) => void, unitsOf?: (path: string, context: string) => string | undefined, holdMs?: number): HistoryApi.HistoryProvider;
|