@camstack/system 1.2.151 → 1.2.154
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/addon-runner.js +14 -15
- package/dist/addon-runner.mjs +14 -14
- package/dist/builtins/addon-pages-aggregator/addon-pages-aggregator.addon.js +1 -1
- package/dist/builtins/addon-pages-aggregator/addon-pages-aggregator.addon.mjs +1 -1
- package/dist/builtins/addon-widgets-aggregator/addon-widgets-aggregator.addon.js +1 -1
- package/dist/builtins/addon-widgets-aggregator/addon-widgets-aggregator.addon.mjs +1 -1
- package/dist/builtins/alerts/alerts.addon.js +1 -1
- package/dist/builtins/alerts/alerts.addon.mjs +1 -1
- package/dist/builtins/backup-orchestrator/backup-orchestrator.addon.js +1 -1
- package/dist/builtins/backup-orchestrator/backup-orchestrator.addon.mjs +1 -1
- package/dist/builtins/console-logging/index.js +1 -1
- package/dist/builtins/console-logging/index.mjs +1 -1
- package/dist/builtins/core-blocks/core-blocks.addon.js +1 -1
- package/dist/builtins/core-blocks/core-blocks.addon.mjs +1 -1
- package/dist/builtins/device-manager/device-manager.addon.js +2 -2
- package/dist/builtins/device-manager/device-manager.addon.mjs +2 -2
- package/dist/builtins/doorbell/virtual-doorbell.addon.js +1 -1
- package/dist/builtins/doorbell/virtual-doorbell.addon.mjs +1 -1
- package/dist/builtins/hub-forwarder/index.js +1 -1
- package/dist/builtins/hub-forwarder/index.mjs +1 -1
- package/dist/builtins/liveness-monitor/liveness-monitor.addon.js +1 -1
- package/dist/builtins/liveness-monitor/liveness-monitor.addon.mjs +1 -1
- package/dist/builtins/local-auth/local-auth.addon.js +1 -1
- package/dist/builtins/local-auth/local-auth.addon.mjs +1 -1
- package/dist/builtins/local-network/local-network.addon.js +3 -3
- package/dist/builtins/local-network/local-network.addon.mjs +3 -3
- package/dist/builtins/loki-logging/index.js +1 -1
- package/dist/builtins/loki-logging/index.mjs +1 -1
- package/dist/builtins/native-metrics/cgroup-memory.d.ts +193 -0
- package/dist/builtins/native-metrics/container-memory-series.d.ts +226 -0
- package/dist/builtins/native-metrics/native-metrics.addon.d.ts +26 -0
- package/dist/builtins/native-metrics/native-metrics.addon.js +1536 -661
- package/dist/builtins/native-metrics/native-metrics.addon.mjs +1536 -660
- package/dist/builtins/platform-probe/index.js +3 -3
- package/dist/builtins/platform-probe/index.mjs +2 -2
- package/dist/builtins/remote-access-orchestrator/remote-access-orchestrator.addon.js +1 -1
- package/dist/builtins/remote-access-orchestrator/remote-access-orchestrator.addon.mjs +1 -1
- package/dist/builtins/snapshot/index.js +1 -1
- package/dist/builtins/snapshot/index.mjs +1 -1
- package/dist/builtins/sqlite-storage/filesystem-storage.addon.js +2 -2
- package/dist/builtins/sqlite-storage/filesystem-storage.addon.mjs +2 -2
- 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/storage-orchestrator/storage-orchestrator.addon.js +1 -1
- package/dist/builtins/storage-orchestrator/storage-orchestrator.addon.mjs +1 -1
- package/dist/builtins/system-config/system-config.addon.js +1 -1
- package/dist/builtins/system-config/system-config.addon.mjs +1 -1
- package/dist/builtins/winston-logging/index.js +1 -1
- package/dist/builtins/winston-logging/index.mjs +1 -1
- package/dist/{dist-CImxMt5h.js → dist-BPlfW-CG.js} +96 -10
- package/dist/{dist-CbRciAbu.mjs → dist-D_NyUV6Y.mjs} +96 -10
- package/dist/index.d.ts +6 -2
- package/dist/index.js +24 -5
- package/dist/index.mjs +5 -5
- package/dist/kernel/event-plane-report.d.ts +202 -0
- package/dist/kernel/heap-spaces.d.ts +138 -0
- package/dist/kernel/heap-watch.d.ts +142 -5
- package/dist/kernel/index.d.ts +2 -0
- package/dist/kernel/transport/local-child-registry.d.ts +22 -0
- package/dist/{manifest-python-deps-DuQ3Uqoq.mjs → manifest-python-deps-BILJm2mI.mjs} +522 -15
- package/dist/{manifest-python-deps-BqWrDjSN.js → manifest-python-deps-COeSr7el.js} +636 -15
- package/dist/{retired-settings-keys-DhFPXIRD.mjs → retired-settings-keys-Bq1Tb84I.mjs} +1 -1
- package/dist/{retired-settings-keys-C7gLUS3x.js → retired-settings-keys-DBY6ebwV.js} +1 -1
- package/package.json +1 -1
- package/dist/{tls-u8QCJCFE.js → tls-BxQlomxd.js} +1 -1
- package/dist/{tls-CQhPGSJm.mjs → tls-DOTmtLCW.mjs} +1 -1
|
@@ -0,0 +1,226 @@
|
|
|
1
|
+
import { ContainerMemoryPoint, ContainerMemorySnapshot, IScopedLogger, SettingsStoreClient } from '@camstack/types';
|
|
2
|
+
import { LoadSeriesRetention } from './load-series-store.js';
|
|
3
|
+
/**
|
|
4
|
+
* @durable class=audit owner=metrics-native
|
|
5
|
+
* write="one row per NODE per sample, appended by the HUB's `native-metrics`
|
|
6
|
+
* off its subscription to `metrics.node-processes-snapshot` — the same
|
|
7
|
+
* subscription, the same accepted-sample gate and the same timestamps as
|
|
8
|
+
* `metrics:node-load-samples`, so a memory row and the process rows of the
|
|
9
|
+
* same instant share an `atMs`. A node with no cgroup writes NOTHING; it
|
|
10
|
+
* never writes a zeroed row."
|
|
11
|
+
* retention="The operator's `loadSeriesRetentionHours` by age and
|
|
12
|
+
* CONTAINER_MEMORY_MAX_ROWS by count, swept in the same pass as the process
|
|
13
|
+
* table and through the same bounded page. Losing the table loses the cold
|
|
14
|
+
* window only: the ring keeps serving the last hour."
|
|
15
|
+
*/
|
|
16
|
+
export declare const CONTAINER_MEMORY_COLLECTION = "metrics:node-memory-samples";
|
|
17
|
+
/**
|
|
18
|
+
* Per-node ceiling on the HOT ring, in samples.
|
|
19
|
+
*
|
|
20
|
+
* Deliberately the same 180 as the process ring — one hour at the 20 s cadence,
|
|
21
|
+
* six at the 10 s default's own reckoning — because the two series are read
|
|
22
|
+
* together and a memory window shorter than the process window would silently
|
|
23
|
+
* truncate the comparison the pair exists to support.
|
|
24
|
+
*/
|
|
25
|
+
export declare const CONTAINER_MEMORY_SAMPLES_PER_NODE = 180;
|
|
26
|
+
/**
|
|
27
|
+
* Fleet ceiling on the hot ring, in READINGS.
|
|
28
|
+
*
|
|
29
|
+
* One reading per node per sample, so this is simply the per-node ceiling times
|
|
30
|
+
* the partition ceiling the process ring already declares (16). 2 880 readings
|
|
31
|
+
* of ten numbers each is well under a megabyte — the bound exists so a runaway
|
|
32
|
+
* node id space costs a fixed amount, not because the number is large.
|
|
33
|
+
*/
|
|
34
|
+
export declare const CONTAINER_MEMORY_MAX_TOTAL_ROWS: number;
|
|
35
|
+
/**
|
|
36
|
+
* Hard ceiling on the durable table, in ROWS. The guarantee behind the
|
|
37
|
+
* retention intention, exactly as `DEFAULT_MAX_ROWS` is for the process table.
|
|
38
|
+
*
|
|
39
|
+
* | cadence | retention | rows (3 nodes) |
|
|
40
|
+
* | --- | --- | --- |
|
|
41
|
+
* | 10 s | 6 h (default) | 6 480 |
|
|
42
|
+
* | 5 s | 6 h | 12 960 |
|
|
43
|
+
* | 5 s | 72 h, 16 nodes | 829 440 — capped here |
|
|
44
|
+
*
|
|
45
|
+
* 50 000 rows is 7.7x the shipped default. This row is strictly narrower than
|
|
46
|
+
* a `metrics:node-load-samples` row — fewer columns, one index, every value an
|
|
47
|
+
* integer varint — and that one was MEASURED at 87 B/row, so 50 000 rows is
|
|
48
|
+
* **under 4.2 MiB** with the real figure lower still. It is not a knob: a
|
|
49
|
+
* guarantee that moves with the hand that raised the intention is not one.
|
|
50
|
+
*/
|
|
51
|
+
export declare const CONTAINER_MEMORY_MAX_ROWS = 50000;
|
|
52
|
+
/** Rows one prune pass may remove. Bounded, never a full scan. */
|
|
53
|
+
export declare const CONTAINER_MEMORY_PRUNE_PAGE_ROWS = 20000;
|
|
54
|
+
export declare const CONTAINER_MEMORY_COLUMNS: readonly [{
|
|
55
|
+
readonly name: "id";
|
|
56
|
+
readonly type: "INTEGER";
|
|
57
|
+
readonly primaryKey: true;
|
|
58
|
+
readonly notNull: true;
|
|
59
|
+
}, {
|
|
60
|
+
readonly name: "nodeId";
|
|
61
|
+
readonly type: "TEXT";
|
|
62
|
+
readonly notNull: true;
|
|
63
|
+
}, {
|
|
64
|
+
readonly name: "atMs";
|
|
65
|
+
readonly type: "INTEGER";
|
|
66
|
+
readonly notNull: true;
|
|
67
|
+
}, {
|
|
68
|
+
readonly name: "source";
|
|
69
|
+
readonly type: "TEXT";
|
|
70
|
+
readonly notNull: true;
|
|
71
|
+
}, {
|
|
72
|
+
readonly name: "currentMib";
|
|
73
|
+
readonly type: "INTEGER";
|
|
74
|
+
readonly notNull: true;
|
|
75
|
+
}, {
|
|
76
|
+
readonly name: "limitMib";
|
|
77
|
+
readonly type: "INTEGER";
|
|
78
|
+
}, {
|
|
79
|
+
readonly name: "anonMib";
|
|
80
|
+
readonly type: "INTEGER";
|
|
81
|
+
}, {
|
|
82
|
+
readonly name: "fileMib";
|
|
83
|
+
readonly type: "INTEGER";
|
|
84
|
+
}, {
|
|
85
|
+
readonly name: "shmemMib";
|
|
86
|
+
readonly type: "INTEGER";
|
|
87
|
+
}, {
|
|
88
|
+
readonly name: "slabMib";
|
|
89
|
+
readonly type: "INTEGER";
|
|
90
|
+
}, {
|
|
91
|
+
readonly name: "gpuShmemMib";
|
|
92
|
+
readonly type: "INTEGER";
|
|
93
|
+
}];
|
|
94
|
+
export declare const CONTAINER_MEMORY_INDEXES: readonly [{
|
|
95
|
+
readonly name: "idx_node_memory_node_at";
|
|
96
|
+
readonly columns: readonly ["nodeId", "atMs"];
|
|
97
|
+
}];
|
|
98
|
+
/** One stored row, in the units the table stores them in. */
|
|
99
|
+
export interface ContainerMemoryRow {
|
|
100
|
+
readonly nodeId: string;
|
|
101
|
+
readonly atMs: number;
|
|
102
|
+
readonly source: string;
|
|
103
|
+
readonly currentMib: number;
|
|
104
|
+
readonly limitMib: number | null;
|
|
105
|
+
readonly anonMib: number | null;
|
|
106
|
+
readonly fileMib: number | null;
|
|
107
|
+
readonly shmemMib: number | null;
|
|
108
|
+
readonly slabMib: number | null;
|
|
109
|
+
readonly gpuShmemMib: number | null;
|
|
110
|
+
}
|
|
111
|
+
/** The ONE encode. */
|
|
112
|
+
export declare function snapshotToRow(nodeId: string, atMs: number, snapshot: ContainerMemorySnapshot): ContainerMemoryRow;
|
|
113
|
+
/**
|
|
114
|
+
* The ONE decode. A row whose `source` is not a hierarchy this build knows is
|
|
115
|
+
* skipped rather than coerced — a stored string that no longer means anything
|
|
116
|
+
* must not be charted as though it did.
|
|
117
|
+
*/
|
|
118
|
+
export declare function rowToPoint(row: ContainerMemoryRow): ContainerMemoryPoint | null;
|
|
119
|
+
/** Structural validation on read. A malformed row is skipped, never charted. */
|
|
120
|
+
export declare function recordToMemoryRow(data: Record<string, unknown>): ContainerMemoryRow | null;
|
|
121
|
+
export interface ContainerMemoryRingOptions {
|
|
122
|
+
readonly samplesPerNode?: number;
|
|
123
|
+
/**
|
|
124
|
+
* Injectable clock, forwarded to the ring's idle-partition sweep. Tests
|
|
125
|
+
* drive eviction without sleeping for an hour; production uses `Date.now`.
|
|
126
|
+
*/
|
|
127
|
+
readonly now?: () => number;
|
|
128
|
+
}
|
|
129
|
+
/**
|
|
130
|
+
* The HOT tier — a naming adapter over {@link NodeLoadRing}, not a second ring.
|
|
131
|
+
*
|
|
132
|
+
* One reading per sample, so the ring's "rows" and its samples are the same
|
|
133
|
+
* count and its fleet budget is a sample budget. Every bound, the idle sweep
|
|
134
|
+
* and the monotonic replay gate come from there unchanged.
|
|
135
|
+
*/
|
|
136
|
+
export declare class ContainerMemoryRing {
|
|
137
|
+
private readonly ring;
|
|
138
|
+
constructor(options?: ContainerMemoryRingOptions);
|
|
139
|
+
/** Retain one reading. `false` = a timestamp this node already delivered. */
|
|
140
|
+
record(nodeId: string, atMs: number, snapshot: ContainerMemorySnapshot): boolean;
|
|
141
|
+
/**
|
|
142
|
+
* Read one node's retained readings, oldest-first. `sinceMs` is EXCLUSIVE,
|
|
143
|
+
* matching every other reader in this subsystem.
|
|
144
|
+
*
|
|
145
|
+
* A node nobody has recorded answers EMPTY — which is what "this node has no
|
|
146
|
+
* cgroup" looks like, and it must stay distinguishable from a zero.
|
|
147
|
+
*/
|
|
148
|
+
read(nodeId: string, sinceMs?: number): readonly ContainerMemoryPoint[];
|
|
149
|
+
}
|
|
150
|
+
/**
|
|
151
|
+
* Merge the two tiers and reduce, oldest-first.
|
|
152
|
+
*
|
|
153
|
+
* Dedupe is on `atMs` with the HOT copy winning, exactly as the process reader
|
|
154
|
+
* does: the ring holds the reading as it arrived, the table holds it after the
|
|
155
|
+
* MiB encode.
|
|
156
|
+
*
|
|
157
|
+
* Reduction keeps, for each bucket, the reading with the largest
|
|
158
|
+
* `currentBytes` — WHOLE, every field from that one sample. A per-field
|
|
159
|
+
* maximum would synthesise a row whose parts never coexisted (an `anon` peak
|
|
160
|
+
* and a `file` peak minutes apart, summed into a total that never happened),
|
|
161
|
+
* and a mean would smear away the spike this series exists to find. `atMs`
|
|
162
|
+
* becomes the bucket START so the points lie on the same x-axis as the folded
|
|
163
|
+
* process series, which is the only reason both travel in one answer.
|
|
164
|
+
*
|
|
165
|
+
* An empty bucket stays ABSENT — never zero, never carried forward.
|
|
166
|
+
*/
|
|
167
|
+
export declare function reduceContainerMemory(points: readonly ContainerMemoryPoint[], bucketMs: number, origin: number): readonly ContainerMemoryPoint[];
|
|
168
|
+
/** Merge cold + hot on `atMs`, hot wins. Exported because dedup is the
|
|
169
|
+
* property worth testing on its own. */
|
|
170
|
+
export declare function mergeContainerMemory(cold: readonly ContainerMemoryPoint[], hot: readonly ContainerMemoryPoint[]): readonly ContainerMemoryPoint[];
|
|
171
|
+
export interface ContainerMemoryStoreDeps {
|
|
172
|
+
readonly store: SettingsStoreClient;
|
|
173
|
+
readonly logger: IScopedLogger;
|
|
174
|
+
readonly now?: () => number;
|
|
175
|
+
readonly prunePageRows?: number;
|
|
176
|
+
}
|
|
177
|
+
/** What one prune pass did. Returned so a test can assert the bound held. */
|
|
178
|
+
export interface ContainerMemoryPruneOutcome {
|
|
179
|
+
readonly deletedByAge: number;
|
|
180
|
+
readonly deletedByCap: number;
|
|
181
|
+
readonly rowsExamined: number;
|
|
182
|
+
readonly capBit: boolean;
|
|
183
|
+
}
|
|
184
|
+
/**
|
|
185
|
+
* The COLD tier. Append-only, bounded, and never the authority on anything the
|
|
186
|
+
* hot ring can answer.
|
|
187
|
+
*
|
|
188
|
+
* Every method is best-effort and says so in the log rather than throwing at
|
|
189
|
+
* the emitter: a storage hiccup must cost a gap in a chart, never a dropped
|
|
190
|
+
* snapshot on the bus.
|
|
191
|
+
*/
|
|
192
|
+
export declare class ContainerMemoryStore {
|
|
193
|
+
private declared;
|
|
194
|
+
private readonly store;
|
|
195
|
+
private readonly logger;
|
|
196
|
+
private readonly nowFn;
|
|
197
|
+
private readonly prunePageRows;
|
|
198
|
+
constructor(deps: ContainerMemoryStoreDeps);
|
|
199
|
+
/** Idempotently declare the collection. `false` when the store refused. */
|
|
200
|
+
declare(): Promise<boolean>;
|
|
201
|
+
/** Append ONE reading. */
|
|
202
|
+
append(nodeId: string, atMs: number, snapshot: ContainerMemorySnapshot): Promise<number>;
|
|
203
|
+
/** Read one node's cold readings, oldest-first. `sinceMs` is EXCLUSIVE. */
|
|
204
|
+
read(nodeId: string, sinceMs: number, limitRows: number): Promise<readonly ContainerMemoryPoint[]>;
|
|
205
|
+
/**
|
|
206
|
+
* Enforce BOTH bounds, oldest-first, through a bounded page each.
|
|
207
|
+
*
|
|
208
|
+
* Not rate-limited here: it is driven by the process store's own prune
|
|
209
|
+
* cadence, so it runs at most once a minute by construction and a second
|
|
210
|
+
* timer would be a second thing to get wrong.
|
|
211
|
+
*/
|
|
212
|
+
prune(nodeIds: readonly string[], retention: LoadSeriesRetention): Promise<ContainerMemoryPruneOutcome | null>;
|
|
213
|
+
/** Total rows, or `null` when the store could not answer. */
|
|
214
|
+
count(): Promise<number | null>;
|
|
215
|
+
/**
|
|
216
|
+
* Delete this node's oldest rows, at most one page's worth.
|
|
217
|
+
*
|
|
218
|
+
* `cutoff === null` means "the oldest rows, whatever their age" — the
|
|
219
|
+
* row-cap path, which is bounded by `limitRows` instead. Either way the page
|
|
220
|
+
* is READ FIRST, ordered by the one index, and its last `atMs` becomes the
|
|
221
|
+
* effective cutoff for a single bounded delete. An unbounded `deleteWhere`
|
|
222
|
+
* would be one statement and one stalling transaction the first time a
|
|
223
|
+
* retention is lowered.
|
|
224
|
+
*/
|
|
225
|
+
private deleteOldestPage;
|
|
226
|
+
}
|
|
@@ -48,6 +48,20 @@ export default class NativeMetricsAddon extends BaseAddon<NativeMetricsConfig> {
|
|
|
48
48
|
* per sample, to reach that very same table.
|
|
49
49
|
*/
|
|
50
50
|
private loadStore;
|
|
51
|
+
/**
|
|
52
|
+
* The container's OWN memory, sampled on the process-snapshot tick.
|
|
53
|
+
*
|
|
54
|
+
* It rides that tick rather than a timer of its own so a memory reading and
|
|
55
|
+
* the process rows it must be compared against carry the SAME timestamp —
|
|
56
|
+
* the whole question is a subtraction, and two clocks make it an estimate.
|
|
57
|
+
* Constructed in `onInitialize`, where the logger exists; it resolves the
|
|
58
|
+
* cgroup once and latches.
|
|
59
|
+
*/
|
|
60
|
+
private memoryProbe;
|
|
61
|
+
/** Hot tier for the container reading. Bounds in `container-memory-series.ts`. */
|
|
62
|
+
private readonly memoryRing;
|
|
63
|
+
/** Cold tier, HUB only — same reasoning as `loadStore`. */
|
|
64
|
+
private memoryStore;
|
|
51
65
|
/** The resolved knobs. Re-resolved on every settings write. */
|
|
52
66
|
private loadConfig;
|
|
53
67
|
/**
|
|
@@ -97,6 +111,18 @@ export default class NativeMetricsAddon extends BaseAddon<NativeMetricsConfig> {
|
|
|
97
111
|
* cost this subsystem exists to avoid.
|
|
98
112
|
*/
|
|
99
113
|
private readLoadSeries;
|
|
114
|
+
/**
|
|
115
|
+
* The container's memory over the same window, on the same x-axis.
|
|
116
|
+
*
|
|
117
|
+
* Both tiers, merged and deduped on `atMs` like the process series, then
|
|
118
|
+
* reduced against the SAME `bucketMs` and the SAME origin — otherwise the
|
|
119
|
+
* two series in one answer would sit on two different axes and the
|
|
120
|
+
* subtraction they exist for would be an eyeball.
|
|
121
|
+
*
|
|
122
|
+
* An EMPTY answer means the node has no cgroup, or nothing has been retained
|
|
123
|
+
* for it yet. It never means zero.
|
|
124
|
+
*/
|
|
125
|
+
private readContainerMemory;
|
|
100
126
|
/**
|
|
101
127
|
* Re-resolve the knobs and restate the fixed cadence.
|
|
102
128
|
*
|