@prisma/streams-server 0.1.1 → 0.1.3
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/CONTRIBUTING.md +8 -0
- package/package.json +2 -1
- package/src/app.ts +290 -17
- package/src/app_core.ts +1833 -698
- package/src/app_local.ts +144 -4
- package/src/auto_tune.ts +62 -0
- package/src/bootstrap.ts +159 -1
- package/src/concurrency_gate.ts +108 -0
- package/src/config.ts +116 -14
- package/src/db/db.ts +1201 -131
- package/src/db/schema.ts +308 -8
- package/src/foreground_activity.ts +55 -0
- package/src/index/indexer.ts +254 -124
- package/src/index/lexicon_file_cache.ts +261 -0
- package/src/index/lexicon_format.ts +93 -0
- package/src/index/lexicon_indexer.ts +789 -0
- package/src/index/secondary_indexer.ts +824 -0
- package/src/index/secondary_schema.ts +105 -0
- package/src/ingest.ts +10 -12
- package/src/manifest.ts +143 -8
- package/src/memory.ts +183 -8
- package/src/metrics.ts +15 -29
- package/src/metrics_emitter.ts +26 -3
- package/src/notifier.ts +121 -5
- package/src/objectstore/accounting.ts +92 -0
- package/src/objectstore/mock_r2.ts +1 -1
- package/src/objectstore/r2.ts +17 -1
- package/src/profiles/evlog/schema.ts +234 -0
- package/src/profiles/evlog.ts +299 -0
- package/src/profiles/generic.ts +47 -0
- package/src/profiles/index.ts +205 -0
- package/src/profiles/metrics/block_format.ts +109 -0
- package/src/profiles/metrics/normalize.ts +366 -0
- package/src/profiles/metrics/schema.ts +319 -0
- package/src/profiles/metrics.ts +85 -0
- package/src/profiles/profile.ts +225 -0
- package/src/{touch/engine.ts → profiles/stateProtocol/changes.ts} +3 -20
- package/src/profiles/stateProtocol/routes.ts +389 -0
- package/src/profiles/stateProtocol/types.ts +6 -0
- package/src/profiles/stateProtocol/validation.ts +51 -0
- package/src/profiles/stateProtocol.ts +100 -0
- package/src/read_filter.ts +468 -0
- package/src/reader.ts +2151 -164
- package/src/runtime/host_runtime.ts +5 -0
- package/src/runtime_memory.ts +200 -0
- package/src/runtime_memory_sampler.ts +235 -0
- package/src/schema/read_json.ts +43 -0
- package/src/schema/registry.ts +563 -59
- package/src/search/agg_format.ts +638 -0
- package/src/search/aggregate.ts +389 -0
- package/src/search/binary/codec.ts +162 -0
- package/src/search/binary/docset.ts +67 -0
- package/src/search/binary/restart_strings.ts +181 -0
- package/src/search/binary/varint.ts +34 -0
- package/src/search/bitset.ts +19 -0
- package/src/search/col_format.ts +382 -0
- package/src/search/col_runtime.ts +59 -0
- package/src/search/column_encoding.ts +43 -0
- package/src/search/companion_file_cache.ts +319 -0
- package/src/search/companion_format.ts +313 -0
- package/src/search/companion_manager.ts +1086 -0
- package/src/search/companion_plan.ts +218 -0
- package/src/search/fts_format.ts +423 -0
- package/src/search/fts_runtime.ts +333 -0
- package/src/search/query.ts +875 -0
- package/src/search/schema.ts +245 -0
- package/src/segment/cache.ts +93 -2
- package/src/segment/cached_segment.ts +89 -0
- package/src/segment/format.ts +108 -36
- package/src/segment/segmenter.ts +79 -5
- package/src/segment/segmenter_worker.ts +35 -6
- package/src/segment/segmenter_workers.ts +42 -12
- package/src/server.ts +150 -36
- package/src/sqlite/adapter.ts +185 -14
- package/src/sqlite/runtime_stats.ts +163 -0
- package/src/stats.ts +3 -3
- package/src/stream_size_reconciler.ts +100 -0
- package/src/touch/canonical_change.ts +7 -0
- package/src/touch/live_metrics.ts +94 -64
- package/src/touch/live_templates.ts +15 -1
- package/src/touch/manager.ts +166 -88
- package/src/touch/{interpreter_worker.ts → processor_worker.ts} +19 -14
- package/src/touch/spec.ts +95 -92
- package/src/touch/touch_journal.ts +4 -0
- package/src/touch/worker_pool.ts +8 -14
- package/src/touch/worker_protocol.ts +3 -3
- package/src/uploader.ts +77 -6
- package/src/util/bloom256.ts +2 -2
- package/src/util/byte_lru.ts +73 -0
- package/src/util/lru.ts +8 -0
- package/src/util/stream_paths.ts +19 -0
package/src/db/schema.ts
CHANGED
|
@@ -9,7 +9,7 @@ import { dsError } from "../util/ds_error.ts";
|
|
|
9
9
|
* - local metadata store (streams/segments/manifests/schemas)
|
|
10
10
|
*/
|
|
11
11
|
|
|
12
|
-
export const SCHEMA_VERSION =
|
|
12
|
+
export const SCHEMA_VERSION = 24;
|
|
13
13
|
|
|
14
14
|
export const DEFAULT_PRAGMAS_SQL = `
|
|
15
15
|
PRAGMA journal_mode = WAL;
|
|
@@ -26,6 +26,7 @@ CREATE TABLE IF NOT EXISTS streams (
|
|
|
26
26
|
updated_at_ms INTEGER NOT NULL,
|
|
27
27
|
|
|
28
28
|
content_type TEXT NOT NULL,
|
|
29
|
+
profile TEXT NULL,
|
|
29
30
|
stream_seq TEXT NULL,
|
|
30
31
|
closed INTEGER NOT NULL DEFAULT 0,
|
|
31
32
|
closed_producer_id TEXT NULL,
|
|
@@ -42,6 +43,11 @@ CREATE TABLE IF NOT EXISTS streams (
|
|
|
42
43
|
pending_rows INTEGER NOT NULL,
|
|
43
44
|
pending_bytes INTEGER NOT NULL,
|
|
44
45
|
|
|
46
|
+
-- Logical payload bytes ever appended to this stream and still part of its
|
|
47
|
+
-- visible history on this node. This is the constant-time source for
|
|
48
|
+
-- management endpoints such as /_details.
|
|
49
|
+
logical_size_bytes INTEGER NOT NULL DEFAULT 0,
|
|
50
|
+
|
|
45
51
|
-- Logical size of retained rows in the wal table for this stream (payload-only bytes).
|
|
46
52
|
-- This is explicitly tracked because SQLite file size is high-water and does not shrink
|
|
47
53
|
-- deterministically after DELETE-based GC/retention trimming.
|
|
@@ -73,7 +79,6 @@ CREATE TABLE IF NOT EXISTS wal (
|
|
|
73
79
|
);
|
|
74
80
|
|
|
75
81
|
CREATE UNIQUE INDEX IF NOT EXISTS wal_stream_offset_uniq ON wal(stream, offset);
|
|
76
|
-
CREATE INDEX IF NOT EXISTS wal_stream_offset_idx ON wal(stream, offset);
|
|
77
82
|
CREATE INDEX IF NOT EXISTS wal_ts_idx ON wal(ts_ms);
|
|
78
83
|
|
|
79
84
|
CREATE TABLE IF NOT EXISTS segments (
|
|
@@ -84,6 +89,7 @@ CREATE TABLE IF NOT EXISTS segments (
|
|
|
84
89
|
end_offset INTEGER NOT NULL,
|
|
85
90
|
block_count INTEGER NOT NULL,
|
|
86
91
|
last_append_ms INTEGER NOT NULL,
|
|
92
|
+
payload_bytes INTEGER NOT NULL DEFAULT 0,
|
|
87
93
|
size_bytes INTEGER NOT NULL,
|
|
88
94
|
local_path TEXT NOT NULL,
|
|
89
95
|
created_at_ms INTEGER NOT NULL,
|
|
@@ -108,12 +114,20 @@ CREATE TABLE IF NOT EXISTS manifests (
|
|
|
108
114
|
generation INTEGER NOT NULL,
|
|
109
115
|
uploaded_generation INTEGER NOT NULL,
|
|
110
116
|
last_uploaded_at_ms INTEGER NULL,
|
|
111
|
-
last_uploaded_etag TEXT NULL
|
|
117
|
+
last_uploaded_etag TEXT NULL,
|
|
118
|
+
last_uploaded_size_bytes INTEGER NULL
|
|
112
119
|
);
|
|
113
120
|
|
|
114
121
|
CREATE TABLE IF NOT EXISTS schemas (
|
|
115
122
|
stream TEXT PRIMARY KEY,
|
|
116
123
|
schema_json TEXT NOT NULL,
|
|
124
|
+
updated_at_ms INTEGER NOT NULL,
|
|
125
|
+
uploaded_size_bytes INTEGER NOT NULL DEFAULT 0
|
|
126
|
+
);
|
|
127
|
+
|
|
128
|
+
CREATE TABLE IF NOT EXISTS stream_profiles (
|
|
129
|
+
stream TEXT PRIMARY KEY,
|
|
130
|
+
profile_json TEXT NOT NULL,
|
|
117
131
|
updated_at_ms INTEGER NOT NULL
|
|
118
132
|
);
|
|
119
133
|
|
|
@@ -126,9 +140,9 @@ CREATE TABLE IF NOT EXISTS producer_state (
|
|
|
126
140
|
PRIMARY KEY (stream, producer_id)
|
|
127
141
|
);
|
|
128
142
|
|
|
129
|
-
CREATE TABLE IF NOT EXISTS
|
|
143
|
+
CREATE TABLE IF NOT EXISTS stream_touch_state (
|
|
130
144
|
stream TEXT PRIMARY KEY,
|
|
131
|
-
|
|
145
|
+
processed_through INTEGER NOT NULL,
|
|
132
146
|
updated_at_ms INTEGER NOT NULL
|
|
133
147
|
);
|
|
134
148
|
|
|
@@ -170,6 +184,7 @@ CREATE TABLE IF NOT EXISTS index_runs (
|
|
|
170
184
|
start_segment INTEGER NOT NULL,
|
|
171
185
|
end_segment INTEGER NOT NULL,
|
|
172
186
|
object_key TEXT NOT NULL,
|
|
187
|
+
size_bytes INTEGER NOT NULL DEFAULT 0,
|
|
173
188
|
filter_len INTEGER NOT NULL,
|
|
174
189
|
record_count INTEGER NOT NULL,
|
|
175
190
|
retired_gen INTEGER NULL,
|
|
@@ -179,6 +194,107 @@ CREATE TABLE IF NOT EXISTS index_runs (
|
|
|
179
194
|
CREATE INDEX IF NOT EXISTS index_runs_stream_idx ON index_runs(stream, level, start_segment);
|
|
180
195
|
`;
|
|
181
196
|
|
|
197
|
+
const CREATE_SECONDARY_INDEX_TABLES_SQL = `
|
|
198
|
+
CREATE TABLE IF NOT EXISTS secondary_index_state (
|
|
199
|
+
stream TEXT NOT NULL,
|
|
200
|
+
index_name TEXT NOT NULL,
|
|
201
|
+
index_secret BLOB NOT NULL,
|
|
202
|
+
config_hash TEXT NOT NULL,
|
|
203
|
+
indexed_through INTEGER NOT NULL,
|
|
204
|
+
updated_at_ms INTEGER NOT NULL,
|
|
205
|
+
PRIMARY KEY (stream, index_name)
|
|
206
|
+
);
|
|
207
|
+
|
|
208
|
+
CREATE TABLE IF NOT EXISTS secondary_index_runs (
|
|
209
|
+
run_id TEXT PRIMARY KEY,
|
|
210
|
+
stream TEXT NOT NULL,
|
|
211
|
+
index_name TEXT NOT NULL,
|
|
212
|
+
level INTEGER NOT NULL,
|
|
213
|
+
start_segment INTEGER NOT NULL,
|
|
214
|
+
end_segment INTEGER NOT NULL,
|
|
215
|
+
object_key TEXT NOT NULL,
|
|
216
|
+
size_bytes INTEGER NOT NULL DEFAULT 0,
|
|
217
|
+
filter_len INTEGER NOT NULL,
|
|
218
|
+
record_count INTEGER NOT NULL,
|
|
219
|
+
retired_gen INTEGER NULL,
|
|
220
|
+
retired_at_ms INTEGER NULL
|
|
221
|
+
);
|
|
222
|
+
|
|
223
|
+
CREATE INDEX IF NOT EXISTS secondary_index_runs_stream_idx
|
|
224
|
+
ON secondary_index_runs(stream, index_name, level, start_segment);
|
|
225
|
+
`;
|
|
226
|
+
|
|
227
|
+
const CREATE_LEXICON_INDEX_TABLES_SQL = `
|
|
228
|
+
CREATE TABLE IF NOT EXISTS lexicon_index_state (
|
|
229
|
+
stream TEXT NOT NULL,
|
|
230
|
+
source_kind TEXT NOT NULL,
|
|
231
|
+
source_name TEXT NOT NULL,
|
|
232
|
+
indexed_through INTEGER NOT NULL,
|
|
233
|
+
updated_at_ms INTEGER NOT NULL,
|
|
234
|
+
PRIMARY KEY (stream, source_kind, source_name)
|
|
235
|
+
);
|
|
236
|
+
|
|
237
|
+
CREATE TABLE IF NOT EXISTS lexicon_index_runs (
|
|
238
|
+
run_id TEXT PRIMARY KEY,
|
|
239
|
+
stream TEXT NOT NULL,
|
|
240
|
+
source_kind TEXT NOT NULL,
|
|
241
|
+
source_name TEXT NOT NULL,
|
|
242
|
+
level INTEGER NOT NULL,
|
|
243
|
+
start_segment INTEGER NOT NULL,
|
|
244
|
+
end_segment INTEGER NOT NULL,
|
|
245
|
+
object_key TEXT NOT NULL,
|
|
246
|
+
size_bytes INTEGER NOT NULL DEFAULT 0,
|
|
247
|
+
record_count INTEGER NOT NULL,
|
|
248
|
+
retired_gen INTEGER NULL,
|
|
249
|
+
retired_at_ms INTEGER NULL
|
|
250
|
+
);
|
|
251
|
+
|
|
252
|
+
CREATE INDEX IF NOT EXISTS lexicon_index_runs_stream_idx
|
|
253
|
+
ON lexicon_index_runs(stream, source_kind, source_name, level, start_segment);
|
|
254
|
+
`;
|
|
255
|
+
|
|
256
|
+
const CREATE_SEARCH_COMPANION_TABLES_SQL = `
|
|
257
|
+
CREATE TABLE IF NOT EXISTS search_companion_plans (
|
|
258
|
+
stream TEXT PRIMARY KEY,
|
|
259
|
+
generation INTEGER NOT NULL,
|
|
260
|
+
plan_hash TEXT NOT NULL,
|
|
261
|
+
plan_json TEXT NOT NULL,
|
|
262
|
+
updated_at_ms INTEGER NOT NULL
|
|
263
|
+
);
|
|
264
|
+
|
|
265
|
+
CREATE TABLE IF NOT EXISTS search_segment_companions (
|
|
266
|
+
stream TEXT NOT NULL,
|
|
267
|
+
segment_index INTEGER NOT NULL,
|
|
268
|
+
object_key TEXT NOT NULL,
|
|
269
|
+
plan_generation INTEGER NOT NULL,
|
|
270
|
+
sections_json TEXT NOT NULL,
|
|
271
|
+
section_sizes_json TEXT NOT NULL DEFAULT '{}',
|
|
272
|
+
size_bytes INTEGER NOT NULL DEFAULT 0,
|
|
273
|
+
primary_timestamp_min_ms INTEGER NULL,
|
|
274
|
+
primary_timestamp_max_ms INTEGER NULL,
|
|
275
|
+
updated_at_ms INTEGER NOT NULL,
|
|
276
|
+
PRIMARY KEY (stream, segment_index)
|
|
277
|
+
);
|
|
278
|
+
|
|
279
|
+
CREATE INDEX IF NOT EXISTS search_segment_companions_stream_plan_idx
|
|
280
|
+
ON search_segment_companions(stream, plan_generation, segment_index);
|
|
281
|
+
`;
|
|
282
|
+
|
|
283
|
+
const CREATE_OBJECTSTORE_REQUEST_TABLES_SQL = `
|
|
284
|
+
CREATE TABLE IF NOT EXISTS objectstore_request_counts (
|
|
285
|
+
stream_hash TEXT NOT NULL,
|
|
286
|
+
artifact TEXT NOT NULL,
|
|
287
|
+
op TEXT NOT NULL,
|
|
288
|
+
count INTEGER NOT NULL DEFAULT 0,
|
|
289
|
+
bytes INTEGER NOT NULL DEFAULT 0,
|
|
290
|
+
updated_at_ms INTEGER NOT NULL,
|
|
291
|
+
PRIMARY KEY (stream_hash, artifact, op)
|
|
292
|
+
);
|
|
293
|
+
|
|
294
|
+
CREATE INDEX IF NOT EXISTS objectstore_request_counts_stream_hash_idx
|
|
295
|
+
ON objectstore_request_counts(stream_hash, updated_at_ms);
|
|
296
|
+
`;
|
|
297
|
+
|
|
182
298
|
const CREATE_TABLES_V4_SUFFIX_SQL = (suffix: string): string => `
|
|
183
299
|
CREATE TABLE streams_${suffix} (
|
|
184
300
|
stream TEXT PRIMARY KEY,
|
|
@@ -201,6 +317,7 @@ CREATE TABLE streams_${suffix} (
|
|
|
201
317
|
|
|
202
318
|
pending_rows INTEGER NOT NULL,
|
|
203
319
|
pending_bytes INTEGER NOT NULL,
|
|
320
|
+
logical_size_bytes INTEGER NOT NULL DEFAULT 0,
|
|
204
321
|
|
|
205
322
|
last_append_ms INTEGER NOT NULL,
|
|
206
323
|
last_segment_cut_ms INTEGER NOT NULL,
|
|
@@ -263,7 +380,6 @@ CREATE TABLE producer_state_${suffix} (
|
|
|
263
380
|
|
|
264
381
|
const CREATE_INDEXES_V4_SQL = `
|
|
265
382
|
CREATE UNIQUE INDEX IF NOT EXISTS wal_stream_offset_uniq ON wal(stream, offset);
|
|
266
|
-
CREATE INDEX IF NOT EXISTS wal_stream_offset_idx ON wal(stream, offset);
|
|
267
383
|
CREATE INDEX IF NOT EXISTS wal_ts_idx ON wal(ts_ms);
|
|
268
384
|
|
|
269
385
|
CREATE INDEX IF NOT EXISTS streams_pending_bytes_idx ON streams(pending_bytes);
|
|
@@ -297,6 +413,10 @@ export function initSchema(db: SqliteDatabase, opts: { skipMigrations?: boolean
|
|
|
297
413
|
if (version0 == null) {
|
|
298
414
|
db.exec(CREATE_TABLES_V4_SQL);
|
|
299
415
|
db.exec(CREATE_INDEX_TABLES_SQL);
|
|
416
|
+
db.exec(CREATE_SECONDARY_INDEX_TABLES_SQL);
|
|
417
|
+
db.exec(CREATE_LEXICON_INDEX_TABLES_SQL);
|
|
418
|
+
db.exec(CREATE_SEARCH_COMPANION_TABLES_SQL);
|
|
419
|
+
db.exec(CREATE_OBJECTSTORE_REQUEST_TABLES_SQL);
|
|
300
420
|
db.query("INSERT INTO schema_version(version) VALUES (?);").run(SCHEMA_VERSION);
|
|
301
421
|
return;
|
|
302
422
|
}
|
|
@@ -325,6 +445,32 @@ export function initSchema(db: SqliteDatabase, opts: { skipMigrations?: boolean
|
|
|
325
445
|
migrateV9ToV10(db);
|
|
326
446
|
} else if (version === 10) {
|
|
327
447
|
migrateV10ToV11(db);
|
|
448
|
+
} else if (version === 11) {
|
|
449
|
+
migrateV11ToV12(db);
|
|
450
|
+
} else if (version === 12) {
|
|
451
|
+
migrateV12ToV13(db);
|
|
452
|
+
} else if (version === 13) {
|
|
453
|
+
migrateV13ToV14(db);
|
|
454
|
+
} else if (version === 14) {
|
|
455
|
+
migrateV14ToV15(db);
|
|
456
|
+
} else if (version === 15) {
|
|
457
|
+
migrateV15ToV16(db);
|
|
458
|
+
} else if (version === 16) {
|
|
459
|
+
migrateV16ToV17(db);
|
|
460
|
+
} else if (version === 17) {
|
|
461
|
+
migrateV17ToV18(db);
|
|
462
|
+
} else if (version === 18) {
|
|
463
|
+
migrateV18ToV19(db);
|
|
464
|
+
} else if (version === 19) {
|
|
465
|
+
migrateV19ToV20(db);
|
|
466
|
+
} else if (version === 20) {
|
|
467
|
+
migrateV20ToV21(db);
|
|
468
|
+
} else if (version === 21) {
|
|
469
|
+
migrateV21ToV22(db);
|
|
470
|
+
} else if (version === 22) {
|
|
471
|
+
migrateV22ToV23(db);
|
|
472
|
+
} else if (version === 23) {
|
|
473
|
+
migrateV23ToV24(db);
|
|
328
474
|
} else {
|
|
329
475
|
throw dsError(`unexpected schema version: ${version} (expected ${SCHEMA_VERSION})`);
|
|
330
476
|
}
|
|
@@ -541,9 +687,9 @@ function migrateV5ToV6(db: SqliteDatabase): void {
|
|
|
541
687
|
function migrateV6ToV7(db: SqliteDatabase): void {
|
|
542
688
|
const tx = db.transaction(() => {
|
|
543
689
|
db.exec(`
|
|
544
|
-
CREATE TABLE IF NOT EXISTS
|
|
690
|
+
CREATE TABLE IF NOT EXISTS stream_touch_state (
|
|
545
691
|
stream TEXT PRIMARY KEY,
|
|
546
|
-
|
|
692
|
+
processed_through INTEGER NOT NULL,
|
|
547
693
|
updated_at_ms INTEGER NOT NULL
|
|
548
694
|
);
|
|
549
695
|
`);
|
|
@@ -619,7 +765,161 @@ function migrateV9ToV10(db: SqliteDatabase): void {
|
|
|
619
765
|
function migrateV10ToV11(db: SqliteDatabase): void {
|
|
620
766
|
const tx = db.transaction(() => {
|
|
621
767
|
db.exec(`DROP INDEX IF EXISTS wal_touch_stream_rk_offset_idx;`);
|
|
768
|
+
db.exec(`UPDATE schema_version SET version = 11;`);
|
|
769
|
+
});
|
|
770
|
+
tx();
|
|
771
|
+
}
|
|
772
|
+
|
|
773
|
+
function migrateV11ToV12(db: SqliteDatabase): void {
|
|
774
|
+
const tx = db.transaction(() => {
|
|
775
|
+
db.exec(`ALTER TABLE streams ADD COLUMN profile TEXT NULL;`);
|
|
776
|
+
db.exec(`UPDATE schema_version SET version = 12;`);
|
|
777
|
+
});
|
|
778
|
+
tx();
|
|
779
|
+
}
|
|
780
|
+
|
|
781
|
+
function migrateV12ToV13(db: SqliteDatabase): void {
|
|
782
|
+
const tx = db.transaction(() => {
|
|
783
|
+
db.exec(`
|
|
784
|
+
CREATE TABLE IF NOT EXISTS stream_profiles (
|
|
785
|
+
stream TEXT PRIMARY KEY,
|
|
786
|
+
profile_json TEXT NOT NULL,
|
|
787
|
+
updated_at_ms INTEGER NOT NULL
|
|
788
|
+
);
|
|
789
|
+
`);
|
|
790
|
+
db.exec(`UPDATE schema_version SET version = 13;`);
|
|
791
|
+
});
|
|
792
|
+
tx();
|
|
793
|
+
}
|
|
794
|
+
|
|
795
|
+
function migrateV13ToV14(db: SqliteDatabase): void {
|
|
796
|
+
const tx = db.transaction(() => {
|
|
797
|
+
db.exec(`
|
|
798
|
+
CREATE TABLE IF NOT EXISTS stream_touch_state (
|
|
799
|
+
stream TEXT PRIMARY KEY,
|
|
800
|
+
processed_through INTEGER NOT NULL,
|
|
801
|
+
updated_at_ms INTEGER NOT NULL
|
|
802
|
+
);
|
|
803
|
+
`);
|
|
804
|
+
|
|
805
|
+
const hasLegacy = !!db
|
|
806
|
+
.query(`SELECT name FROM sqlite_master WHERE type='table' AND name='stream_interpreters' LIMIT 1;`)
|
|
807
|
+
.get();
|
|
808
|
+
if (hasLegacy) {
|
|
809
|
+
db.exec(`
|
|
810
|
+
INSERT OR REPLACE INTO stream_touch_state(stream, processed_through, updated_at_ms)
|
|
811
|
+
SELECT stream, interpreted_through, updated_at_ms
|
|
812
|
+
FROM stream_interpreters;
|
|
813
|
+
`);
|
|
814
|
+
db.exec(`DROP TABLE stream_interpreters;`);
|
|
815
|
+
}
|
|
816
|
+
|
|
622
817
|
db.exec(`UPDATE schema_version SET version = ${SCHEMA_VERSION};`);
|
|
623
818
|
});
|
|
624
819
|
tx();
|
|
625
820
|
}
|
|
821
|
+
|
|
822
|
+
function migrateV14ToV15(db: SqliteDatabase): void {
|
|
823
|
+
const tx = db.transaction(() => {
|
|
824
|
+
db.exec(CREATE_SECONDARY_INDEX_TABLES_SQL);
|
|
825
|
+
db.exec(`UPDATE schema_version SET version = 15;`);
|
|
826
|
+
});
|
|
827
|
+
tx();
|
|
828
|
+
}
|
|
829
|
+
|
|
830
|
+
function migrateV15ToV16(db: SqliteDatabase): void {
|
|
831
|
+
const tx = db.transaction(() => {
|
|
832
|
+
db.exec(`UPDATE schema_version SET version = 16;`);
|
|
833
|
+
});
|
|
834
|
+
tx();
|
|
835
|
+
}
|
|
836
|
+
|
|
837
|
+
function migrateV16ToV17(db: SqliteDatabase): void {
|
|
838
|
+
const tx = db.transaction(() => {
|
|
839
|
+
db.exec(`ALTER TABLE streams ADD COLUMN logical_size_bytes INTEGER NOT NULL DEFAULT 0;`);
|
|
840
|
+
|
|
841
|
+
// Streams that still live entirely in the retained WAL can be backfilled
|
|
842
|
+
// cheaply here. Streams with published segments are repaired asynchronously
|
|
843
|
+
// at runtime from segment objects if this value is still missing.
|
|
844
|
+
db.exec(`
|
|
845
|
+
UPDATE streams
|
|
846
|
+
SET logical_size_bytes = wal_bytes
|
|
847
|
+
WHERE next_offset = wal_rows;
|
|
848
|
+
`);
|
|
849
|
+
|
|
850
|
+
db.exec(`UPDATE schema_version SET version = 17;`);
|
|
851
|
+
});
|
|
852
|
+
tx();
|
|
853
|
+
}
|
|
854
|
+
|
|
855
|
+
function migrateV17ToV18(db: SqliteDatabase): void {
|
|
856
|
+
const tx = db.transaction(() => {
|
|
857
|
+
db.exec(CREATE_SEARCH_COMPANION_TABLES_SQL);
|
|
858
|
+
db.exec(`UPDATE schema_version SET version = 18;`);
|
|
859
|
+
});
|
|
860
|
+
tx();
|
|
861
|
+
}
|
|
862
|
+
|
|
863
|
+
function migrateV18ToV19(db: SqliteDatabase): void {
|
|
864
|
+
const tx = db.transaction(() => {
|
|
865
|
+
db.exec(`ALTER TABLE secondary_index_state ADD COLUMN config_hash TEXT NOT NULL DEFAULT '';`);
|
|
866
|
+
db.exec(`DROP INDEX IF EXISTS search_family_segments_stream_idx;`);
|
|
867
|
+
db.exec(`DROP TABLE IF EXISTS search_family_segments;`);
|
|
868
|
+
db.exec(`DROP TABLE IF EXISTS search_family_state;`);
|
|
869
|
+
db.exec(`UPDATE schema_version SET version = 19;`);
|
|
870
|
+
});
|
|
871
|
+
tx();
|
|
872
|
+
}
|
|
873
|
+
|
|
874
|
+
function migrateV19ToV20(db: SqliteDatabase): void {
|
|
875
|
+
const tx = db.transaction(() => {
|
|
876
|
+
db.exec(`ALTER TABLE manifests ADD COLUMN last_uploaded_size_bytes INTEGER NULL;`);
|
|
877
|
+
db.exec(`ALTER TABLE schemas ADD COLUMN uploaded_size_bytes INTEGER NOT NULL DEFAULT 0;`);
|
|
878
|
+
db.exec(`ALTER TABLE index_runs ADD COLUMN size_bytes INTEGER NOT NULL DEFAULT 0;`);
|
|
879
|
+
db.exec(`ALTER TABLE secondary_index_runs ADD COLUMN size_bytes INTEGER NOT NULL DEFAULT 0;`);
|
|
880
|
+
db.exec(`ALTER TABLE search_segment_companions ADD COLUMN section_sizes_json TEXT NOT NULL DEFAULT '{}';`);
|
|
881
|
+
db.exec(`ALTER TABLE search_segment_companions ADD COLUMN size_bytes INTEGER NOT NULL DEFAULT 0;`);
|
|
882
|
+
db.exec(CREATE_OBJECTSTORE_REQUEST_TABLES_SQL);
|
|
883
|
+
db.exec(`UPDATE schema_version SET version = 20;`);
|
|
884
|
+
});
|
|
885
|
+
tx();
|
|
886
|
+
}
|
|
887
|
+
|
|
888
|
+
function migrateV20ToV21(db: SqliteDatabase): void {
|
|
889
|
+
const tx = db.transaction(() => {
|
|
890
|
+
db.exec(`ALTER TABLE search_segment_companions ADD COLUMN primary_timestamp_min_ms INTEGER NULL;`);
|
|
891
|
+
db.exec(`ALTER TABLE search_segment_companions ADD COLUMN primary_timestamp_max_ms INTEGER NULL;`);
|
|
892
|
+
db.exec(`UPDATE schema_version SET version = 21;`);
|
|
893
|
+
});
|
|
894
|
+
tx();
|
|
895
|
+
}
|
|
896
|
+
|
|
897
|
+
function migrateV21ToV22(db: SqliteDatabase): void {
|
|
898
|
+
const tx = db.transaction(() => {
|
|
899
|
+
db.exec(CREATE_LEXICON_INDEX_TABLES_SQL);
|
|
900
|
+
db.exec(`UPDATE schema_version SET version = 22;`);
|
|
901
|
+
});
|
|
902
|
+
tx();
|
|
903
|
+
}
|
|
904
|
+
|
|
905
|
+
function migrateV22ToV23(db: SqliteDatabase): void {
|
|
906
|
+
const tx = db.transaction(() => {
|
|
907
|
+
db.exec(`DROP INDEX IF EXISTS wal_stream_offset_idx;`);
|
|
908
|
+
db.exec(`UPDATE schema_version SET version = 23;`);
|
|
909
|
+
});
|
|
910
|
+
tx();
|
|
911
|
+
}
|
|
912
|
+
|
|
913
|
+
function migrateV23ToV24(db: SqliteDatabase): void {
|
|
914
|
+
const tx = db.transaction(() => {
|
|
915
|
+
const hasPayloadBytes = db
|
|
916
|
+
.query(`PRAGMA table_info(segments);`)
|
|
917
|
+
.all()
|
|
918
|
+
.some((row: any) => String(row.name) === "payload_bytes");
|
|
919
|
+
if (!hasPayloadBytes) {
|
|
920
|
+
db.exec(`ALTER TABLE segments ADD COLUMN payload_bytes INTEGER NOT NULL DEFAULT 0;`);
|
|
921
|
+
}
|
|
922
|
+
db.exec(`UPDATE schema_version SET version = 24;`);
|
|
923
|
+
});
|
|
924
|
+
tx();
|
|
925
|
+
}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import { yieldToEventLoop } from "./util/yield";
|
|
2
|
+
|
|
3
|
+
export class ForegroundActivityTracker {
|
|
4
|
+
private active = 0;
|
|
5
|
+
private lastActivityAt = 0;
|
|
6
|
+
private readonly idleResolvers = new Set<() => void>();
|
|
7
|
+
|
|
8
|
+
enter(): () => void {
|
|
9
|
+
this.lastActivityAt = Date.now();
|
|
10
|
+
this.active += 1;
|
|
11
|
+
let released = false;
|
|
12
|
+
return () => {
|
|
13
|
+
if (released) return;
|
|
14
|
+
released = true;
|
|
15
|
+
this.lastActivityAt = Date.now();
|
|
16
|
+
this.active = Math.max(0, this.active - 1);
|
|
17
|
+
if (this.active !== 0) return;
|
|
18
|
+
const resolvers = Array.from(this.idleResolvers);
|
|
19
|
+
this.idleResolvers.clear();
|
|
20
|
+
for (const resolve of resolvers) resolve();
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
isActive(): boolean {
|
|
25
|
+
return this.active > 0;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
getActive(): number {
|
|
29
|
+
return this.active;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
wasActiveWithin(windowMs: number): boolean {
|
|
33
|
+
return Date.now() - this.lastActivityAt <= windowMs;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
async yieldBackgroundWork(maxPauseMs = 5): Promise<void> {
|
|
37
|
+
if (this.active === 0) {
|
|
38
|
+
await yieldToEventLoop();
|
|
39
|
+
return;
|
|
40
|
+
}
|
|
41
|
+
let idleResolve!: () => void;
|
|
42
|
+
const idlePromise = new Promise<void>((resolve) => {
|
|
43
|
+
idleResolve = resolve;
|
|
44
|
+
this.idleResolvers.add(resolve);
|
|
45
|
+
});
|
|
46
|
+
try {
|
|
47
|
+
await Promise.race([
|
|
48
|
+
idlePromise,
|
|
49
|
+
new Promise<void>((resolve) => setTimeout(resolve, maxPauseMs)),
|
|
50
|
+
]);
|
|
51
|
+
} finally {
|
|
52
|
+
this.idleResolvers.delete(idleResolve);
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
}
|