Compare commits

..
Author SHA1 Message Date
ajnart a37e101bba fix(docker): normalize container CPU usage 2026-09-06 10:54:05 +02:00
6 changed files with 15 additions and 34 deletions

No files matched your search

+2 -3
View File
@@ -30,9 +30,8 @@ needed to restore encrypted integration credentials.
Select a Homarr backup ZIP. The browser previews entity counts, board names, and required database migrations before Select a Homarr backup ZIP. The browser previews entity counts, board names, and required database migrations before
the archive is uploaded. the archive is uploaded.
Restoring replaces the current SQLite database and cannot be undone. It also invalidates the current database session. Restoring replaces the current SQLite database and cannot be undone. After a successful restore, the Next.js process
After a successful management restore, Homarr waits for the restarted server and sends the administrator to sign in restarts and the page reloads when Homarr is ready.
again. The first-run onboarding restore keeps its existing board destination.
A backup can be restored on another Homarr instance. When its `SECRET_ENCRYPTION_KEY` differs, Homarr re-encrypts stored A backup can be restored on another Homarr instance. When its `SECRET_ENCRYPTION_KEY` differs, Homarr re-encrypts stored
integration secrets for the target instance. Version metadata is used to apply compatible migrations. integration secrets for the target instance. Version metadata is used to apply compatible migrations.
@@ -204,12 +204,8 @@ export const DatabaseRestoreFlow = ({ variant = "card", onRestoreComplete }: Dat
} }
onRestoreComplete?.(); onRestoreComplete?.();
let destination: string | null = null; const destination =
if (variant === "standalone" && homeBoardName) { variant === "standalone" && homeBoardName ? `/boards/${encodeURIComponent(homeBoardName)}` : null;
destination = `/boards/${encodeURIComponent(homeBoardName)}`;
} else if (variant === "card") {
destination = "/auth/login";
}
startReadinessCheck(restartAfterMs, destination); startReadinessCheck(restartAfterMs, destination);
}, [file, onRestoreComplete, startReadinessCheck, t, variant]); }, [file, onRestoreComplete, startReadinessCheck, t, variant]);
@@ -321,7 +317,6 @@ export const DatabaseRestoreFlow = ({ variant = "card", onRestoreComplete }: Dat
<RestoreProgressPanel <RestoreProgressPanel
active active
status={restoreStatus} status={restoreStatus}
reloginRequired={variant === "card"}
onRetry={() => startReadinessCheck(0)} onRetry={() => startReadinessCheck(0)}
onReload={() => window.location.reload()} onReload={() => window.location.reload()}
/> />
@@ -11,18 +11,11 @@ export type RestoreProgressStatus = "restoring" | "restarting" | "timedOut";
interface RestoreProgressPanelProps { interface RestoreProgressPanelProps {
active: boolean; active: boolean;
status: RestoreProgressStatus; status: RestoreProgressStatus;
reloginRequired?: boolean;
onRetry: () => void; onRetry: () => void;
onReload: () => void; onReload: () => void;
} }
export const RestoreProgressPanel = ({ export const RestoreProgressPanel = ({ active, status, onRetry, onReload }: RestoreProgressPanelProps) => {
active,
status,
reloginRequired = false,
onRetry,
onReload,
}: RestoreProgressPanelProps) => {
const t = useI18n("management.page.tool.backup.restore"); const t = useI18n("management.page.tool.backup.restore");
const tCommon = useI18n("common"); const tCommon = useI18n("common");
const reduceMotion = useReducedMotion(); const reduceMotion = useReducedMotion();
@@ -70,12 +63,6 @@ export const RestoreProgressPanel = ({
</div> </div>
</Group> </Group>
{reloginRequired && !isRestoring ? (
<Text size="sm" c="dimmed">
{t("progress.relogin")}
</Text>
) : null}
{isTimedOut ? ( {isTimedOut ? (
<Group> <Group>
<Button variant="light" onClick={onRetry} leftSection={<IconRefresh size={16} />}> <Button variant="light" onClick={onRetry} leftSection={<IconRefresh size={16} />}>
+5 -4
View File
@@ -481,13 +481,14 @@ export function calculateCpuUsage(stats: ContainerStats): number {
return 0; return 0;
} }
const numberOfCpus = stats.cpu_stats.online_cpus; const cpuDelta = stats.cpu_stats.cpu_usage.total_usage - (stats.precpu_stats?.cpu_usage?.total_usage ?? 0);
const usage = stats.cpu_stats.system_cpu_usage; const systemDelta = (stats.cpu_stats.system_cpu_usage ?? 0) - (stats.precpu_stats?.system_cpu_usage ?? 0);
if (!usage || usage === 0) {
if (systemDelta <= 0 || cpuDelta < 0) {
return 0; return 0;
} }
return (stats.cpu_stats.cpu_usage.total_usage / usage) * numberOfCpus * 100; return (cpuDelta / systemDelta) * 100;
} }
export function calculateMemoryUsage(stats: ContainerStats): number { export function calculateMemoryUsage(stats: ContainerStats): number {
@@ -72,16 +72,16 @@ describe("calculateCpuUsage", () => {
const stats = createStats({ const stats = createStats({
cpu_stats: { online_cpus: 4, cpu_usage: { total_usage: 2000 }, system_cpu_usage: 10000 }, cpu_stats: { online_cpus: 4, cpu_usage: { total_usage: 2000 }, system_cpu_usage: 10000 },
}); });
// (2000 / 10000) * 4 * 100 = 80 // (2000 / 10000) * 100 = 20
expect(calculateCpuUsage(stats)).toBe(80); expect(calculateCpuUsage(stats)).toBe(20);
}); });
test("should handle fractional CPU usage", () => { test("should handle fractional CPU usage", () => {
const stats = createStats({ const stats = createStats({
cpu_stats: { online_cpus: 2, cpu_usage: { total_usage: 500 }, system_cpu_usage: 100000 }, cpu_stats: { online_cpus: 2, cpu_usage: { total_usage: 500 }, system_cpu_usage: 100000 },
}); });
// (500 / 100000) * 2 * 100 = 1 // (500 / 100000) * 100 = 0.5
expect(calculateCpuUsage(stats)).toBe(1); expect(calculateCpuUsage(stats)).toBe(0.5);
}); });
}); });
+1 -2
View File
@@ -6456,8 +6456,7 @@
}, },
"progress": { "progress": {
"title": "Restoring database...", "title": "Restoring database...",
"restarting": "Restarting server", "restarting": "Restarting server"
"relogin": "Restoring the database invalidated your current session. Homarr will send you to sign in when the server is ready."
}, },
"timeout": { "timeout": {
"title": "Server did not come back online", "title": "Server did not come back online",