fix: base Stack health uptime on container start, not creation (#1341)

The dashboard Stack health UP column counted from each container's
Created timestamp, which never moves on stop/start or restart, so a
restarted container kept reporting its original age. Resolve uptime from
State.StartedAt (via a briefly cached inspect with bounded concurrency,
falling back to Created when inspect is unavailable) so it reflects the
real time since last start.

The current CPU and MEM columns separately summed the latest sample per
container with no recency filter, letting a recently stopped container's
final reading linger in the totals. Drop samples that trail the freshest
sample by more than the stale window so stopped containers leave the sum.
This commit is contained in:
Anso
2026-06-09 20:16:04 -04:00
committed by GitHub
parent 3dc8199907
commit e7895c889d
5 changed files with 330 additions and 32 deletions
@@ -3,6 +3,7 @@ import { Button } from '@/components/ui/button';
import { Sparkline } from '@/components/ui/sparkline';
import { ChevronLeft, ChevronRight, Layers } from 'lucide-react';
import type { StackStatusEntry, MetricPoint, StackCpuSeries } from './types';
import { aggregateCurrentUsage } from './aggregateCurrentUsage';
interface StackHealthTableProps {
stackStatuses: Record<string, StackStatusEntry>;
@@ -80,28 +81,7 @@ export function StackHealthTable({
return () => clearInterval(id);
}, []);
const stackAggregates = useMemo(() => {
const latestPerContainer: Record<string, Record<string, MetricPoint>> = {};
for (const m of metrics) {
if (!m.stack_name) continue;
if (!latestPerContainer[m.stack_name]) latestPerContainer[m.stack_name] = {};
const existing = latestPerContainer[m.stack_name][m.container_id];
if (!existing || m.timestamp > existing.timestamp) {
latestPerContainer[m.stack_name][m.container_id] = m;
}
}
const result: Record<string, { mem: number; cpu: number }> = {};
for (const [stack, containers] of Object.entries(latestPerContainer)) {
let mem = 0;
let cpu = 0;
for (const m of Object.values(containers)) {
mem += m.memory_mb;
cpu += m.cpu_percent;
}
result[stack] = { mem, cpu };
}
return result;
}, [metrics]);
const stackAggregates = useMemo(() => aggregateCurrentUsage(metrics), [metrics]);
const rows = useMemo(() => {
const list = Object.entries(stackStatuses).map(([file, entry]) => {