Keep service metrics stable across metadata refreshes [skip version]

This commit is contained in:
Juan Denis committed 2026-09-07 23:34:57 -04:00
1 parent 116f817197
commit 1ace946ab9
4 files changed
+21 -7

No files matched your search

+2
View File
@@ -40,6 +40,8 @@ Changes after 1.9.28 on the development branch.
lines as readable structured entries (PR #136).
- Keep remote application log requests on their assigned server instead of
looking up the recorded container on the panel host.
- Avoid restarting service metrics requests when unrelated application metadata
refreshes.
- Close the container inspector when opening Logs or Exec so it can no longer
cover those surfaces (PR #137).
+7 -1
View File
@@ -155,6 +155,12 @@ try {
window.serviceFixture.setApp({ id: 1, name: 'alpha', app_type: 'docker' });
});
await page.waitForFunction(() => window.serviceFixture.state.pending.length === 1);
await action(() => window.serviceFixture.setApp({
id: 1, name: 'alpha', app_type: 'docker', status: 'running',
}));
await page.waitForTimeout(50);
assert.equal(await action(() => window.serviceFixture.state.pending.length), 1,
'recreated app metadata does not restart an in-flight metrics request');
await action(() => window.serviceFixture.state.pending.shift().resolve({ cpu_percent: 42 }));
await page.locator('#service .metrics-tab').waitFor();
assert.match(await page.locator('#service').innerText(), /42\.0%/);
@@ -197,7 +203,7 @@ try {
await action(() => window.serviceFixture.state.python.shift()({ active: false }));
await page.locator('#service .metrics-tab').waitFor();
assert.deepEqual(errors, []);
console.log('Service metrics regression passed: missing container, failed poll, app switch, late response and Python state reset.');
console.log('Service metrics regression passed: stable metadata refresh, missing container, failed poll, app switch, late response and Python state reset.');
console.log('Metrics browser regression passed: slow response, manual sharing, socket loss/reconnect, hidden tab, remote selection, refresh off; widget sharing, refresh retention, workspace isolation.');
} finally {
await browser?.close();
@@ -19,6 +19,7 @@ const MetricsTabContent = ({ app }) => {
const isDocker = app.app_type === 'docker';
const isPython = ['flask', 'django'].includes(app.app_type);
const { id: appId, name: appName, container_id: containerId } = app;
// Load on mount and whenever the app changes; poll on top of that.
const loadMetrics = useCallback(async () => {
@@ -26,7 +27,9 @@ const MetricsTabContent = ({ app }) => {
try {
if (isDocker) {
const data = await api.getContainers(true);
const runtimeId = resolveAppContainerId(app, data.containers || []);
const runtimeId = resolveAppContainerId(
{ id: appId, name: appName, container_id: containerId }, data.containers || []
);
if (runtimeId) {
const containerStats = normalizeContainerStats(
await api.getContainerStats(runtimeId)
@@ -36,7 +39,7 @@ const MetricsTabContent = ({ app }) => {
setStats(null);
}
} else if (isPython) {
const data = await api.getPythonAppStatus(app.id);
const data = await api.getPythonAppStatus(appId);
if (currentRequest === requestId.current) setProcessInfo(data);
}
} catch (err) {
@@ -47,7 +50,7 @@ const MetricsTabContent = ({ app }) => {
} finally {
if (currentRequest === requestId.current) setLoading(false);
}
}, [app, isDocker, isPython]);
}, [appId, appName, containerId, isDocker, isPython]);
useEffect(() => {
loadMetrics();
@@ -35,13 +35,16 @@ const OverviewTab = ({ app, deployConfig }) => {
const isDocker = app.app_type === 'docker';
const isPython = ['flask', 'django'].includes(app.app_type);
const { id: appId, name: appName, container_id: containerId } = app;
// Load on mount and whenever the app changes; poll on top of that.
const loadMetrics = useCallback(async () => {
try {
if (isDocker) {
const data = await api.getContainers(true);
const runtimeId = resolveAppContainerId(app, data.containers || []);
const runtimeId = resolveAppContainerId(
{ id: appId, name: appName, container_id: containerId }, data.containers || []
);
if (runtimeId) {
const containerStats = normalizeContainerStats(
await api.getContainerStats(runtimeId)
@@ -55,7 +58,7 @@ const OverviewTab = ({ app, deployConfig }) => {
});
}
} else if (isPython) {
const data = await api.getPythonAppStatus(app.id);
const data = await api.getPythonAppStatus(appId);
setMetrics({
active: data.active,
pid: data.pid,
@@ -69,7 +72,7 @@ const OverviewTab = ({ app, deployConfig }) => {
} finally {
setMetricsLoading(false);
}
}, [app, isDocker, isPython]);
}, [appId, appName, containerId, isDocker, isPython]);
useEffect(() => { loadMetrics(); }, [loadMetrics]);
usePolling(loadMetrics, METRICS_REFRESH_MS, { immediate: false });