signalk-questdb 2.1.4 → 2.1.5
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 +112 -27
- package/dist/api-contract.d.ts +45 -0
- package/dist/configpanel/responses.d.ts +28 -1
- package/dist/configpanel/responses.js +74 -0
- package/dist/configpanel/responses.js.map +1 -1
- package/dist/ilp-writer.d.ts +52 -0
- package/dist/ilp-writer.js +88 -7
- package/dist/ilp-writer.js.map +1 -1
- package/dist/index.js +477 -45
- package/dist/index.js.map +1 -1
- package/dist/migration-checkpoint.d.ts +170 -0
- package/dist/migration-checkpoint.js +276 -0
- package/dist/migration-checkpoint.js.map +1 -0
- package/dist/migration-cleanup.d.ts +48 -0
- package/dist/migration-cleanup.js +155 -0
- package/dist/migration-cleanup.js.map +1 -0
- package/dist/migration.d.ts +134 -17
- package/dist/migration.js +694 -158
- package/dist/migration.js.map +1 -1
- package/dist/questdb-mount.d.ts +106 -0
- package/dist/questdb-mount.js +173 -0
- package/dist/questdb-mount.js.map +1 -0
- package/dist/restore.js +44 -8
- package/dist/restore.js.map +1 -1
- package/package.json +2 -2
- package/public/assets/AppPanel-p27aQlBS.js +1 -0
- package/public/assets/PluginConfigurationPanel-BO_4OxBo.js +9 -0
- package/public/assets/_virtual_mf-localSharedImportMap___mfe_internal__signalk_questdb__mf_owner__1-BiBqFhVp.js +1 -0
- package/public/assets/_virtual_mf___mfe_internal__signalk_questdb__mf_owner__275086164343916__loadShare__react__loadShare__.js-BWO9MGdR.js +1 -0
- package/public/assets/_virtual_mf___mfe_internal__signalk_questdb__mf_owner__275086164343916__prebuild__react__prebuild__-D2g10OtU.js +1 -0
- package/public/assets/dist-BK97MX24.js +1 -0
- package/public/assets/helpers-Bizl8Raj.js +1 -0
- package/public/assets/hostInit-BFZIDSUI.js +1 -0
- package/public/assets/index-bERRzqQZ.js +2 -0
- package/public/assets/pendingShares-BS4kO2ec.js +2 -0
- package/public/assets/{dist-BgxWXJ5y.js → utils-jR9fY54i.js} +2 -2
- package/public/assets/{vite-preload-helper-HclGiUj8.js → vite-preload-helper-uBIymjUX.js} +1 -1
- package/public/remoteEntry.js +2 -2
- package/public/assets/AppPanel-CNFtJpeq.js +0 -1
- package/public/assets/PluginConfigurationPanel-CR4mD9IH.js +0 -9
- package/public/assets/_virtual_mf-localSharedImportMap___mfe_internal__signalk_questdb__mf_owner__1-7tb3Q-Bx.js +0 -1
- package/public/assets/_virtual_mf___mfe_internal__signalk_questdb__mf_owner__1__loadShare__react__loadShare__.js-DzhF4wXp.js +0 -1
- package/public/assets/hostInit-CyS9_M1c.js +0 -1
- package/public/assets/index-Dh-t5VMR.js +0 -1
- package/public/assets/virtualExposes-BBIkbTtD.js +0 -1
|
@@ -0,0 +1,276 @@
|
|
|
1
|
+
// Where an InfluxDB import has got to, kept on disk so it survives a restart.
|
|
2
|
+
//
|
|
3
|
+
// An import runs for hours, measurement by measurement, and a re-run begins
|
|
4
|
+
// again at the first one. Interrupted at the same point each time, it never
|
|
5
|
+
// reaches the measurements late in the alphabet — `navigation.position` among
|
|
6
|
+
// them. The checkpoint is what lets a re-run pick up where the last one stopped.
|
|
7
|
+
//
|
|
8
|
+
// The one rule it must never break: a checkpoint may not run ahead of the
|
|
9
|
+
// data. Rows the importer has "written" sit in the ILP writer's buffer, then in
|
|
10
|
+
// the socket, then in QuestDB's page cache, before they are on disk. A
|
|
11
|
+
// checkpoint claiming them done while they are still in any of those would,
|
|
12
|
+
// after a crash, make the resumed run skip rows that were never stored — a
|
|
13
|
+
// silent hole in the history. CheckpointTracker is the guard against that.
|
|
14
|
+
import { mkdir, open, readFile, rename, rm } from "node:fs/promises";
|
|
15
|
+
import path from "node:path";
|
|
16
|
+
export function migrationIdentity(req, windowMs) {
|
|
17
|
+
return {
|
|
18
|
+
url: req.url,
|
|
19
|
+
type: req.type,
|
|
20
|
+
bucket: req.bucket,
|
|
21
|
+
// Normalised, so the same instant written two ways is the same import.
|
|
22
|
+
from: new Date(req.from).toISOString(),
|
|
23
|
+
to: new Date(req.to).toISOString(),
|
|
24
|
+
context: req.context,
|
|
25
|
+
sourceSelfContext: req.sourceSelfContext,
|
|
26
|
+
others: req.others ?? "keep",
|
|
27
|
+
sourceLabel: req.sourceLabel ?? "influxdb-import",
|
|
28
|
+
measurements: req.measurements && req.measurements.length > 0
|
|
29
|
+
? [...req.measurements].sort()
|
|
30
|
+
: undefined,
|
|
31
|
+
windowMs,
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
export function sameIdentity(a, b) {
|
|
35
|
+
return (a.url === b.url &&
|
|
36
|
+
a.type === b.type &&
|
|
37
|
+
a.bucket === b.bucket &&
|
|
38
|
+
a.from === b.from &&
|
|
39
|
+
a.to === b.to &&
|
|
40
|
+
a.context === b.context &&
|
|
41
|
+
a.sourceSelfContext === b.sourceSelfContext &&
|
|
42
|
+
(a.others ?? "keep") === (b.others ?? "keep") &&
|
|
43
|
+
a.sourceLabel === b.sourceLabel &&
|
|
44
|
+
a.windowMs === b.windowMs &&
|
|
45
|
+
JSON.stringify(a.measurements ?? null) ===
|
|
46
|
+
JSON.stringify(b.measurements ?? null));
|
|
47
|
+
}
|
|
48
|
+
/** The checkpoint of the one import this plugin runs at a time, as a file. */
|
|
49
|
+
export class FileCheckpointStore {
|
|
50
|
+
file;
|
|
51
|
+
constructor(file) {
|
|
52
|
+
this.file = file;
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* The stored checkpoint, or null. A file that cannot be read or does not
|
|
56
|
+
* hold a checkpoint is treated as absent: the cost is an import starting
|
|
57
|
+
* over, which is always safe, where trusting a damaged one is not.
|
|
58
|
+
*/
|
|
59
|
+
async load() {
|
|
60
|
+
let text;
|
|
61
|
+
try {
|
|
62
|
+
text = await readFile(this.file, "utf8");
|
|
63
|
+
}
|
|
64
|
+
catch {
|
|
65
|
+
return null;
|
|
66
|
+
}
|
|
67
|
+
try {
|
|
68
|
+
const parsed = JSON.parse(text);
|
|
69
|
+
return isCheckpoint(parsed) ? parsed : null;
|
|
70
|
+
}
|
|
71
|
+
catch {
|
|
72
|
+
return null;
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* Written beside the target, synced, and renamed over it, so a crash or a
|
|
77
|
+
* power cut leaves either the previous checkpoint or the new one — never a
|
|
78
|
+
* truncated one. The directory is synced too, so the rename itself is on
|
|
79
|
+
* disk; where a directory cannot be opened for that, the rename is left to
|
|
80
|
+
* the file system's own ordering.
|
|
81
|
+
*/
|
|
82
|
+
async save(checkpoint) {
|
|
83
|
+
const dir = path.dirname(this.file);
|
|
84
|
+
await mkdir(dir, { recursive: true });
|
|
85
|
+
const tmp = `${this.file}.tmp`;
|
|
86
|
+
const fh = await open(tmp, "w");
|
|
87
|
+
try {
|
|
88
|
+
await fh.writeFile(JSON.stringify(checkpoint), "utf8");
|
|
89
|
+
await fh.sync();
|
|
90
|
+
}
|
|
91
|
+
finally {
|
|
92
|
+
await fh.close();
|
|
93
|
+
}
|
|
94
|
+
await rename(tmp, this.file);
|
|
95
|
+
await syncDirectory(dir);
|
|
96
|
+
}
|
|
97
|
+
/** Synced like a save: a cleared checkpoint must not come back after a power cut. */
|
|
98
|
+
async clear() {
|
|
99
|
+
await rm(this.file, { force: true });
|
|
100
|
+
await syncDirectory(path.dirname(this.file));
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
/**
|
|
104
|
+
* Put a directory's entries on disk, so a rename or removal in it survives a
|
|
105
|
+
* power cut. A directory that does not exist has nothing to sync, and not
|
|
106
|
+
* every platform lets one be opened and synced; any other failure is a real
|
|
107
|
+
* one and is not hidden.
|
|
108
|
+
*/
|
|
109
|
+
async function syncDirectory(dir) {
|
|
110
|
+
try {
|
|
111
|
+
const dh = await open(dir, "r");
|
|
112
|
+
try {
|
|
113
|
+
await dh.sync();
|
|
114
|
+
}
|
|
115
|
+
finally {
|
|
116
|
+
await dh.close();
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
catch (err) {
|
|
120
|
+
const code = typeof err === "object" &&
|
|
121
|
+
err !== null &&
|
|
122
|
+
"code" in err &&
|
|
123
|
+
typeof err.code === "string"
|
|
124
|
+
? err.code
|
|
125
|
+
: "";
|
|
126
|
+
if (code !== "ENOENT" && !DIRECTORY_SYNC_UNSUPPORTED.has(code))
|
|
127
|
+
throw err;
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
/**
|
|
131
|
+
* Errors that mean "this platform or file system does not sync directories":
|
|
132
|
+
* Windows refuses to open one, and some file systems refuse to fsync one.
|
|
133
|
+
*/
|
|
134
|
+
const DIRECTORY_SYNC_UNSUPPORTED = new Set([
|
|
135
|
+
"EISDIR",
|
|
136
|
+
"EPERM",
|
|
137
|
+
"EINVAL",
|
|
138
|
+
"ENOTSUP",
|
|
139
|
+
"EOPNOTSUPP",
|
|
140
|
+
]);
|
|
141
|
+
function isCheckpoint(value) {
|
|
142
|
+
if (typeof value !== "object" || value === null)
|
|
143
|
+
return false;
|
|
144
|
+
const cp = value;
|
|
145
|
+
const id = cp.identity;
|
|
146
|
+
const progress = cp.progress;
|
|
147
|
+
return (cp.version === 1 &&
|
|
148
|
+
typeof id === "object" &&
|
|
149
|
+
id !== null &&
|
|
150
|
+
typeof id.url === "string" &&
|
|
151
|
+
typeof id.type === "string" &&
|
|
152
|
+
typeof id.bucket === "string" &&
|
|
153
|
+
typeof id.from === "string" &&
|
|
154
|
+
typeof id.to === "string" &&
|
|
155
|
+
typeof id.context === "string" &&
|
|
156
|
+
(id.sourceSelfContext === undefined ||
|
|
157
|
+
typeof id.sourceSelfContext === "string") &&
|
|
158
|
+
(id.others === undefined || id.others === "keep" || id.others === "skip") &&
|
|
159
|
+
typeof id.sourceLabel === "string" &&
|
|
160
|
+
typeof id.windowMs === "number" &&
|
|
161
|
+
(id.measurements === undefined ||
|
|
162
|
+
(Array.isArray(id.measurements) &&
|
|
163
|
+
id.measurements.every((m) => typeof m === "string"))) &&
|
|
164
|
+
Array.isArray(cp.done) &&
|
|
165
|
+
cp.done.every((m) => typeof m === "string") &&
|
|
166
|
+
(cp.current === undefined ||
|
|
167
|
+
(typeof cp.current === "object" &&
|
|
168
|
+
cp.current !== null &&
|
|
169
|
+
typeof cp.current.measurement === "string" &&
|
|
170
|
+
typeof cp.current.windowStart === "number")) &&
|
|
171
|
+
typeof progress === "object" &&
|
|
172
|
+
progress !== null &&
|
|
173
|
+
typeof progress.read === "number" &&
|
|
174
|
+
typeof progress.written === "number" &&
|
|
175
|
+
typeof progress.skipped === "number" &&
|
|
176
|
+
typeof cp.updatedAt === "string");
|
|
177
|
+
}
|
|
178
|
+
/**
|
|
179
|
+
* How old a position must be before it is persisted.
|
|
180
|
+
*
|
|
181
|
+
* Leaving the writer is not the same as being stored. QuestDB takes ILP rows
|
|
182
|
+
* into memory and commits them on an interval of seconds, so a crash of either
|
|
183
|
+
* process loses the rows of the last interval. The managed container runs
|
|
184
|
+
* QuestDB with cairo.commit.mode=sync, which puts a commit on disk; an external
|
|
185
|
+
* one may be on QuestDB's default nosync, where what survives a power cut is
|
|
186
|
+
* what the kernel had already written back — with default settings anything
|
|
187
|
+
* older than about 35 s (30 s dirty-page expiry plus the 5 s flusher period).
|
|
188
|
+
* A minute clears both with room to spare, on the assumption of those
|
|
189
|
+
* defaults: an external QuestDB is the operator's, and the README asks them to
|
|
190
|
+
* run it with sync, which is the real answer for a power cut. The price is
|
|
191
|
+
* that a resumed import redoes up to a minute of work, which costs nothing but
|
|
192
|
+
* the time: rows upsert.
|
|
193
|
+
*/
|
|
194
|
+
export const CHECKPOINT_LAG_MS = 60_000;
|
|
195
|
+
/**
|
|
196
|
+
* Persists positions only once they are safe to resume from.
|
|
197
|
+
*
|
|
198
|
+
* A position offered now is held as the candidate. It is written later, by a
|
|
199
|
+
* subsequent offer, once three things hold: every line enqueued up to it has
|
|
200
|
+
* left the writer, it is at least `lagMs` old, and — where a `confirmStored`
|
|
201
|
+
* is given — QuestDB can read back the newest row written before it. The
|
|
202
|
+
* first two are local knowledge and bound only the writer and the commit
|
|
203
|
+
* delay; the third is QuestDB's own word that the rows are committed and
|
|
204
|
+
* applied, which no lag can stand in for when the server is stalled. A
|
|
205
|
+
* position that cannot be confirmed simply waits for a later offer. Positions
|
|
206
|
+
* offered while a candidate is waiting are passed over, so what reaches disk
|
|
207
|
+
* is always a position that was reached at least `lagMs` ago.
|
|
208
|
+
*
|
|
209
|
+
* If the writer drops lines the tracker stops for good. The cap discards the
|
|
210
|
+
* OLDEST buffered lines, which may predate any position taken since, so none
|
|
211
|
+
* can be trusted; the last one persisted before the drop still can. The run
|
|
212
|
+
* itself fails at its end for the same drops, and the re-run resumes from
|
|
213
|
+
* there.
|
|
214
|
+
*/
|
|
215
|
+
export class CheckpointTracker {
|
|
216
|
+
store;
|
|
217
|
+
writer;
|
|
218
|
+
lagMs;
|
|
219
|
+
now;
|
|
220
|
+
confirmStored;
|
|
221
|
+
candidate = null;
|
|
222
|
+
droppedAtStart;
|
|
223
|
+
constructor(store, writer, lagMs = CHECKPOINT_LAG_MS, now = Date.now, confirmStored) {
|
|
224
|
+
this.store = store;
|
|
225
|
+
this.writer = writer;
|
|
226
|
+
this.lagMs = lagMs;
|
|
227
|
+
this.now = now;
|
|
228
|
+
this.confirmStored = confirmStored;
|
|
229
|
+
this.droppedAtStart = writer.droppedLineCount ?? 0;
|
|
230
|
+
}
|
|
231
|
+
/**
|
|
232
|
+
* Offer the position just reached, with the newest rows written before it.
|
|
233
|
+
* `snapshot` is only called if the position is taken up, so passing over
|
|
234
|
+
* one costs nothing.
|
|
235
|
+
*/
|
|
236
|
+
async offer(snapshot, tail) {
|
|
237
|
+
const dropped = this.writer.droppedLineCount ?? 0;
|
|
238
|
+
if (dropped !== this.droppedAtStart) {
|
|
239
|
+
this.candidate = null;
|
|
240
|
+
return;
|
|
241
|
+
}
|
|
242
|
+
const enqueued = this.writer.enqueuedLineCount;
|
|
243
|
+
const settled = this.writer.settledLineCount;
|
|
244
|
+
if (enqueued === undefined || settled === undefined)
|
|
245
|
+
return;
|
|
246
|
+
if (this.candidate) {
|
|
247
|
+
// Lines leave the writer in the order they came, so the ones that have
|
|
248
|
+
// left — accepted by the socket, or dropped before this run began — are
|
|
249
|
+
// the first `settled + dropped` of the sequence.
|
|
250
|
+
const left = settled + dropped;
|
|
251
|
+
const ripe = this.now() - this.candidate.takenAt >= this.lagMs;
|
|
252
|
+
if (!ripe || left < this.candidate.mark)
|
|
253
|
+
return;
|
|
254
|
+
if (this.confirmStored && !(await this.stored(this.candidate.tail)))
|
|
255
|
+
return;
|
|
256
|
+
await this.store.save(this.candidate.checkpoint);
|
|
257
|
+
this.candidate = null;
|
|
258
|
+
}
|
|
259
|
+
this.candidate = {
|
|
260
|
+
checkpoint: snapshot(),
|
|
261
|
+
tail: { ...tail },
|
|
262
|
+
mark: enqueued,
|
|
263
|
+
takenAt: this.now(),
|
|
264
|
+
};
|
|
265
|
+
}
|
|
266
|
+
/** A confirmation that fails to answer is a "no": the position waits. */
|
|
267
|
+
async stored(tail) {
|
|
268
|
+
try {
|
|
269
|
+
return await this.confirmStored(tail);
|
|
270
|
+
}
|
|
271
|
+
catch {
|
|
272
|
+
return false;
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
//# sourceMappingURL=migration-checkpoint.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"migration-checkpoint.js","sourceRoot":"","sources":["../src/migration-checkpoint.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import type { QueryClient } from "./query-client.js";
|
|
2
|
+
/**
|
|
3
|
+
* The rows in question: imported, and under a suffixed path. A live row has
|
|
4
|
+
* no source when its delta had none, and the predicate is spelled so that
|
|
5
|
+
* such a row is kept whatever the database makes of a comparison with null —
|
|
6
|
+
* verified on QuestDB, which keeps it either way, but not left to it.
|
|
7
|
+
*/
|
|
8
|
+
export declare const LEGACY_IMPORT_ROWS_WHERE: string;
|
|
9
|
+
export interface CleanupWriter {
|
|
10
|
+
hold(): void;
|
|
11
|
+
release(): void;
|
|
12
|
+
startTimestampWatch(): void;
|
|
13
|
+
endTimestampWatch(): bigint | null;
|
|
14
|
+
writeStringAtNanos(path: string, context: string, value: string, tsNanos: bigint, kind?: "boolean" | "identity", source?: string): void;
|
|
15
|
+
readonly droppedLineCount?: number;
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* How many legacy rows there are — after finishing what a rebuild that failed
|
|
19
|
+
* after its swap left undone, so the count never reads zero while a retired
|
|
20
|
+
* table still holds rows to carry over.
|
|
21
|
+
*/
|
|
22
|
+
export declare function countLegacyImportRows(query: Pick<QueryClient, "exec">): Promise<number>;
|
|
23
|
+
/**
|
|
24
|
+
* Rebuild the string table without the legacy rows.
|
|
25
|
+
*
|
|
26
|
+
* The copy runs with the writer working as usual. Two markers through the
|
|
27
|
+
* writer's own stream bound what it wrote meanwhile: one before the copy,
|
|
28
|
+
* waited for, so that everything sent earlier is in the snapshot; and one
|
|
29
|
+
* last before the hold, waited for, so that everything sent before the swap
|
|
30
|
+
* is in the old table. Between the two the writer notes the oldest timestamp
|
|
31
|
+
* it wrote, and rows of the old table from there on are carried over before
|
|
32
|
+
* it is dropped — a source with a wrong clock records the present under any
|
|
33
|
+
* date. Only the swap holds the writer. A swap that fails before the rebuilt
|
|
34
|
+
* table is in place puts the old one back; a failure after that leaves the
|
|
35
|
+
* rebuilt table in place, since it has accepted writes since, and the old one
|
|
36
|
+
* is carried over and dropped by the next run.
|
|
37
|
+
*
|
|
38
|
+
* Reports the lines the writer's cap discarded during the hold, if any: the
|
|
39
|
+
* hold lasts seconds, but a recorder fed fast enough could still fill it.
|
|
40
|
+
*/
|
|
41
|
+
export declare function removeLegacyImportRows(query: Pick<QueryClient, "exec">, writer: CleanupWriter, deps?: {
|
|
42
|
+
now?: () => number;
|
|
43
|
+
sleep?: (ms: number) => Promise<void>;
|
|
44
|
+
barrierTimeoutMs?: number;
|
|
45
|
+
}): Promise<{
|
|
46
|
+
removed: number;
|
|
47
|
+
dropped: number;
|
|
48
|
+
}>;
|
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
// Removing what an import made before 2.1.5 left behind.
|
|
2
|
+
//
|
|
3
|
+
// Against a signalk-to-influxdb 1.x source, those imports filed every string,
|
|
4
|
+
// boolean and object under a path suffixed with the InfluxDB field name —
|
|
5
|
+
// `navigation.state.stringValue`, `steering.autopilot.engaged.boolValue`,
|
|
6
|
+
// `navigation.position.jsonValue` — paths no consumer ever asks for. A re-run
|
|
7
|
+
// of the import writes the rows under their real paths, but cannot remove the
|
|
8
|
+
// old ones: the path is part of the dedup key, and QuestDB has no row DELETE.
|
|
9
|
+
// The only way to drop rows is to rebuild the table without them.
|
|
10
|
+
/** The field names the 1.x writer used, which the old importer appended. */
|
|
11
|
+
const LEGACY_SUFFIXES = [".stringValue", ".boolValue", ".jsonValue"];
|
|
12
|
+
/**
|
|
13
|
+
* The rows in question: imported, and under a suffixed path. A live row has
|
|
14
|
+
* no source when its delta had none, and the predicate is spelled so that
|
|
15
|
+
* such a row is kept whatever the database makes of a comparison with null —
|
|
16
|
+
* verified on QuestDB, which keeps it either way, but not left to it.
|
|
17
|
+
*/
|
|
18
|
+
export const LEGACY_IMPORT_ROWS_WHERE = `(source IS NOT NULL AND source = 'influxdb-import' AND (` +
|
|
19
|
+
LEGACY_SUFFIXES.map((s) => `path LIKE '%${s}'`).join(" OR ") +
|
|
20
|
+
`))`;
|
|
21
|
+
const TABLE = "signalk_str";
|
|
22
|
+
const REBUILT = "signalk_str_clean";
|
|
23
|
+
const RETIRED = "signalk_str_old";
|
|
24
|
+
/** How long the rebuild may take: a table of tens of millions of rows. */
|
|
25
|
+
const REBUILD_TIMEOUT_MS = 30 * 60 * 1000;
|
|
26
|
+
/** How long to wait for the marker, and so for everything sent before it. */
|
|
27
|
+
const BARRIER_TIMEOUT_MS = 60 * 1000;
|
|
28
|
+
/**
|
|
29
|
+
* Marker rows: one written before the copy, one written last before the
|
|
30
|
+
* hold. Rows of the old table only — never copied, never carried over,
|
|
31
|
+
* dropped with it. Their source keeps them out of every query the plugin
|
|
32
|
+
* makes for real data.
|
|
33
|
+
*/
|
|
34
|
+
const MARKER_SOURCE = "signalk-questdb-cleanup";
|
|
35
|
+
const MARKER_PATH = "cleanup.marker";
|
|
36
|
+
/** Every row the rebuild keeps or carries over: not legacy, not a marker. */
|
|
37
|
+
const KEPT_ROWS_WHERE = `NOT ${LEGACY_IMPORT_ROWS_WHERE} AND (source IS NULL OR source != '${MARKER_SOURCE}')`;
|
|
38
|
+
/**
|
|
39
|
+
* How many legacy rows there are — after finishing what a rebuild that failed
|
|
40
|
+
* after its swap left undone, so the count never reads zero while a retired
|
|
41
|
+
* table still holds rows to carry over.
|
|
42
|
+
*/
|
|
43
|
+
export async function countLegacyImportRows(query) {
|
|
44
|
+
await recoverRetiredTable(query);
|
|
45
|
+
const r = await query.exec(`SELECT count() FROM ${TABLE} WHERE ${LEGACY_IMPORT_ROWS_WHERE}`, REBUILD_TIMEOUT_MS);
|
|
46
|
+
return Number(r.dataset[0]?.[0] ?? 0);
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* Rebuild the string table without the legacy rows.
|
|
50
|
+
*
|
|
51
|
+
* The copy runs with the writer working as usual. Two markers through the
|
|
52
|
+
* writer's own stream bound what it wrote meanwhile: one before the copy,
|
|
53
|
+
* waited for, so that everything sent earlier is in the snapshot; and one
|
|
54
|
+
* last before the hold, waited for, so that everything sent before the swap
|
|
55
|
+
* is in the old table. Between the two the writer notes the oldest timestamp
|
|
56
|
+
* it wrote, and rows of the old table from there on are carried over before
|
|
57
|
+
* it is dropped — a source with a wrong clock records the present under any
|
|
58
|
+
* date. Only the swap holds the writer. A swap that fails before the rebuilt
|
|
59
|
+
* table is in place puts the old one back; a failure after that leaves the
|
|
60
|
+
* rebuilt table in place, since it has accepted writes since, and the old one
|
|
61
|
+
* is carried over and dropped by the next run.
|
|
62
|
+
*
|
|
63
|
+
* Reports the lines the writer's cap discarded during the hold, if any: the
|
|
64
|
+
* hold lasts seconds, but a recorder fed fast enough could still fill it.
|
|
65
|
+
*/
|
|
66
|
+
export async function removeLegacyImportRows(query, writer, deps = {}) {
|
|
67
|
+
const now = deps.now ?? Date.now;
|
|
68
|
+
const sleep = deps.sleep ?? ((ms) => new Promise((r) => setTimeout(r, ms)));
|
|
69
|
+
const barrierTimeoutMs = deps.barrierTimeoutMs ?? BARRIER_TIMEOUT_MS;
|
|
70
|
+
const before = await countLegacyImportRows(query);
|
|
71
|
+
if (before === 0)
|
|
72
|
+
return { removed: 0, dropped: 0 };
|
|
73
|
+
// A rebuild that failed earlier may have left its table behind.
|
|
74
|
+
await query.exec(`DROP TABLE IF EXISTS ${REBUILT}`);
|
|
75
|
+
const writeMarker = () => {
|
|
76
|
+
const marker = `${now()}-${Math.random().toString(36).slice(2)}`;
|
|
77
|
+
writer.writeStringAtNanos(MARKER_PATH, "self", marker, BigInt(now()) * 1000000n, undefined, MARKER_SOURCE);
|
|
78
|
+
return marker;
|
|
79
|
+
};
|
|
80
|
+
// The watch first, then the marker: every line from here on is watched,
|
|
81
|
+
// and once the marker is there, every line from before is in the table
|
|
82
|
+
// and so in the snapshot the copy takes.
|
|
83
|
+
writer.startTimestampWatch();
|
|
84
|
+
await waitForMarker(query, writeMarker(), sleep, barrierTimeoutMs);
|
|
85
|
+
await query.exec(`CREATE TABLE ${REBUILT} AS (SELECT * FROM ${TABLE} WHERE ${KEPT_ROWS_WHERE}) TIMESTAMP(ts) PARTITION BY DAY WAL`, REBUILD_TIMEOUT_MS);
|
|
86
|
+
await query.exec(`ALTER TABLE ${REBUILT} DEDUP ENABLE UPSERT KEYS(ts, path, context, source)`);
|
|
87
|
+
const droppedBefore = writer.droppedLineCount ?? 0;
|
|
88
|
+
// Written and held in one go, so this marker is the last line sent.
|
|
89
|
+
const last = writeMarker();
|
|
90
|
+
writer.hold();
|
|
91
|
+
// Everything the writer sent into the old table after the copy's snapshot
|
|
92
|
+
// it sent between the two markers, and the watch says how old the oldest
|
|
93
|
+
// of it is. Nothing at all means nothing to carry over.
|
|
94
|
+
const oldestWritten = writer.endTimestampWatch();
|
|
95
|
+
try {
|
|
96
|
+
await waitForMarker(query, last, sleep, barrierTimeoutMs);
|
|
97
|
+
await query.exec(`RENAME TABLE ${TABLE} TO ${RETIRED}`);
|
|
98
|
+
try {
|
|
99
|
+
await query.exec(`RENAME TABLE ${REBUILT} TO ${TABLE}`);
|
|
100
|
+
}
|
|
101
|
+
catch (err) {
|
|
102
|
+
await query.exec(`RENAME TABLE ${RETIRED} TO ${TABLE}`);
|
|
103
|
+
throw err;
|
|
104
|
+
}
|
|
105
|
+
await carryOverAndDrop(query, oldestWritten === null
|
|
106
|
+
? undefined
|
|
107
|
+
: new Date(Number(oldestWritten / 1000000n)).toISOString(), oldestWritten === null);
|
|
108
|
+
}
|
|
109
|
+
finally {
|
|
110
|
+
writer.release();
|
|
111
|
+
}
|
|
112
|
+
return {
|
|
113
|
+
removed: before,
|
|
114
|
+
dropped: (writer.droppedLineCount ?? 0) - droppedBefore,
|
|
115
|
+
};
|
|
116
|
+
}
|
|
117
|
+
/**
|
|
118
|
+
* Rows of the retired table that are not legacy rows or markers go into the
|
|
119
|
+
* live table — dedup makes the overlap harmless — and then it is dropped.
|
|
120
|
+
* `since` narrows the copy to the stragglers when the cutoff is known; a
|
|
121
|
+
* recovery of a table left behind takes everything; `nothing` skips the copy
|
|
122
|
+
* when the writer wrote nothing meanwhile.
|
|
123
|
+
*/
|
|
124
|
+
async function carryOverAndDrop(query, since, nothing = false) {
|
|
125
|
+
if (!nothing) {
|
|
126
|
+
const window = since ? `ts >= '${since}' AND ` : "";
|
|
127
|
+
await query.exec(`INSERT INTO ${TABLE} SELECT * FROM ${RETIRED} WHERE ${window}${KEPT_ROWS_WHERE}`, REBUILD_TIMEOUT_MS);
|
|
128
|
+
}
|
|
129
|
+
await query.exec(`DROP TABLE ${RETIRED}`);
|
|
130
|
+
}
|
|
131
|
+
/**
|
|
132
|
+
* A rebuild that failed after the swap leaves the retired table behind, with
|
|
133
|
+
* the rows recorded during the copy still in it. Finish that work first.
|
|
134
|
+
*/
|
|
135
|
+
async function recoverRetiredTable(query) {
|
|
136
|
+
const r = await query.exec(`SELECT count() FROM tables() WHERE table_name = '${RETIRED}'`);
|
|
137
|
+
if (Number(r.dataset[0]?.[0] ?? 0) === 0)
|
|
138
|
+
return;
|
|
139
|
+
await carryOverAndDrop(query);
|
|
140
|
+
}
|
|
141
|
+
/** Until the marker can be read back: everything sent before it has landed. */
|
|
142
|
+
async function waitForMarker(query, marker, sleep, timeoutMs) {
|
|
143
|
+
let waited = 0;
|
|
144
|
+
for (;;) {
|
|
145
|
+
const r = await query.exec(`SELECT count() FROM ${TABLE} WHERE path = '${MARKER_PATH}' AND source = '${MARKER_SOURCE}' AND value_str = '${marker}'`);
|
|
146
|
+
if (Number(r.dataset[0]?.[0] ?? 0) > 0)
|
|
147
|
+
return;
|
|
148
|
+
if (waited >= timeoutMs) {
|
|
149
|
+
throw new Error(`QuestDB has not taken the last writes to ${TABLE} after ${Math.round(timeoutMs / 1000)}s; try again later`);
|
|
150
|
+
}
|
|
151
|
+
await sleep(500);
|
|
152
|
+
waited += 500;
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
//# sourceMappingURL=migration-cleanup.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"migration-cleanup.js","sourceRoot":"","sources":["../src/migration-cleanup.ts"],"names":[],"mappings":"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"}
|