From c3bb8fc2657a446c8604b3f73b4ccd38c1ae4175 Mon Sep 17 00:00:00 2001 From: Vincent Koc Date: Fri, 31 Jul 2026 10:23:32 +0800 Subject: [PATCH] fix(perf): make Kova startup diagnostics actionable (#116319) * fix(perf): rank startup duration hotspots correctly * ci(perf): exercise prepared runtime startup cases --- .github/workflows/openclaw-performance.yml | 3 +++ scripts/openclaw-performance-source-summary.mjs | 10 +++++++++- .../openclaw-performance-source-summary.test.ts | 6 ++++++ 3 files changed, 18 insertions(+), 1 deletion(-) diff --git a/.github/workflows/openclaw-performance.yml b/.github/workflows/openclaw-performance.yml index b8b3ef73549e..6552ede028c2 100644 --- a/.github/workflows/openclaw-performance.yml +++ b/.github/workflows/openclaw-performance.yml @@ -715,6 +715,9 @@ jobs: --skip-qa \ --startup-case default \ --startup-case skipChannels \ + --startup-case preparedRuntimeCatalogStall \ + --startup-case preparedRuntimeScaleOne \ + --startup-case preparedRuntimeScaleMany \ --startup-case oneInternalHook \ --startup-case allInternalHooks \ --startup-case fiftyPlugins \ diff --git a/scripts/openclaw-performance-source-summary.mjs b/scripts/openclaw-performance-source-summary.mjs index 3e505ffad06d..3f8465d6c109 100644 --- a/scripts/openclaw-performance-source-summary.mjs +++ b/scripts/openclaw-performance-source-summary.mjs @@ -360,7 +360,7 @@ function buildTraceRows(startup) { const rows = []; for (const result of startup?.results ?? []) { const traceEntries = Object.entries(result.summary?.startupTrace ?? {}) - .filter(([, stats]) => typeof stats?.p50 === "number") + .filter(([name, stats]) => isStartupTraceDuration(name) && typeof stats?.p50 === "number") .toSorted((a, b) => (b[1].p50 ?? 0) - (a[1].p50 ?? 0)) .slice(0, 5); for (const [name, stats] of traceEntries) { @@ -370,6 +370,14 @@ function buildTraceRows(startup) { return rows; } +function isStartupTraceDuration(name) { + if (name.endsWith(".total") || name.startsWith("memory.")) { + return false; + } + const metricName = name.slice(name.lastIndexOf(".") + 1); + return !metricName.endsWith("Count") && !metricName.endsWith("Mb"); +} + function buildMockHelloRows(summaries) { return summaries.map(({ id, summary }) => { const status = diff --git a/test/scripts/openclaw-performance-source-summary.test.ts b/test/scripts/openclaw-performance-source-summary.test.ts index 1aeb2e43f3bf..667bc96608a2 100644 --- a/test/scripts/openclaw-performance-source-summary.test.ts +++ b/test/scripts/openclaw-performance-source-summary.test.ts @@ -46,6 +46,8 @@ function writeSourceFixture(sourceDir: string) { cpuCoreRatio: { p95: 0.25 }, startupTrace: { "memory.ready.heapUsedMb": { p50: 30, p95: 32 }, + "phase.load.total": { p50: 70, p95: 80 }, + "phase.load.itemCount": { p50: 40, p95: 50 }, "phase.load": { p50: 7, p95: 8 }, }, }, @@ -165,6 +167,10 @@ describe("buildMarkdown", () => { expect(buildMarkdown(sourceDir, null)).toContain("gateway health json"); expect(buildMarkdown(sourceDir, null)).toContain("## SQLite State Smoke"); expect(buildMarkdown(sourceDir, null)).toContain("4100"); + expect(buildMarkdown(sourceDir, null)).toContain("| default | phase.load | 7.0ms | 8.0ms |"); + expect(buildMarkdown(sourceDir, null)).not.toContain("phase.load.total"); + expect(buildMarkdown(sourceDir, null)).not.toContain("phase.load.itemCount"); + expect(buildMarkdown(sourceDir, null)).not.toContain("memory.ready.heapUsedMb"); }); it("rejects a missing source directory", () => {