landing: central container memory/load stats for all three sites

Collect RSS groups, top processes, and loadavg from inside each podman
container (not host /proc), merge into performance.memory, show compact
labels with cgroup limits and byte tooltips, and cover this in the test suite.
This commit is contained in:
Hernâni Marques 2026-07-17 08:55:00 +02:00
parent 9e0f85024b
commit 78bbd80d1f
No known key found for this signature in database
11 changed files with 474 additions and 69 deletions

View file

@ -2037,20 +2037,33 @@ tw run-until-done &amp;&amp; tw balance</pre>
if (http && http !== "200") s += " (" + http + ")";
return s;
}
function setMem(id, human, n, bytes) {
var el = document.getElementById(id);
if (!el) return;
if (!human && human !== 0) {
el.textContent = "—";
el.removeAttribute("title");
return;
}
var h = String(human);
if (n != null && n !== "" && Number(n) > 0) h += " · " + n + "p";
el.textContent = h;
if (bytes != null && bytes !== "" && bytes !== 0) {
el.setAttribute("title", Number(bytes).toLocaleString("en-US") + " B");
} else {
el.removeAttribute("title");
}
}
var p = d.performance || {};
set("st-config-ms", msLabel(p.config_ms, p.config_http));
set("st-int-ms", msLabel(p.integration_ms, p.integration_http));
set("st-webui-ms", msLabel(p.webui_ms, p.webui_http));
set("st-load", p.loadavg || "—");
var mem = p.memory || {};
set("st-mem-ctr", mem.container_rss_human || "—");
set("st-mem-pg", mem.postgres_rss_human
? mem.postgres_rss_human + (mem.postgres_n ? " · " + mem.postgres_n + "p" : "")
: "—");
set("st-mem-java", mem.java_rss_human
? mem.java_rss_human + (mem.java_n ? " · " + mem.java_n + "p" : "")
: "—");
set("st-mem-nginx", mem.nginx_rss_human || "—");
setMem("st-mem-ctr", mem.container_rss_label || mem.container_rss_human || "—", null, mem.container_rss_bytes);
setMem("st-mem-pg", mem.postgres_rss_human, mem.postgres_n, mem.postgres_rss_bytes);
setMem("st-mem-java", mem.java_rss_human, mem.java_n, mem.java_rss_bytes);
setMem("st-mem-nginx", mem.nginx_rss_human, mem.nginx_n, mem.nginx_rss_bytes);
var topBox = document.getElementById("st-mem-top");
if (topBox) {
var tops = mem.top || [];
@ -2062,10 +2075,16 @@ tw run-until-done &amp;&amp; tw balance</pre>
'<th style="text-align:left;padding:0.25rem;border-bottom:1px solid var(--border)">Comm</th>' +
'<th style="text-align:left;padding:0.25rem;border-bottom:1px solid var(--border)">Command</th></tr></thead><tbody>';
tops.forEach(function (t) {
th += '<tr><td style="padding:0.25rem;color:#5eead4">' + (t.rss_human || "?") +
var cmd = t.cmd || "";
var cmdFull = t.cmd_full || cmd;
if (cmd.length > 64) cmd = cmd.slice(0, 61) + "…";
th += '<tr><td style="padding:0.25rem;color:#5eead4" title="' +
(t.rss_bytes != null ? Number(t.rss_bytes).toLocaleString("en-US") + " B" : "") + '">' +
(t.rss_human || "?") +
'</td><td style="padding:0.25rem">' + (t.comm || "") +
'</td><td style="padding:0.25rem;word-break:break-all;color:var(--muted)">' +
(t.cmd || "") + "</td></tr>";
'</td><td style="padding:0.25rem;word-break:break-all;color:var(--muted)" title="' +
String(cmdFull).replace(/"/g, "&quot;") + '">' +
cmd + "</td></tr>";
});
th += "</tbody></table>";
topBox.innerHTML = th;
@ -2073,7 +2092,8 @@ tw run-until-done &amp;&amp; tw balance</pre>
}
var pf = document.getElementById("st-perf-foot");
if (pf) {
pf.textContent = "Container RSS + process groups · " +
pf.textContent = "Container RSS + process groups" +
(mem.source ? " · " + mem.source : "") + " · " +
(d.generated_at_human || d.generated_at || "");
}
return fetch("stats-run.json", { cache: "no-store" })

View file

@ -569,19 +569,30 @@ sudo apt-get install -y taler-wallet-cli</pre>
(d.generated_at_human || d.generated_at || "");
}
function setMem(id, human, n, bytes) {
var el = document.getElementById(id);
if (!el) return;
if (!human && human !== 0) {
el.textContent = "—";
el.removeAttribute("title");
return;
}
var h = String(human);
if (n != null && n !== "" && Number(n) > 0) h += " · " + n + "p";
el.textContent = h;
if (bytes != null && bytes !== "" && Number(bytes) > 0) {
el.setAttribute("title", Number(bytes).toLocaleString("en-US") + " B");
} else el.removeAttribute("title");
}
var p = d.performance || {};
set("st-keys-ms", msLabel(p.keys_ms, p.keys_http));
set("st-config-ms", msLabel(p.config_ms, p.config_http));
set("st-load", p.loadavg || "—");
var mem = p.memory || {};
set("st-mem-ctr", mem.container_rss_human || "—");
set("st-mem-pg", mem.postgres_rss_human
? mem.postgres_rss_human + (mem.postgres_n ? " · " + mem.postgres_n + "p" : "")
: "—");
set("st-mem-taler", mem.taler_rss_human
? mem.taler_rss_human + (mem.taler_n ? " · " + mem.taler_n + "p" : "")
: "—");
set("st-mem-nginx", mem.nginx_rss_human || "—");
setMem("st-mem-ctr", mem.container_rss_label || mem.container_rss_human || "—", null, mem.container_rss_bytes);
setMem("st-mem-pg", mem.postgres_rss_human, mem.postgres_n, mem.postgres_rss_bytes);
setMem("st-mem-taler", mem.taler_rss_human, mem.taler_n, mem.taler_rss_bytes);
setMem("st-mem-nginx", mem.nginx_rss_human, mem.nginx_n, mem.nginx_rss_bytes);
var topBox = document.getElementById("st-mem-top");
if (topBox) {
var tops = mem.top || [];
@ -590,9 +601,14 @@ sudo apt-get install -y taler-wallet-cli</pre>
} else {
var th = "<table><thead><tr><th>RSS</th><th>Comm</th><th>Command</th></tr></thead><tbody>";
tops.forEach(function (t) {
th += "<tr><td>" + (t.rss_human || "?") + "</td><td>" +
(t.comm || "") + "</td><td style=\"font-size:0.72rem;word-break:break-all\">" +
(t.cmd || "") + "</td></tr>";
var cmd = t.cmd || "";
var cmdFull = t.cmd_full || cmd;
if (cmd.length > 64) cmd = cmd.slice(0, 61) + "…";
th += "<tr><td title=\"" + (t.rss_bytes != null ? Number(t.rss_bytes).toLocaleString("en-US") + " B" : "") + "\">" +
(t.rss_human || "?") + "</td><td>" +
(t.comm || "") + "</td><td style=\"font-size:0.72rem;word-break:break-all\" title=\"" +
String(cmdFull).replace(/"/g, "&quot;") + "\">" +
cmd + "</td></tr>";
});
th += "</tbody></table>";
topBox.innerHTML = th;
@ -600,7 +616,8 @@ sudo apt-get install -y taler-wallet-cli</pre>
}
var pf = document.getElementById("st-perf-foot");
if (pf) {
pf.textContent = "Container RSS + process groups · " +
pf.textContent = "Container RSS + process groups" +
(mem.source ? " · " + mem.source : "") + " · " +
(d.generated_at_human || d.generated_at || "");
}
return fetch("/intro/stats-run.json", { cache: "no-store" })

View file

@ -973,20 +973,31 @@ sudo apt-get install -y taler-wallet-cli</pre>
foot.textContent = (d.dual_currency ? "Dual-currency · " : "") +
(d.generated_at_human || d.generated_at || "");
}
function setMem(id, human, n, bytes) {
var el = document.getElementById(id);
if (!el) return;
if (!human && human !== 0) {
el.textContent = "—";
el.removeAttribute("title");
return;
}
var h = String(human);
if (n != null && n !== "" && Number(n) > 0) h += " · " + n + "p";
el.textContent = h;
if (bytes != null && bytes !== "" && Number(bytes) > 0) {
el.setAttribute("title", Number(bytes).toLocaleString("en-US") + " B");
} else el.removeAttribute("title");
}
var p = d.performance || {};
set("st-config-ms", msLabel(p.config_ms, p.config_http));
set("st-terms-ms", msLabel(p.terms_ms, p.terms_http));
set("st-webui-ms", msLabel(p.webui_ms, p.webui_http));
set("st-load", p.loadavg || "—");
var mem = p.memory || {};
set("st-mem-ctr", mem.container_rss_human || "—");
set("st-mem-pg", mem.postgres_rss_human
? mem.postgres_rss_human + (mem.postgres_n ? " · " + mem.postgres_n + "p" : "")
: "—");
set("st-mem-taler", mem.taler_rss_human
? mem.taler_rss_human + (mem.taler_n ? " · " + mem.taler_n + "p" : "")
: "—");
set("st-mem-nginx", mem.nginx_rss_human || "—");
setMem("st-mem-ctr", mem.container_rss_label || mem.container_rss_human || "—", null, mem.container_rss_bytes);
setMem("st-mem-pg", mem.postgres_rss_human, mem.postgres_n, mem.postgres_rss_bytes);
setMem("st-mem-taler", mem.taler_rss_human, mem.taler_n, mem.taler_rss_bytes);
setMem("st-mem-nginx", mem.nginx_rss_human, mem.nginx_n, mem.nginx_rss_bytes);
var topBox = document.getElementById("st-mem-top");
if (topBox) {
var tops = mem.top || [];
@ -998,10 +1009,16 @@ sudo apt-get install -y taler-wallet-cli</pre>
'<th style="text-align:left;padding:0.25rem;border-bottom:1px solid var(--border)">Comm</th>' +
'<th style="text-align:left;padding:0.25rem;border-bottom:1px solid var(--border)">Command</th></tr></thead><tbody>';
tops.forEach(function (t) {
th += '<tr><td style="padding:0.25rem;color:#5eead4">' + (t.rss_human || "?") +
var cmd = t.cmd || "";
var cmdFull = t.cmd_full || cmd;
if (cmd.length > 64) cmd = cmd.slice(0, 61) + "…";
th += '<tr><td style="padding:0.25rem;color:#5eead4" title="' +
(t.rss_bytes != null ? Number(t.rss_bytes).toLocaleString("en-US") + " B" : "") + '">' +
(t.rss_human || "?") +
'</td><td style="padding:0.25rem">' + (t.comm || "") +
'</td><td style="padding:0.25rem;word-break:break-all;color:var(--muted)">' +
(t.cmd || "") + "</td></tr>";
'</td><td style="padding:0.25rem;word-break:break-all;color:var(--muted)" title="' +
String(cmdFull).replace(/"/g, "&quot;") + '">' +
cmd + "</td></tr>";
});
th += "</tbody></table>";
topBox.innerHTML = th;
@ -1009,7 +1026,8 @@ sudo apt-get install -y taler-wallet-cli</pre>
}
var pf = document.getElementById("st-perf-foot");
if (pf) {
pf.textContent = "Container RSS + process groups · " +
pf.textContent = "Container RSS + process groups" +
(mem.source ? " · " + mem.source : "") + " · " +
(d.generated_at_human || d.generated_at || "");
}
return fetch("/intro/stats-run.json", { cache: "no-store" })