@camstack/system 1.2.127 → 1.2.129
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/dist/builtins/sqlite-storage/sqlite-pragmas.d.ts +26 -0
- package/dist/builtins/sqlite-storage/sqlite-settings-backend.d.ts +17 -0
- package/dist/builtins/sqlite-storage/sqlite-settings.addon.js +0 -0
- package/dist/builtins/sqlite-storage/sqlite-settings.addon.mjs +0 -0
- package/dist/builtins/sqlite-storage/table-sizes.d.ts +70 -0
- package/package.json +1 -1
|
@@ -116,3 +116,29 @@ export declare const SQLITE_PAGE_CACHE_CEILING_KIB = -262144;
|
|
|
116
116
|
export declare function sqliteMmapSizeFor(fileSizeBytes: number): number;
|
|
117
117
|
/** The `cache_size` to apply for a database of this size, in KiB (negative). */
|
|
118
118
|
export declare function sqlitePageCacheKibFor(fileSizeBytes: number): number;
|
|
119
|
+
/** Page counters read straight off the connection, in the units SQLite reports. */
|
|
120
|
+
export interface SqlitePageCounters {
|
|
121
|
+
readonly pageCount: number;
|
|
122
|
+
readonly freelistCount: number;
|
|
123
|
+
readonly pageSizeBytes: number;
|
|
124
|
+
}
|
|
125
|
+
/** How much of the file holds rows, and how much is free pages awaiting reuse. */
|
|
126
|
+
export interface SqliteOccupancy {
|
|
127
|
+
readonly liveMb: number;
|
|
128
|
+
readonly freeMb: number;
|
|
129
|
+
readonly freePct: number;
|
|
130
|
+
}
|
|
131
|
+
/**
|
|
132
|
+
* Split a database file into live bytes and free bytes.
|
|
133
|
+
*
|
|
134
|
+
* A file that stopped growing is not the same as a file that is full. SQLite
|
|
135
|
+
* reuses freed pages but never returns them to the filesystem without a
|
|
136
|
+
* `VACUUM`, so a database sitting at its high-water mark looks — from `stat`,
|
|
137
|
+
* from `df`, from any measurement outside the connection — exactly like one
|
|
138
|
+
* genuinely packed with rows. The two call for opposite actions: the first is
|
|
139
|
+
* reclaimed by a VACUUM, the second means retention is not keeping up.
|
|
140
|
+
*
|
|
141
|
+
* `dbSizeMb` on the boot line could not tell them apart. This can, and it costs
|
|
142
|
+
* three header reads on a connection that is already open.
|
|
143
|
+
*/
|
|
144
|
+
export declare function sqliteOccupancy(counters: SqlitePageCounters): SqliteOccupancy;
|
|
@@ -218,6 +218,23 @@ export declare class SqliteSettingsBackend implements ISettingsBackend {
|
|
|
218
218
|
* unreadable `stat`, and both callers treat it as "unknown" and fall back to
|
|
219
219
|
* the floor — never to a smaller setting than the one already argued for.
|
|
220
220
|
*/
|
|
221
|
+
/**
|
|
222
|
+
* One numeric PRAGMA off the open connection. A pragma that does not answer
|
|
223
|
+
* with a number yields 0, which `sqliteOccupancy` reads as "unknown" and
|
|
224
|
+
* reports as an empty file rather than inventing a ratio.
|
|
225
|
+
*/
|
|
226
|
+
/**
|
|
227
|
+
* Name the biggest tables, once, a minute after the connection opens.
|
|
228
|
+
*
|
|
229
|
+
* `dbSizeMb` says how big the file is and `freePct` says how much of it is
|
|
230
|
+
* live; neither says WHOSE. Per-addon scoped collections are created on first
|
|
231
|
+
* write and declared nowhere, so this is the only place the real set can be
|
|
232
|
+
* enumerated. `unref` so it can never hold the process open, and
|
|
233
|
+
* `CAMSTACK_SQLITE_STORAGE_REPORT=off` turns it off without a rebuild.
|
|
234
|
+
*/
|
|
235
|
+
private scheduleStorageReport;
|
|
236
|
+
private logStorageReport;
|
|
237
|
+
private readPragmaNumber;
|
|
221
238
|
private measureDbSizeBytes;
|
|
222
239
|
private getDb;
|
|
223
240
|
/**
|
|
Binary file
|
|
Binary file
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Which collection IS the database.
|
|
3
|
+
*
|
|
4
|
+
* ## Why this exists
|
|
5
|
+
*
|
|
6
|
+
* The boot line already says how big the file is (`dbSizeMb`) and how much of
|
|
7
|
+
* it is real (`freePct`). Measured on the live hub 2026-08-25 those read
|
|
8
|
+
* `dbSizeMb=858 liveMb=840 freeMb=18 freePct=2` — so the growth from 153 MB
|
|
9
|
+
* (08-08) is not a high-water mark waiting for a VACUUM, it is live rows. That
|
|
10
|
+
* turned "should we VACUUM" into "which table", and nothing in this system
|
|
11
|
+
* could answer it.
|
|
12
|
+
*
|
|
13
|
+
* Nothing could, structurally: per-addon scoped collections
|
|
14
|
+
* (`<addonId>:<canonical>`) are created on the first write and declared in no
|
|
15
|
+
* source file, so the real set only exists on the running node. A reference
|
|
16
|
+
* page cannot list them and a grep cannot find them.
|
|
17
|
+
*
|
|
18
|
+
* ## Why `dbstat` and not `COUNT(*)`
|
|
19
|
+
*
|
|
20
|
+
* `dbstat` reports the pages a table actually occupies, which is the question —
|
|
21
|
+
* a table with few enormous rows and a table with many small ones are the same
|
|
22
|
+
* problem when the file is the thing that grew. It walks the b-tree structure
|
|
23
|
+
* without decoding row payloads, so it costs a page traversal rather than a
|
|
24
|
+
* full table scan. It is still real work on an 840 MB file, which is why the
|
|
25
|
+
* caller runs it ONCE, well after boot, off the critical path — never on the
|
|
26
|
+
* path a config read waits behind.
|
|
27
|
+
*/
|
|
28
|
+
/** One `dbstat` group: a table (or index) and the space it occupies. */
|
|
29
|
+
export interface TableSizeRow {
|
|
30
|
+
readonly name: string;
|
|
31
|
+
readonly bytes: number;
|
|
32
|
+
readonly pages: number;
|
|
33
|
+
}
|
|
34
|
+
/** One line of the report. */
|
|
35
|
+
export interface TableSizeEntry {
|
|
36
|
+
readonly name: string;
|
|
37
|
+
readonly sizeMb: number;
|
|
38
|
+
readonly pages: number;
|
|
39
|
+
/** Share of the whole file, percent. 0 when the file size is unknown. */
|
|
40
|
+
readonly pct: number;
|
|
41
|
+
}
|
|
42
|
+
export interface TableSizeReport {
|
|
43
|
+
readonly tables: readonly TableSizeEntry[];
|
|
44
|
+
/** Megabytes held by everything below the cut. */
|
|
45
|
+
readonly otherMb: number;
|
|
46
|
+
/** How many tables are behind `otherMb`. */
|
|
47
|
+
readonly otherTables: number;
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Rank tables by the space they hold and keep the top `limit`.
|
|
51
|
+
*
|
|
52
|
+
* The tail is REPORTED, never dropped: a top-N that silently discards the rest
|
|
53
|
+
* reads as "these N are the database", which is exactly the wrong conclusion
|
|
54
|
+
* when the remainder is a third of the file.
|
|
55
|
+
*/
|
|
56
|
+
export declare function summariseTableSizes(rows: readonly TableSizeRow[], totalBytes: number, limit: number): TableSizeReport;
|
|
57
|
+
/**
|
|
58
|
+
* How long after opening the connection the report runs.
|
|
59
|
+
*
|
|
60
|
+
* Not zero and not "on every boot forever at startup": boot is already the
|
|
61
|
+
* busiest window this engine sees (`ensureTable` x9, the retired-key purge, the
|
|
62
|
+
* vector registry), and a page traversal of an 840 MB file placed there would
|
|
63
|
+
* be indistinguishable from the stall this whole subsystem was just fixed for.
|
|
64
|
+
* Sixty seconds puts it after the storm, once per process.
|
|
65
|
+
*/
|
|
66
|
+
export declare const STORAGE_REPORT_DELAY_MS = 60000;
|
|
67
|
+
/** Top-N tables named individually; the remainder is reported as a tail. */
|
|
68
|
+
export declare const STORAGE_REPORT_TOP_N = 10;
|
|
69
|
+
/** `dbstat` shape, narrowed without a cast. */
|
|
70
|
+
export declare function toTableSizeRow(value: unknown): TableSizeRow | null;
|