signalk-questdb 1.4.2 → 1.5.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/README.md +29 -21
- package/dist/config/schema.d.ts +12 -0
- package/dist/config/schema.js +28 -0
- package/dist/config/schema.js.map +1 -1
- package/dist/ilp-writer.d.ts +8 -3
- package/dist/ilp-writer.js +52 -6
- package/dist/ilp-writer.js.map +1 -1
- package/dist/index.js +87 -5
- package/dist/index.js.map +1 -1
- package/dist/query-client.d.ts +1 -1
- package/dist/query-client.js +7 -2
- package/dist/query-client.js.map +1 -1
- package/dist/test/config.test.d.ts +1 -0
- package/dist/test/config.test.js +61 -0
- package/dist/test/config.test.js.map +1 -0
- package/dist/test/ilp-writer.test.js +138 -9
- package/dist/test/ilp-writer.test.js.map +1 -1
- package/package.json +1 -1
- package/public/805.js +1 -1
package/README.md
CHANGED
|
@@ -49,6 +49,13 @@ Three tables, all with WAL mode, daily partitioning, and deduplication:
|
|
|
49
49
|
| `signalk_str` | String values | `ts`, `path` (SYMBOL), `context` (SYMBOL), `value_str` (VARCHAR) |
|
|
50
50
|
| `signalk_position` | Positions | `ts`, `context` (SYMBOL), `lat` (DOUBLE), `lon` (DOUBLE) |
|
|
51
51
|
|
|
52
|
+
`ts` is the **server receive time**, not the timestamp a source claims. Marine
|
|
53
|
+
sources carry independent clocks, and storing their timestamps makes commits
|
|
54
|
+
land out of order — QuestDB then rewrites partition tails on every merge
|
|
55
|
+
(observed as >3000x write amplification). Receive time keeps ingestion
|
|
56
|
+
append-only; the millisecond difference is far below the sampling resolution,
|
|
57
|
+
and a device with a broken clock gets more accurate history, not less.
|
|
58
|
+
|
|
52
59
|
## History API
|
|
53
60
|
|
|
54
61
|
### v2 (REST -- `/signalk/v2/api/history/`)
|
|
@@ -106,26 +113,27 @@ wants to slice it into kopia-dedup-friendly shards. Allowed tables:
|
|
|
106
113
|
|
|
107
114
|
## Configuration
|
|
108
115
|
|
|
109
|
-
| Setting
|
|
110
|
-
|
|
|
111
|
-
| QuestDB version
|
|
112
|
-
| Managed container
|
|
113
|
-
| QuestDB host
|
|
114
|
-
| HTTP port
|
|
115
|
-
| ILP port
|
|
116
|
-
| PostgreSQL port
|
|
117
|
-
| Sampling rate (ms)
|
|
118
|
-
|
|
|
119
|
-
|
|
|
120
|
-
|
|
|
121
|
-
| Record
|
|
122
|
-
|
|
|
123
|
-
|
|
|
124
|
-
| Path filter
|
|
125
|
-
|
|
|
126
|
-
| Compression
|
|
127
|
-
|
|
|
128
|
-
|
|
|
116
|
+
| Setting | Default | Description |
|
|
117
|
+
| ------------------------- | ------------ | ----------------------------------------------------------------------------------------------------- |
|
|
118
|
+
| QuestDB version | `latest` | Docker image tag (dropdown shows stable + pre-releases) |
|
|
119
|
+
| Managed container | `true` | Let signalk-container manage QuestDB, or connect to external |
|
|
120
|
+
| QuestDB host | `127.0.0.1` | External QuestDB host (only used when managed=false) |
|
|
121
|
+
| HTTP port | `9000` | External mode, or the host binding when "Bind to 0.0.0.0" is on |
|
|
122
|
+
| ILP port | `9009` | External mode, or the host binding when "Bind to 0.0.0.0" is on |
|
|
123
|
+
| PostgreSQL port | `8812` | Host binding for Grafana/psql when "Bind to 0.0.0.0" is on |
|
|
124
|
+
| Sampling rate (ms) | `2000` | Default min ms between writes per path (0 = every update) |
|
|
125
|
+
| Write batch interval (ms) | `5000` | How often buffered samples are committed — one WAL transaction per table per commit (see Performance) |
|
|
126
|
+
| Memory limit | `768m` | Hard cgroup cap on QuestDB container RAM (empty = unlimited) |
|
|
127
|
+
| CPU limit (cores) | `1.5` | Max CPU cores QuestDB can use (0 = unlimited) |
|
|
128
|
+
| Record own vessel | `true` | Record self context |
|
|
129
|
+
| Record AIS targets | `false` | Record other vessels |
|
|
130
|
+
| Retention (days) | `0` | Auto-delete old partitions (0 = keep forever) |
|
|
131
|
+
| Path filter mode | `exclude` | `exclude` matching paths, or `include` only matching paths |
|
|
132
|
+
| Path filter paths | _(empty)_ | Glob patterns, one per line (e.g. `navigation.position`); empty = record everything |
|
|
133
|
+
| Compression codec | `lz4` | On-disk WAL compression: `none`, `lz4`, or `zstd` |
|
|
134
|
+
| Compression level | `3` | ZSTD level 1-22 (only when codec is zstd) |
|
|
135
|
+
| Container network | `sk-network` | Shared network for QuestDB (only applied when binding to 0.0.0.0) |
|
|
136
|
+
| Bind to 0.0.0.0 | `false` | Expose QuestDB's ports on the LAN (see Connectivity below) |
|
|
129
137
|
|
|
130
138
|
## Connectivity
|
|
131
139
|
|
|
@@ -178,7 +186,7 @@ The plugin is optimized for Raspberry Pi and similar low-power devices:
|
|
|
178
186
|
- **Default sampling rate** of 2000ms limits each path to 1 write per 2 seconds, keeping write volume modest on busy NMEA 2000 buses
|
|
179
187
|
- **Resource caps** of 768 MB RAM and 1.5 CPU cores (cgroup limits via signalk-container) keep QuestDB from squeezing co-resident containers like Grafana, mayara, or signalk-backup. The JVM auto-sizes its heap to a fraction of the memory cap, so total footprint (heap + off-heap) is bounded
|
|
180
188
|
- **QuestDB worker threads** reduced to 1 each (WAL, shared, ILP) to minimize CPU usage
|
|
181
|
-
- **ILP batching**
|
|
189
|
+
- **ILP batching** commits every 5s (configurable via "Write batch interval"; a large backlog of buffered rows commits early). Each commit is one WAL transaction per table, and with deduplicated tables the apply cost of a transaction grows with partition size — frequent tiny commits eventually outpace what a Pi can apply and recording stalls. Bigger batches keep the WAL healthy; the cost is that at most one interval of buffered samples is lost on a hard crash
|
|
182
190
|
- Per-path overrides allow faster rates for critical paths (e.g. `{ "environment.wind.*": 200 }`) while keeping slow-changing paths throttled
|
|
183
191
|
- Set the memory limit to empty or CPU limit to `0` to disable the cap entirely on roomier hosts
|
|
184
192
|
|
package/dist/config/schema.d.ts
CHANGED
|
@@ -13,6 +13,7 @@ export declare const ConfigSchema: import("@sinclair/typebox").TObject<{
|
|
|
13
13
|
paths: import("@sinclair/typebox").TArray<import("@sinclair/typebox").TString>;
|
|
14
14
|
}>;
|
|
15
15
|
defaultSamplingRate: import("@sinclair/typebox").TNumber;
|
|
16
|
+
ilpFlushIntervalMs: import("@sinclair/typebox").TNumber;
|
|
16
17
|
samplingRates: import("@sinclair/typebox").TRecord<import("@sinclair/typebox").TString, import("@sinclair/typebox").TNumber>;
|
|
17
18
|
recordSelf: import("@sinclair/typebox").TBoolean;
|
|
18
19
|
recordOthers: import("@sinclair/typebox").TBoolean;
|
|
@@ -23,3 +24,14 @@ export declare const ConfigSchema: import("@sinclair/typebox").TObject<{
|
|
|
23
24
|
compressionLevel: import("@sinclair/typebox").TNumber;
|
|
24
25
|
}>;
|
|
25
26
|
export type Config = Static<typeof ConfigSchema>;
|
|
27
|
+
/**
|
|
28
|
+
* Fill in config keys that older saved configs may lack. Signal K hands the
|
|
29
|
+
* plugin its stored configuration verbatim — TypeBox defaults are only
|
|
30
|
+
* applied by the Admin UI form, so a config saved before an option existed
|
|
31
|
+
* (or hand-edited) simply misses the key. The delta pipeline dereferences
|
|
32
|
+
* `pathFilter.paths` and `Object.entries(samplingRates)` on every delta, so
|
|
33
|
+
* a missing key crashed the stream subscription and recording silently
|
|
34
|
+
* stopped. Normalize once at the config boundary instead of guarding every
|
|
35
|
+
* consumer.
|
|
36
|
+
*/
|
|
37
|
+
export declare function normalizeConfig(config: Config): Config;
|
package/dist/config/schema.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.ConfigSchema = void 0;
|
|
4
|
+
exports.normalizeConfig = normalizeConfig;
|
|
4
5
|
const typebox_1 = require("@sinclair/typebox");
|
|
5
6
|
exports.ConfigSchema = typebox_1.Type.Object({
|
|
6
7
|
questdbHost: typebox_1.Type.String({
|
|
@@ -58,6 +59,13 @@ exports.ConfigSchema = typebox_1.Type.Object({
|
|
|
58
59
|
title: "Default sampling rate (ms)",
|
|
59
60
|
description: "Minimum ms between writes for any path (0 = write every update). 2000ms is a sensible default for Pi/Cerbo-class hardware; lower it per-path via samplingRates when you need finer resolution.",
|
|
60
61
|
}),
|
|
62
|
+
ilpFlushIntervalMs: typebox_1.Type.Number({
|
|
63
|
+
default: 5000,
|
|
64
|
+
minimum: 500,
|
|
65
|
+
maximum: 60000,
|
|
66
|
+
title: "Write batch interval (ms)",
|
|
67
|
+
description: "How often buffered samples are committed to QuestDB. Each commit is one WAL transaction per table, so short intervals create huge numbers of tiny transactions whose apply cost eventually stalls recording on Pi-class hardware — QuestDB recommends >100 rows per transaction. Longer intervals mean at most this many ms of buffered data is lost on a hard crash.",
|
|
68
|
+
}),
|
|
61
69
|
samplingRates: typebox_1.Type.Record(typebox_1.Type.String(), typebox_1.Type.Number(), {
|
|
62
70
|
default: {},
|
|
63
71
|
title: "Per-path sampling rates (ms)",
|
|
@@ -96,4 +104,24 @@ exports.ConfigSchema = typebox_1.Type.Object({
|
|
|
96
104
|
description: "zstd: 1-22 (default 3), lz4: ignored",
|
|
97
105
|
}),
|
|
98
106
|
});
|
|
107
|
+
/**
|
|
108
|
+
* Fill in config keys that older saved configs may lack. Signal K hands the
|
|
109
|
+
* plugin its stored configuration verbatim — TypeBox defaults are only
|
|
110
|
+
* applied by the Admin UI form, so a config saved before an option existed
|
|
111
|
+
* (or hand-edited) simply misses the key. The delta pipeline dereferences
|
|
112
|
+
* `pathFilter.paths` and `Object.entries(samplingRates)` on every delta, so
|
|
113
|
+
* a missing key crashed the stream subscription and recording silently
|
|
114
|
+
* stopped. Normalize once at the config boundary instead of guarding every
|
|
115
|
+
* consumer.
|
|
116
|
+
*/
|
|
117
|
+
function normalizeConfig(config) {
|
|
118
|
+
return {
|
|
119
|
+
...config,
|
|
120
|
+
pathFilter: {
|
|
121
|
+
mode: config.pathFilter?.mode ?? "exclude",
|
|
122
|
+
paths: config.pathFilter?.paths ?? [],
|
|
123
|
+
},
|
|
124
|
+
samplingRates: config.samplingRates ?? {},
|
|
125
|
+
};
|
|
126
|
+
}
|
|
99
127
|
//# sourceMappingURL=schema.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"schema.js","sourceRoot":"","sources":["../../src/config/schema.ts"],"names":[],"mappings":";;;
|
|
1
|
+
{"version":3,"file":"schema.js","sourceRoot":"","sources":["../../src/config/schema.ts"],"names":[],"mappings":"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"}
|
package/dist/ilp-writer.d.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
export declare const DEFAULT_FLUSH_INTERVAL_MS = 5000;
|
|
1
2
|
export declare class ILPWriter {
|
|
2
3
|
private host;
|
|
3
4
|
private port;
|
|
@@ -10,6 +11,7 @@ export declare class ILPWriter {
|
|
|
10
11
|
private connected;
|
|
11
12
|
private connecting;
|
|
12
13
|
private stopped;
|
|
14
|
+
private lastNanos;
|
|
13
15
|
private connectedAt;
|
|
14
16
|
private consecutiveFlaps;
|
|
15
17
|
private droppedLines;
|
|
@@ -21,9 +23,11 @@ export declare class ILPWriter {
|
|
|
21
23
|
private readonly maxReconnectDelay;
|
|
22
24
|
private readonly stableConnectionMs;
|
|
23
25
|
private readonly unhealthyAfterFlaps;
|
|
26
|
+
private readonly flushIntervalMs;
|
|
24
27
|
constructor(host: string, port: number, debug?: (msg: string) => void, callbacks?: {
|
|
25
28
|
onUnhealthy?: (msg: string) => void;
|
|
26
29
|
onHealthy?: () => void;
|
|
30
|
+
flushIntervalMs?: number;
|
|
27
31
|
timing?: {
|
|
28
32
|
initialReconnectDelay?: number;
|
|
29
33
|
maxReconnectDelay?: number;
|
|
@@ -37,12 +41,13 @@ export declare class ILPWriter {
|
|
|
37
41
|
private markUnhealthy;
|
|
38
42
|
private markHealthy;
|
|
39
43
|
private scheduleReconnect;
|
|
40
|
-
|
|
41
|
-
|
|
44
|
+
private nextNanos;
|
|
45
|
+
write(path: string, context: string, value: number, timestamp?: Date): void;
|
|
46
|
+
writeString(path: string, context: string, value: string, timestamp?: Date): void;
|
|
42
47
|
writePosition(path: string, context: string, position: {
|
|
43
48
|
latitude: number;
|
|
44
49
|
longitude: number;
|
|
45
|
-
}, timestamp
|
|
50
|
+
}, timestamp?: Date): void;
|
|
46
51
|
private enqueue;
|
|
47
52
|
private requeueFront;
|
|
48
53
|
private enforceBufferCap;
|
package/dist/ilp-writer.js
CHANGED
|
@@ -33,9 +33,18 @@ var __importStar = (this && this.__importStar) || (function () {
|
|
|
33
33
|
};
|
|
34
34
|
})();
|
|
35
35
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
-
exports.ILPWriter = void 0;
|
|
36
|
+
exports.ILPWriter = exports.DEFAULT_FLUSH_INTERVAL_MS = void 0;
|
|
37
37
|
const net = __importStar(require("net"));
|
|
38
|
-
|
|
38
|
+
// Every flush that carries lines becomes one WAL transaction per touched
|
|
39
|
+
// table, and with DEDUP tables each transaction pays a dedup pass on apply
|
|
40
|
+
// whose cost grows with partition size. Flushing twice a second produced
|
|
41
|
+
// ~173K tiny (~30-row) transactions per table per day in the field — enough
|
|
42
|
+
// that by day two the WAL apply on a Pi could no longer keep up with the
|
|
43
|
+
// commit rate and tables stalled. Fewer, larger transactions are dramatically
|
|
44
|
+
// cheaper to apply; QuestDB's own guidance is >100 rows per transaction. The
|
|
45
|
+
// trade-off is bounded staleness: at most this many ms of buffered samples
|
|
46
|
+
// are lost on a hard crash (a normal stop flushes first).
|
|
47
|
+
exports.DEFAULT_FLUSH_INTERVAL_MS = 5000;
|
|
39
48
|
const FLUSH_BATCH_SIZE = 1000;
|
|
40
49
|
const INITIAL_RECONNECT_DELAY_MS = 1000;
|
|
41
50
|
const MAX_RECONNECT_DELAY_MS = 30000;
|
|
@@ -76,6 +85,17 @@ class ILPWriter {
|
|
|
76
85
|
connected = false;
|
|
77
86
|
connecting = false;
|
|
78
87
|
stopped = false;
|
|
88
|
+
// Last timestamp handed out, in nanoseconds. Rows are stamped at write time
|
|
89
|
+
// with the server clock (see the delta handler), but `Date` is only
|
|
90
|
+
// millisecond-resolution while the `signalk`/`signalk_str` tables dedup on
|
|
91
|
+
// KEYS(ts, path, context). Two writes for the same path within one
|
|
92
|
+
// millisecond would collide and the later would upsert over the earlier —
|
|
93
|
+
// silent data loss for unthrottled (samplingRate 0) or bursty paths. QuestDB
|
|
94
|
+
// stores microsecond resolution, so advancing this counter by at least 1µs
|
|
95
|
+
// per write keeps every row's `ts` distinct without moving the visible
|
|
96
|
+
// millisecond. It only ratchets forward: a same/earlier wall-clock reading
|
|
97
|
+
// is bumped past the last one, so ingestion also stays strictly ordered.
|
|
98
|
+
lastNanos = 0n;
|
|
79
99
|
// Wall-clock ms when the current socket's connect callback fired. Read in the
|
|
80
100
|
// `close` handler to decide whether the connection was stable (reset backoff)
|
|
81
101
|
// or an instant flap (grow backoff). Reset to 0 on every close.
|
|
@@ -99,12 +119,18 @@ class ILPWriter {
|
|
|
99
119
|
maxReconnectDelay;
|
|
100
120
|
stableConnectionMs;
|
|
101
121
|
unhealthyAfterFlaps;
|
|
122
|
+
// How often buffered lines are committed. Unlike `timing`, this is a real
|
|
123
|
+
// production knob (wired to the plugin's ilpFlushIntervalMs config) — see
|
|
124
|
+
// the DEFAULT_FLUSH_INTERVAL_MS comment for the batching rationale.
|
|
125
|
+
flushIntervalMs;
|
|
102
126
|
constructor(host, port, debug, callbacks) {
|
|
103
127
|
this.host = host;
|
|
104
128
|
this.port = port;
|
|
105
129
|
this.debug = debug ?? (() => { });
|
|
106
130
|
this.onUnhealthy = callbacks?.onUnhealthy ?? (() => { });
|
|
107
131
|
this.onHealthy = callbacks?.onHealthy ?? (() => { });
|
|
132
|
+
this.flushIntervalMs =
|
|
133
|
+
callbacks?.flushIntervalMs ?? exports.DEFAULT_FLUSH_INTERVAL_MS;
|
|
108
134
|
const t = callbacks?.timing ?? {};
|
|
109
135
|
this.initialReconnectDelay =
|
|
110
136
|
t.initialReconnectDelay ?? INITIAL_RECONNECT_DELAY_MS;
|
|
@@ -231,16 +257,36 @@ class ILPWriter {
|
|
|
231
257
|
});
|
|
232
258
|
}, this.reconnectDelay);
|
|
233
259
|
}
|
|
260
|
+
// Strictly-increasing nanosecond timestamp for the next row. Starts from the
|
|
261
|
+
// server clock but never repeats or goes backwards within one writer, so
|
|
262
|
+
// same-millisecond writes to a dedup table don't collide (see `lastNanos`).
|
|
263
|
+
// An explicit `Date` (used by tests that assert exact ILP lines) is honoured
|
|
264
|
+
// verbatim, but still advances the floor so it can't collide with a later
|
|
265
|
+
// omitted-timestamp write.
|
|
266
|
+
nextNanos(explicit) {
|
|
267
|
+
if (explicit) {
|
|
268
|
+
const explicitNanos = BigInt(explicit.getTime()) * 1000000n;
|
|
269
|
+
if (explicitNanos > this.lastNanos)
|
|
270
|
+
this.lastNanos = explicitNanos;
|
|
271
|
+
return explicitNanos;
|
|
272
|
+
}
|
|
273
|
+
const nowNanos = BigInt(Date.now()) * 1000000n;
|
|
274
|
+
// Advance by at least 1µs (QuestDB's storage resolution) past the last
|
|
275
|
+
// value so the microsecond QuestDB persists is always distinct.
|
|
276
|
+
this.lastNanos =
|
|
277
|
+
nowNanos > this.lastNanos ? nowNanos : this.lastNanos + 1000n;
|
|
278
|
+
return this.lastNanos;
|
|
279
|
+
}
|
|
234
280
|
write(path, context, value, timestamp) {
|
|
235
|
-
const ts =
|
|
281
|
+
const ts = this.nextNanos(timestamp);
|
|
236
282
|
this.enqueue(`signalk,path=${escapeTag(path)},context=${escapeTag(context)} value=${value} ${ts}\n`);
|
|
237
283
|
}
|
|
238
284
|
writeString(path, context, value, timestamp) {
|
|
239
|
-
const ts =
|
|
285
|
+
const ts = this.nextNanos(timestamp);
|
|
240
286
|
this.enqueue(`signalk_str,path=${escapeTag(path)},context=${escapeTag(context)} value_str="${escapeFieldString(value)}" ${ts}\n`);
|
|
241
287
|
}
|
|
242
288
|
writePosition(path, context, position, timestamp) {
|
|
243
|
-
const ts =
|
|
289
|
+
const ts = this.nextNanos(timestamp);
|
|
244
290
|
this.enqueue(`signalk_position,context=${escapeTag(context)} lat=${position.latitude},lon=${position.longitude} ${ts}\n`);
|
|
245
291
|
}
|
|
246
292
|
enqueue(line) {
|
|
@@ -298,7 +344,7 @@ class ILPWriter {
|
|
|
298
344
|
}
|
|
299
345
|
startFlushTimer() {
|
|
300
346
|
this.stopFlushTimer();
|
|
301
|
-
this.flushTimer = setInterval(() => this.flush(),
|
|
347
|
+
this.flushTimer = setInterval(() => this.flush(), this.flushIntervalMs);
|
|
302
348
|
}
|
|
303
349
|
stopFlushTimer() {
|
|
304
350
|
if (this.flushTimer) {
|
package/dist/ilp-writer.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ilp-writer.js","sourceRoot":"","sources":["../src/ilp-writer.ts"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"ilp-writer.js","sourceRoot":"","sources":["../src/ilp-writer.ts"],"names":[],"mappings":"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"}
|
package/dist/index.js
CHANGED
|
@@ -359,6 +359,10 @@ module.exports = (app) => {
|
|
|
359
359
|
}
|
|
360
360
|
}
|
|
361
361
|
async function runStartInner(config, signal) {
|
|
362
|
+
// Older saved configs miss keys added since (pathFilter, samplingRates);
|
|
363
|
+
// the per-delta pipeline dereferences them per delta, so normalize once
|
|
364
|
+
// at the boundary.
|
|
365
|
+
config = (0, schema_1.normalizeConfig)(config);
|
|
362
366
|
currentConfig = config;
|
|
363
367
|
// A start may connect to a different QuestDB than the previous run —
|
|
364
368
|
// notably external mode repointed at a new host/build — so drop any cached
|
|
@@ -501,6 +505,7 @@ module.exports = (app) => {
|
|
|
501
505
|
// stuck on a cheerful "Recording" while every sample is dropped.
|
|
502
506
|
onUnhealthy: (msg) => app.setPluginError(msg),
|
|
503
507
|
onHealthy: () => app.setPluginStatus(`Recording to QuestDB at ${ilpHost}:${ilpPort}`),
|
|
508
|
+
flushIntervalMs: config.ilpFlushIntervalMs,
|
|
504
509
|
});
|
|
505
510
|
await writer.connect();
|
|
506
511
|
// Last abort checkpoint before we register providers, the stream
|
|
@@ -537,7 +542,7 @@ module.exports = (app) => {
|
|
|
537
542
|
const unsub = bus.onValue((delta) => {
|
|
538
543
|
if (!writer)
|
|
539
544
|
return;
|
|
540
|
-
const { path, value, context
|
|
545
|
+
const { path, value, context } = delta;
|
|
541
546
|
if (!path || value === undefined || value === null)
|
|
542
547
|
return;
|
|
543
548
|
const isSelf = context === app.selfContext;
|
|
@@ -549,19 +554,30 @@ module.exports = (app) => {
|
|
|
549
554
|
return;
|
|
550
555
|
if (isThrottled(path, config.samplingRates, config.defaultSamplingRate ?? 2000))
|
|
551
556
|
return;
|
|
552
|
-
|
|
557
|
+
// Rows are stamped with the server receive time, deliberately NOT the
|
|
558
|
+
// delta's own timestamp: a boat is a set of independent clocks (GPS
|
|
559
|
+
// time, RTC-less devices, gateway latencies), and storing per-source
|
|
560
|
+
// timestamps makes commits land out of order. QuestDB then rewrites
|
|
561
|
+
// partition tails on every merge — observed in the field as >3000x
|
|
562
|
+
// write amplification (physical rows rewritten per row inserted),
|
|
563
|
+
// grinding SD cards and, before batching, stalling the WAL outright.
|
|
564
|
+
// The writer assigns the actual timestamp (strictly monotonic to the
|
|
565
|
+
// microsecond, so same-millisecond writes don't collide on the dedup
|
|
566
|
+
// key), so every commit is a pure append. A device with a broken clock
|
|
567
|
+
// even gets *more* accurate history. Re-sent ILP batches keep their
|
|
568
|
+
// original stamps (baked at write() time), so replay-idempotency holds.
|
|
553
569
|
const ctx = isSelf ? "self" : context;
|
|
554
570
|
if (typeof value === "number") {
|
|
555
|
-
writer.write(path, ctx, value
|
|
571
|
+
writer.write(path, ctx, value);
|
|
556
572
|
}
|
|
557
573
|
else if (typeof value === "string") {
|
|
558
|
-
writer.writeString(path, ctx, value
|
|
574
|
+
writer.writeString(path, ctx, value);
|
|
559
575
|
}
|
|
560
576
|
else if (value &&
|
|
561
577
|
typeof value === "object" &&
|
|
562
578
|
"latitude" in value &&
|
|
563
579
|
"longitude" in value) {
|
|
564
|
-
writer.writePosition(path, ctx, value
|
|
580
|
+
writer.writePosition(path, ctx, value);
|
|
565
581
|
}
|
|
566
582
|
});
|
|
567
583
|
unsubscribes.push(unsub);
|
|
@@ -982,6 +998,72 @@ module.exports = (app) => {
|
|
|
982
998
|
// Signal K's own plugin-uninstall can't delete the data dir on rootless
|
|
983
999
|
// Podman (the container writes files as a subuid the SK user can't
|
|
984
1000
|
// remove); removeManagedData wipes them from inside the userns.
|
|
1001
|
+
// Lossless recovery for a suspended WAL: one plain `RESUME WAL` per
|
|
1002
|
+
// suspended plugin table. Deliberately a SINGLE attempt each, no
|
|
1003
|
+
// retries — QuestDB's own HTTP console parks a busy ALTER and retries
|
|
1004
|
+
// it server-side once per second indefinitely, and several console
|
|
1005
|
+
// tabs doing that produced a self-sustaining retry storm in the field.
|
|
1006
|
+
// A busy/failed table is reported to the caller instead. `ALTER` is
|
|
1007
|
+
// DDL, so this cannot go through /api/query (isReadOnlySQL blocks it).
|
|
1008
|
+
//
|
|
1009
|
+
// No lifecycle lock: the resumes mutate no plugin-side state and are
|
|
1010
|
+
// idempotent, so racing a concurrent start/update/purge at worst
|
|
1011
|
+
// yields a per-table error the caller sees — while taking the lock
|
|
1012
|
+
// could park this request behind a wedged start forever.
|
|
1013
|
+
router.post("/api/resume-wal", async (_req, res) => {
|
|
1014
|
+
try {
|
|
1015
|
+
const client = queryClient;
|
|
1016
|
+
if (!client) {
|
|
1017
|
+
res
|
|
1018
|
+
.status(503)
|
|
1019
|
+
.json({ error: "QuestDB connection not initialized" });
|
|
1020
|
+
return;
|
|
1021
|
+
}
|
|
1022
|
+
const walResult = await client.exec("SELECT name, writerTxn, sequencerTxn FROM wal_tables() WHERE suspended = true", 10_000);
|
|
1023
|
+
const suspended = client
|
|
1024
|
+
.toObjects(walResult)
|
|
1025
|
+
.filter((row) => FULL_EXPORT_TABLE_SET.has(String(row.name)));
|
|
1026
|
+
const results = [];
|
|
1027
|
+
for (const row of suspended) {
|
|
1028
|
+
const table = String(row.name);
|
|
1029
|
+
const base = {
|
|
1030
|
+
table,
|
|
1031
|
+
writerTxn: Number(row.writerTxn ?? 0),
|
|
1032
|
+
sequencerTxn: Number(row.sequencerTxn ?? 0),
|
|
1033
|
+
};
|
|
1034
|
+
try {
|
|
1035
|
+
// Plain RESUME WAL replays from the next unapplied txn — it
|
|
1036
|
+
// skips nothing, so no data is lost. 10s deadline: a busy
|
|
1037
|
+
// table parks the request server-side (see above), and one
|
|
1038
|
+
// bounded attempt that reports "busy" beats waiting.
|
|
1039
|
+
await client.exec(`ALTER TABLE "${table.replace(/"/g, '""')}" RESUME WAL`, 10_000);
|
|
1040
|
+
results.push({ ...base, ok: true });
|
|
1041
|
+
}
|
|
1042
|
+
catch (err) {
|
|
1043
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
1044
|
+
results.push({
|
|
1045
|
+
...base,
|
|
1046
|
+
ok: false,
|
|
1047
|
+
error: /abort|timeout/i.test(msg)
|
|
1048
|
+
? "table busy — the writer is held (likely by the WAL apply job or a stuck operation); if this persists, restart the QuestDB container"
|
|
1049
|
+
: msg,
|
|
1050
|
+
});
|
|
1051
|
+
}
|
|
1052
|
+
}
|
|
1053
|
+
res.json({
|
|
1054
|
+
results,
|
|
1055
|
+
resumed: results.filter((r) => r.ok).length,
|
|
1056
|
+
message: results.length === 0
|
|
1057
|
+
? "No suspended tables found"
|
|
1058
|
+
: `Resumed ${results.filter((r) => r.ok).length} of ${results.length} suspended table(s)`,
|
|
1059
|
+
});
|
|
1060
|
+
}
|
|
1061
|
+
catch (err) {
|
|
1062
|
+
res.status(500).json({
|
|
1063
|
+
error: err instanceof Error ? err.message : String(err),
|
|
1064
|
+
});
|
|
1065
|
+
}
|
|
1066
|
+
});
|
|
985
1067
|
router.post("/api/purge-data", async (_req, res) => {
|
|
986
1068
|
try {
|
|
987
1069
|
// External mode is a config fact independent of the runtime, so
|