@glassmkr/crucible 0.5.0 → 0.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/dist/collect/io-latency.d.ts +8 -0
- package/dist/collect/io-latency.js +80 -0
- package/dist/collect/io-latency.js.map +1 -0
- package/dist/index.js +5 -0
- package/dist/index.js.map +1 -1
- package/dist/lib/types.d.ts +7 -0
- package/package.json +1 -1
- package/src/collect/io-latency.ts +103 -0
- package/src/index.ts +2 -0
- package/src/lib/types.ts +1 -0
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
import { readProcFile } from "../lib/parse.js";
|
|
2
|
+
// Previous cumulative counters for delta computation
|
|
3
|
+
const previousCounters = new Map();
|
|
4
|
+
// Match physical block devices, not partitions or virtual devices
|
|
5
|
+
function isPhysicalDevice(name) {
|
|
6
|
+
// sd*, vd*, xvd* without trailing partition number
|
|
7
|
+
if (/^(sd|vd|xvd)[a-z]+$/.test(name))
|
|
8
|
+
return true;
|
|
9
|
+
// nvme*n* without partition suffix (nvme0n1 yes, nvme0n1p1 no)
|
|
10
|
+
if (/^nvme\d+n\d+$/.test(name))
|
|
11
|
+
return true;
|
|
12
|
+
// md* (RAID arrays)
|
|
13
|
+
if (/^md\d+$/.test(name))
|
|
14
|
+
return true;
|
|
15
|
+
return false;
|
|
16
|
+
}
|
|
17
|
+
function parseDiskstats() {
|
|
18
|
+
const raw = readProcFile("/proc/diskstats") || "";
|
|
19
|
+
const result = {};
|
|
20
|
+
for (const line of raw.split("\n")) {
|
|
21
|
+
const parts = line.trim().split(/\s+/);
|
|
22
|
+
if (parts.length < 11)
|
|
23
|
+
continue;
|
|
24
|
+
const name = parts[2];
|
|
25
|
+
if (!isPhysicalDevice(name))
|
|
26
|
+
continue;
|
|
27
|
+
result[name] = {
|
|
28
|
+
reads_completed: Number(parts[3]) || 0,
|
|
29
|
+
read_time_ms: Number(parts[6]) || 0,
|
|
30
|
+
writes_completed: Number(parts[7]) || 0,
|
|
31
|
+
write_time_ms: Number(parts[10]) || 0,
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
return result;
|
|
35
|
+
}
|
|
36
|
+
function delta(current, previous) {
|
|
37
|
+
if (current >= previous)
|
|
38
|
+
return current - previous;
|
|
39
|
+
return current; // counter wrapped or reset
|
|
40
|
+
}
|
|
41
|
+
export function collectIoLatency() {
|
|
42
|
+
const current = parseDiskstats();
|
|
43
|
+
const results = [];
|
|
44
|
+
const currentDevices = new Set();
|
|
45
|
+
for (const [name, counters] of Object.entries(current)) {
|
|
46
|
+
currentDevices.add(name);
|
|
47
|
+
const prev = previousCounters.get(name);
|
|
48
|
+
// Store current for next cycle
|
|
49
|
+
previousCounters.set(name, { ...counters });
|
|
50
|
+
if (!prev) {
|
|
51
|
+
// First cycle: no delta, report null latency
|
|
52
|
+
results.push({
|
|
53
|
+
device: name,
|
|
54
|
+
avg_read_latency_ms: null,
|
|
55
|
+
avg_write_latency_ms: null,
|
|
56
|
+
read_iops: 0,
|
|
57
|
+
write_iops: 0,
|
|
58
|
+
});
|
|
59
|
+
continue;
|
|
60
|
+
}
|
|
61
|
+
const deltaReads = delta(counters.reads_completed, prev.reads_completed);
|
|
62
|
+
const deltaReadTime = delta(counters.read_time_ms, prev.read_time_ms);
|
|
63
|
+
const deltaWrites = delta(counters.writes_completed, prev.writes_completed);
|
|
64
|
+
const deltaWriteTime = delta(counters.write_time_ms, prev.write_time_ms);
|
|
65
|
+
results.push({
|
|
66
|
+
device: name,
|
|
67
|
+
avg_read_latency_ms: deltaReads > 0 ? Math.round((deltaReadTime / deltaReads) * 100) / 100 : null,
|
|
68
|
+
avg_write_latency_ms: deltaWrites > 0 ? Math.round((deltaWriteTime / deltaWrites) * 100) / 100 : null,
|
|
69
|
+
read_iops: deltaReads,
|
|
70
|
+
write_iops: deltaWrites,
|
|
71
|
+
});
|
|
72
|
+
}
|
|
73
|
+
// Remove stale devices
|
|
74
|
+
for (const name of previousCounters.keys()) {
|
|
75
|
+
if (!currentDevices.has(name))
|
|
76
|
+
previousCounters.delete(name);
|
|
77
|
+
}
|
|
78
|
+
return results;
|
|
79
|
+
}
|
|
80
|
+
//# sourceMappingURL=io-latency.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"io-latency.js","sourceRoot":"","sources":["../../src/collect/io-latency.ts"],"names":[],"mappings":"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"}
|
package/dist/index.js
CHANGED
|
@@ -33,6 +33,7 @@ import { pushToForge, initForgeAgent } from "./push/forge.js";
|
|
|
33
33
|
import { collectSecurity } from "./collect/security.js";
|
|
34
34
|
import { collectZfs } from "./collect/zfs.js";
|
|
35
35
|
import { collectIoErrors } from "./collect/io-errors.js";
|
|
36
|
+
import { collectIoLatency } from "./collect/io-latency.js";
|
|
36
37
|
const configPath = process.argv[2] || "/etc/glassmkr/collector.yaml";
|
|
37
38
|
const config = loadConfig(configPath);
|
|
38
39
|
console.log(`[collector] Starting. Server: ${config.server_name}. Interval: ${config.collection.interval_seconds}s`);
|
|
@@ -92,6 +93,10 @@ async function collect() {
|
|
|
92
93
|
snapshot.io_errors = await collectIoErrors() ?? undefined;
|
|
93
94
|
}
|
|
94
95
|
catch { /* skip on error */ }
|
|
96
|
+
try {
|
|
97
|
+
snapshot.io_latency = collectIoLatency();
|
|
98
|
+
}
|
|
99
|
+
catch { /* skip on error */ }
|
|
95
100
|
// Update Prometheus metrics
|
|
96
101
|
updateMetrics(snapshot);
|
|
97
102
|
// Evaluate alerts
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AAEA,OAAO,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AACvC,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AACzC,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAC1C,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAEzC,MAAM,SAAS,GAAG,OAAO,CAAC,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC;AAC1D,MAAM,WAAW,GAAG,CAAC,GAAG,EAAE;IACxB,IAAI,CAAC;QACH,MAAM,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,IAAI,CAAC,SAAS,EAAE,IAAI,EAAE,cAAc,CAAC,EAAE,MAAM,CAAC,CAAC,CAAC;QACpF,OAAO,GAAG,CAAC,OAAO,IAAI,OAAO,CAAC;IAChC,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,OAAO,CAAC;IACjB,CAAC;AACH,CAAC,CAAC,EAAE,CAAC;AACL,OAAO,EAAE,eAAe,EAAE,MAAM,wBAAwB,CAAC;AACzD,OAAO,EAAE,kBAAkB,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AACxE,OAAO,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AACpD,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAC9C,OAAO,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AACpD,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAClD,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAClD,OAAO,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AACtD,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAChD,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAChD,OAAO,EAAE,eAAe,EAAE,MAAM,wBAAwB,CAAC;AACzD,OAAO,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AACvD,OAAO,EAAE,gBAAgB,EAAE,MAAM,mBAAmB,CAAC;AACrD,OAAO,EAAE,YAAY,EAAE,MAAM,sBAAsB,CAAC;AACpD,OAAO,EAAE,SAAS,EAAE,MAAM,mBAAmB,CAAC;AAC9C,OAAO,EAAE,SAAS,EAAE,MAAM,mBAAmB,CAAC;AAC9C,OAAO,EAAE,WAAW,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAC;AAC9D,OAAO,EAAE,eAAe,EAAqB,MAAM,uBAAuB,CAAC;AAC3E,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAC9C,OAAO,EAAE,eAAe,EAAE,MAAM,wBAAwB,CAAC;
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
package/dist/lib/types.d.ts
CHANGED
|
@@ -16,6 +16,13 @@ export interface Snapshot {
|
|
|
16
16
|
count: number;
|
|
17
17
|
devices: string[];
|
|
18
18
|
};
|
|
19
|
+
io_latency?: Array<{
|
|
20
|
+
device: string;
|
|
21
|
+
avg_read_latency_ms: number | null;
|
|
22
|
+
avg_write_latency_ms: number | null;
|
|
23
|
+
read_iops: number;
|
|
24
|
+
write_iops: number;
|
|
25
|
+
}>;
|
|
19
26
|
}
|
|
20
27
|
export interface ZfsPool {
|
|
21
28
|
name: string;
|
package/package.json
CHANGED
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
import { readProcFile } from "../lib/parse.js";
|
|
2
|
+
|
|
3
|
+
export interface IoLatencyInfo {
|
|
4
|
+
device: string;
|
|
5
|
+
avg_read_latency_ms: number | null;
|
|
6
|
+
avg_write_latency_ms: number | null;
|
|
7
|
+
read_iops: number;
|
|
8
|
+
write_iops: number;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
interface DiskstatsCounters {
|
|
12
|
+
reads_completed: number;
|
|
13
|
+
read_time_ms: number;
|
|
14
|
+
writes_completed: number;
|
|
15
|
+
write_time_ms: number;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
// Previous cumulative counters for delta computation
|
|
19
|
+
const previousCounters = new Map<string, DiskstatsCounters>();
|
|
20
|
+
|
|
21
|
+
// Match physical block devices, not partitions or virtual devices
|
|
22
|
+
function isPhysicalDevice(name: string): boolean {
|
|
23
|
+
// sd*, vd*, xvd* without trailing partition number
|
|
24
|
+
if (/^(sd|vd|xvd)[a-z]+$/.test(name)) return true;
|
|
25
|
+
// nvme*n* without partition suffix (nvme0n1 yes, nvme0n1p1 no)
|
|
26
|
+
if (/^nvme\d+n\d+$/.test(name)) return true;
|
|
27
|
+
// md* (RAID arrays)
|
|
28
|
+
if (/^md\d+$/.test(name)) return true;
|
|
29
|
+
return false;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
function parseDiskstats(): Record<string, DiskstatsCounters> {
|
|
33
|
+
const raw = readProcFile("/proc/diskstats") || "";
|
|
34
|
+
const result: Record<string, DiskstatsCounters> = {};
|
|
35
|
+
|
|
36
|
+
for (const line of raw.split("\n")) {
|
|
37
|
+
const parts = line.trim().split(/\s+/);
|
|
38
|
+
if (parts.length < 11) continue;
|
|
39
|
+
|
|
40
|
+
const name = parts[2];
|
|
41
|
+
if (!isPhysicalDevice(name)) continue;
|
|
42
|
+
|
|
43
|
+
result[name] = {
|
|
44
|
+
reads_completed: Number(parts[3]) || 0,
|
|
45
|
+
read_time_ms: Number(parts[6]) || 0,
|
|
46
|
+
writes_completed: Number(parts[7]) || 0,
|
|
47
|
+
write_time_ms: Number(parts[10]) || 0,
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
return result;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
function delta(current: number, previous: number): number {
|
|
55
|
+
if (current >= previous) return current - previous;
|
|
56
|
+
return current; // counter wrapped or reset
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
export function collectIoLatency(): IoLatencyInfo[] {
|
|
60
|
+
const current = parseDiskstats();
|
|
61
|
+
const results: IoLatencyInfo[] = [];
|
|
62
|
+
const currentDevices = new Set<string>();
|
|
63
|
+
|
|
64
|
+
for (const [name, counters] of Object.entries(current)) {
|
|
65
|
+
currentDevices.add(name);
|
|
66
|
+
const prev = previousCounters.get(name);
|
|
67
|
+
|
|
68
|
+
// Store current for next cycle
|
|
69
|
+
previousCounters.set(name, { ...counters });
|
|
70
|
+
|
|
71
|
+
if (!prev) {
|
|
72
|
+
// First cycle: no delta, report null latency
|
|
73
|
+
results.push({
|
|
74
|
+
device: name,
|
|
75
|
+
avg_read_latency_ms: null,
|
|
76
|
+
avg_write_latency_ms: null,
|
|
77
|
+
read_iops: 0,
|
|
78
|
+
write_iops: 0,
|
|
79
|
+
});
|
|
80
|
+
continue;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
const deltaReads = delta(counters.reads_completed, prev.reads_completed);
|
|
84
|
+
const deltaReadTime = delta(counters.read_time_ms, prev.read_time_ms);
|
|
85
|
+
const deltaWrites = delta(counters.writes_completed, prev.writes_completed);
|
|
86
|
+
const deltaWriteTime = delta(counters.write_time_ms, prev.write_time_ms);
|
|
87
|
+
|
|
88
|
+
results.push({
|
|
89
|
+
device: name,
|
|
90
|
+
avg_read_latency_ms: deltaReads > 0 ? Math.round((deltaReadTime / deltaReads) * 100) / 100 : null,
|
|
91
|
+
avg_write_latency_ms: deltaWrites > 0 ? Math.round((deltaWriteTime / deltaWrites) * 100) / 100 : null,
|
|
92
|
+
read_iops: deltaReads,
|
|
93
|
+
write_iops: deltaWrites,
|
|
94
|
+
});
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
// Remove stale devices
|
|
98
|
+
for (const name of previousCounters.keys()) {
|
|
99
|
+
if (!currentDevices.has(name)) previousCounters.delete(name);
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
return results;
|
|
103
|
+
}
|
package/src/index.ts
CHANGED
|
@@ -34,6 +34,7 @@ import { pushToForge, initForgeAgent } from "./push/forge.js";
|
|
|
34
34
|
import { collectSecurity, type SecurityData } from "./collect/security.js";
|
|
35
35
|
import { collectZfs } from "./collect/zfs.js";
|
|
36
36
|
import { collectIoErrors } from "./collect/io-errors.js";
|
|
37
|
+
import { collectIoLatency } from "./collect/io-latency.js";
|
|
37
38
|
import type { Snapshot, IpmiInfo } from "./lib/types.js";
|
|
38
39
|
|
|
39
40
|
const configPath = process.argv[2] || "/etc/glassmkr/collector.yaml";
|
|
@@ -94,6 +95,7 @@ async function collect() {
|
|
|
94
95
|
// ZFS and I/O errors: collect every cycle (lightweight checks)
|
|
95
96
|
try { snapshot.zfs = await collectZfs() ?? undefined; } catch { /* skip if ZFS not available */ }
|
|
96
97
|
try { snapshot.io_errors = await collectIoErrors() ?? undefined; } catch { /* skip on error */ }
|
|
98
|
+
try { snapshot.io_latency = collectIoLatency(); } catch { /* skip on error */ }
|
|
97
99
|
|
|
98
100
|
// Update Prometheus metrics
|
|
99
101
|
updateMetrics(snapshot);
|
package/src/lib/types.ts
CHANGED
|
@@ -13,6 +13,7 @@ export interface Snapshot {
|
|
|
13
13
|
security?: SecurityData;
|
|
14
14
|
zfs?: ZfsData;
|
|
15
15
|
io_errors?: { count: number; devices: string[] };
|
|
16
|
+
io_latency?: Array<{ device: string; avg_read_latency_ms: number | null; avg_write_latency_ms: number | null; read_iops: number; write_iops: number }>;
|
|
16
17
|
}
|
|
17
18
|
|
|
18
19
|
export interface ZfsPool {
|