ux(admin/system): make the Load gauge capacity-aware, not alarmist

The Load ring was driven by load1_percent = min(100, load1/cores*100)
and coloured by the generic gauge thresholds (red at >=90%). On a
low-core box that pinned it red whenever load merely approached the core
count — which is normal "fully used" territory, not a problem.

Drive the ring from raw load1 with max = cores*2 (so load == cores sits
mid-gauge) and colour by load-per-core: green below capacity, orange
around capacity (>=1.0x), red only once load clearly exceeds it (>=1.7x,
tasks genuinely queuing). cpu.cores rides the live SSE payload, so the
colour is correct on live ticks too.

Signed-off-by: librelad <librelad@digitalangels.vip>
This commit is contained in:
librelad 2026-05-28 16:28:42 +01:00
parent ec5b1735dc
commit 7178dddae7

View File

@ -201,11 +201,26 @@ class AdminSystem {
<svg viewBox="0 0 24 24" width="14" height="14"><path d="M3 9V3h6M21 9V3h-6M3 15v6h6M21 15v6h-6" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/></svg> <svg viewBox="0 0 24 24" width="14" height="14"><path d="M3 9V3h6M21 9V3h-6M3 15v6h6M21 15v6h-6" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/></svg>
</span> </span>
</button>`; </button>`;
// Load is read relative to core count: a load equal to the number of
// cores means "fully used, no queue" — normal, not alarming. Only flag
// it red once load clearly exceeds capacity (tasks genuinely queuing).
// The ring fills toward 2x cores so load == cores sits mid-gauge
// instead of maxing out (which read as a constant red alarm on a
// low-core box). The old backend load1_percent capped at cores == 100%,
// so anything near capacity pinned the gauge red.
const cores = cpu.cores || 1;
const load1 = Number(cpu.load1 ?? 0);
const loadRatio = load1 / cores;
const loadColor = loadRatio >= 1.7 ? 'status-danger'
: loadRatio >= 1.0 ? 'status-warning'
: 'status-success';
return ` return `
${wrap('cpu', C.gauge(cpu.percent || 0, { label: 'CPU', sublabel: `${cpu.cores || '?'} cores` }))} ${wrap('cpu', C.gauge(cpu.percent || 0, { label: 'CPU', sublabel: `${cpu.cores || '?'} cores` }))}
${wrap('mem', C.gauge(mem.percent || 0, { label: 'Memory', sublabel: `${this.bytes(mem.used)} / ${this.bytes(mem.total)}` }))} ${wrap('mem', C.gauge(mem.percent || 0, { label: 'Memory', sublabel: `${this.bytes(mem.used)} / ${this.bytes(mem.total)}` }))}
${wrap('disk', C.gauge(rootDisk.percent || 0, { label: 'Disk', sublabel: rootDisk.mount || '/' }))} ${wrap('disk', C.gauge(rootDisk.percent || 0, { label: 'Disk', sublabel: rootDisk.mount || '/' }))}
${wrap('load1', C.gauge(cpu.load1_percent || 0, { label: 'Load', display: (cpu.load1 ?? 0), suffix: '', sublabel: `1m · ${cpu.load5 ?? ''}/${cpu.load15 ?? ''}` }))}`; ${wrap('load1', C.gauge(load1, { label: 'Load', display: load1.toFixed(2), suffix: '', max: cores * 2, color: loadColor, sublabel: `1m · ${cpu.load5 ?? ''}/${cpu.load15 ?? ''}` }))}`;
} }
_applyLive(s) { _applyLive(s) {