mirror of
https://github.com/Studio-Saelix/sencho.git
synced 2026-08-25 09:46:47 +00:00
fix(metrics): host memory usage excludes reclaimable page cache (#1297)
* fix(metrics): host memory usage excludes reclaimable page cache Host RAM was computed as mem.used / mem.total via systeminformation, but mem.used counts reclaimable buffers/cache as used, so a busy Linux host read ~99%. Switch the dashboard stats, the fleet node self-report, and the host-RAM alert threshold to mem.active / mem.total (cache-excluded), and report used: mem.active and free: mem.available so the byte readout stays consistent with the percentage. Add regression tests for a cache-heavy host (no false alert) and a genuinely busy host (alert still fires). * test(metrics): assert system stats memory excludes reclaimable cache The cached /api/system/stats test mocked si.mem() without active/available, so after the route switched to the cache-excluded fields it produced a NaN percentage that the shape-only assertions did not catch. Fill the mock with a realistic shape and assert the route reports the active working set, not the cache-inclusive used/free.
This commit is contained in:
@@ -111,7 +111,10 @@ beforeEach(() => {
|
||||
mockGetBulkStackStatuses.mockResolvedValue({});
|
||||
mockGetStacks.mockResolvedValue([]);
|
||||
mockCurrentLoad.mockResolvedValue({ currentLoad: 42.5, cpus: [{}, {}] });
|
||||
mockMem.mockResolvedValue({ total: 1000, used: 500, free: 500 });
|
||||
// active/available exclude reclaimable cache; used/free include it. The route
|
||||
// reports the cache-excluded figures, so the mock supplies the full shape:
|
||||
// active(400) + available(600) = total; used(500) = active + 100 cache.
|
||||
mockMem.mockResolvedValue({ total: 1000, used: 500, active: 400, free: 500, available: 600, buffcache: 100 });
|
||||
mockFsSize.mockResolvedValue([{ fs: '/dev/sda1', mount: '/', size: 1000, used: 500, available: 500, use: 50 }]);
|
||||
});
|
||||
|
||||
@@ -169,6 +172,19 @@ describe('GET /api/system/stats caching', () => {
|
||||
// CPU/mem/disk sample is cached; network is fresh per request.
|
||||
expect(mockCurrentLoad).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
it('reports memory from the active working set, excluding reclaimable cache', async () => {
|
||||
const res = await request(app).get('/api/system/stats').set('Cookie', authCookie);
|
||||
expect(res.status).toBe(200);
|
||||
// Figures come from mem.active / mem.available (cache-excluded), not the
|
||||
// cache-inclusive mem.used / mem.free, so a busy host does not read ~100%.
|
||||
expect(res.body.memory).toMatchObject({
|
||||
total: 1000,
|
||||
used: 400, // mem.active, not mem.used (500)
|
||||
free: 600, // mem.available, not mem.free (500)
|
||||
usagePercent: '40.0', // 400 / 1000, not 500 / 1000
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
// ── /api/stacks/statuses ───────────────────────────────────────────────
|
||||
|
||||
Reference in New Issue
Block a user