@teamscala/service-boot 0.23.337 → 0.23.339
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/package.json
CHANGED
|
@@ -19,3 +19,6 @@ export declare function checkDirStale(dir: string, cutoffMs: number): Promise<{
|
|
|
19
19
|
bytes: number;
|
|
20
20
|
fileCount: number;
|
|
21
21
|
}>;
|
|
22
|
+
/** Parse the root-fs Use% from `df --output=pcent,target` output (pure, for tests).
|
|
23
|
+
* Returns null if no root-mount line is found. */
|
|
24
|
+
export declare function parseRootDiskPct(out: string): number | null;
|
|
@@ -14,7 +14,7 @@ import { describe, expect, test, beforeEach, afterEach } from "bun:test";
|
|
|
14
14
|
import { mkdtempSync, mkdirSync, writeFileSync, rmSync, utimesSync } from "node:fs";
|
|
15
15
|
import { join } from "node:path";
|
|
16
16
|
import { tmpdir } from "node:os";
|
|
17
|
-
import { expandGlob, checkDirStale } from "./sweep-host-cache.ts";
|
|
17
|
+
import { expandGlob, checkDirStale, parseRootDiskPct } from "./sweep-host-cache.ts";
|
|
18
18
|
|
|
19
19
|
const DAY = 86_400_000;
|
|
20
20
|
const now = Date.now();
|
|
@@ -83,3 +83,28 @@ describe("checkDirStale", () => {
|
|
|
83
83
|
expect(res.fileCount).toBe(0);
|
|
84
84
|
});
|
|
85
85
|
});
|
|
86
|
+
|
|
87
|
+
describe("parseRootDiskPct", () => {
|
|
88
|
+
test("extracts root-fs Use% from df --output=pcent,target output", () => {
|
|
89
|
+
const df =
|
|
90
|
+
"Use% Mounted on\n" +
|
|
91
|
+
" 97% /\n" +
|
|
92
|
+
" 45% /home\n";
|
|
93
|
+
expect(parseRootDiskPct(df)).toBe(97);
|
|
94
|
+
});
|
|
95
|
+
test("picks the root mount (/) over nested mounts", () => {
|
|
96
|
+
const df =
|
|
97
|
+
"Use% Mounted on\n" +
|
|
98
|
+
" 10% /proc/x\n" +
|
|
99
|
+
" 76% /\n";
|
|
100
|
+
expect(parseRootDiskPct(df)).toBe(76);
|
|
101
|
+
});
|
|
102
|
+
test("returns null when no root-mount line present", () => {
|
|
103
|
+
const df = "Use% Mounted on\n 40% /mnt/other\n";
|
|
104
|
+
expect(parseRootDiskPct(df)).toBeNull();
|
|
105
|
+
});
|
|
106
|
+
test("returns null on empty / malformed output", () => {
|
|
107
|
+
expect(parseRootDiskPct("")).toBeNull();
|
|
108
|
+
expect(parseRootDiskPct("Use% Mounted on")).toBeNull();
|
|
109
|
+
});
|
|
110
|
+
});
|
|
@@ -200,6 +200,14 @@ export async function checkDirStale(
|
|
|
200
200
|
return { stale: true, bytes, fileCount };
|
|
201
201
|
}
|
|
202
202
|
|
|
203
|
+
/** Parse the root-fs Use% from `df --output=pcent,target` output (pure, for tests).
|
|
204
|
+
* Returns null if no root-mount line is found. */
|
|
205
|
+
export function parseRootDiskPct(out: string): number | null {
|
|
206
|
+
const rootLine = out.split("\n").find((l) => l.trim().endsWith("/"));
|
|
207
|
+
const m = rootLine?.match(/(\d+)%/);
|
|
208
|
+
return m ? Number(m[1]) : null;
|
|
209
|
+
}
|
|
210
|
+
|
|
203
211
|
async function sweepHostCache(ctx: {
|
|
204
212
|
config: { retention?: Record<string, string>; retention_dirs?: Record<string, string> };
|
|
205
213
|
}): Promise<void> {
|
|
@@ -290,32 +298,27 @@ async function sweepHostCache(ctx: {
|
|
|
290
298
|
},
|
|
291
299
|
});
|
|
292
300
|
|
|
293
|
-
// Disk-threshold
|
|
294
|
-
//
|
|
295
|
-
//
|
|
296
|
-
//
|
|
297
|
-
|
|
301
|
+
// Disk-threshold observability — rides this periodic sweep (NOT an interval:
|
|
302
|
+
// this job is genuinely periodic application-DATA work — deletion — and the
|
|
303
|
+
// df read piggybacks once per run). Emits through the event-driven surface:
|
|
304
|
+
// logger.warn -> GlitchTip (the paging path, same as host.pressure) +
|
|
305
|
+
// recordEvent for the durable event_log record. Catches a fill faster than
|
|
306
|
+
// the nightly prune can absorb (a cache with no retention entry, an external
|
|
307
|
+
// dump, a runaway log) so the operator learns it within a day, not at 100%.
|
|
308
|
+
const diskHighPct = 90;
|
|
298
309
|
try {
|
|
299
|
-
const dfRes = spawnSync({ name: "df:/", command: ["df", "--output=pcent,target"], timeoutMs:
|
|
310
|
+
const dfRes = spawnSync({ name: "df:/", command: ["df", "--output=pcent,target"], timeoutMs: 5_000 });
|
|
300
311
|
const dfOut = (dfRes.stdout ?? "").trim();
|
|
301
|
-
const
|
|
302
|
-
|
|
303
|
-
.
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
log.warn(
|
|
309
|
-
`[host-cache-sweep] disk usage ${usePct}% >= ${highPercent}% threshold — emitting host.disk.high`,
|
|
310
|
-
);
|
|
311
|
-
recordEvent({
|
|
312
|
-
kind: "host.disk.high",
|
|
313
|
-
payload: { usePct, threshold: highPercent, df: dfOut, completedAt: new Date().toISOString() },
|
|
314
|
-
});
|
|
315
|
-
}
|
|
312
|
+
const usePct = parseRootDiskPct(dfOut);
|
|
313
|
+
if (usePct !== null && usePct >= diskHighPct) {
|
|
314
|
+
log.warn("host.disk.high", { usePct, threshold: diskHighPct, df: dfOut });
|
|
315
|
+
recordEvent({
|
|
316
|
+
kind: "host.disk.high",
|
|
317
|
+
payload: { usePct, threshold: diskHighPct, df: dfOut, completedAt: new Date().toISOString() },
|
|
318
|
+
});
|
|
316
319
|
}
|
|
317
320
|
} catch (error) {
|
|
318
|
-
log.warn(
|
|
321
|
+
log.warn("[host-cache-sweep] disk-threshold read failed: " + String(error));
|
|
319
322
|
}
|
|
320
323
|
}
|
|
321
324
|
|