From cb60fca25862063fc89ea7c72cfbe7486e5dd3e8 Mon Sep 17 00:00:00 2001 From: 777genius Date: Sun, 31 May 2026 10:02:43 +0300 Subject: [PATCH] perf(main): reuse runtime process rows across refreshes --- .../services/team/TeamProvisioningService.ts | 10 ++-- .../team/TeamProvisioningService.test.ts | 56 +++++++++++++++++++ 2 files changed, 60 insertions(+), 6 deletions(-) diff --git a/src/main/services/team/TeamProvisioningService.ts b/src/main/services/team/TeamProvisioningService.ts index d8e3d5b9..b0e4dd34 100644 --- a/src/main/services/team/TeamProvisioningService.ts +++ b/src/main/services/team/TeamProvisioningService.ts @@ -26006,15 +26006,13 @@ export class TeamProvisioningService { ): { rows: RuntimeTelemetryProcessTableRow[] | null } | null { const cached = this.runtimeProcessRowsForUsageSnapshotByTeam.get(teamName); const nowMs = Date.now(); - if ( - !cached || - cached.expiresAtMs <= nowMs || - cached.runId !== runId || - cached.generation !== this.getRuntimeSnapshotCacheGeneration(teamName) - ) { + if (!cached || cached.expiresAtMs <= nowMs || cached.runId !== runId) { return null; } + // Process table rows are sampled global OS state. Do not tie reuse to + // runtime snapshot generation: launch progress invalidates runtime metadata + // frequently, and the age gate below keeps liveness freshness bounded. const sampledAtMs = typeof cached.sampledAtMs === 'number' && Number.isFinite(cached.sampledAtMs) ? cached.sampledAtMs diff --git a/test/main/services/team/TeamProvisioningService.test.ts b/test/main/services/team/TeamProvisioningService.test.ts index f7b3981f..1ee1fbf6 100644 --- a/test/main/services/team/TeamProvisioningService.test.ts +++ b/test/main/services/team/TeamProvisioningService.test.ts @@ -144,6 +144,7 @@ import { listTmuxPanePidsForCurrentPlatform, listTmuxPaneRuntimeInfoForCurrentPlatform, sendKeysToTmuxPaneForCurrentPlatform, + type RuntimeProcessTableRow, } from '@features/tmux-installer/main'; import { agentTeamsMcpHttpServer } from '@main/services/team/AgentTeamsMcpHttpServer'; import { @@ -204,6 +205,10 @@ const EXPECTED_RUNTIME_PIDUSAGE_OPTIONS = process.platform === 'win32' ? { maxage: 10_000 } : { maxage: 0 }; const ORIGINAL_RUNTIME_PIDUSAGE_ENABLED = process.env.CLAUDE_TEAM_RUNTIME_PIDUSAGE_ENABLED; +type RuntimeTelemetryProcessTableRow = RuntimeProcessTableRow & { + runtimeTelemetrySource?: 'native' | 'wsl' | 'windows-host'; +}; + function restoreRuntimePidusageTelemetryEnv() { if (ORIGINAL_RUNTIME_PIDUSAGE_ENABLED === undefined) { delete process.env.CLAUDE_TEAM_RUNTIME_PIDUSAGE_ENABLED; @@ -658,6 +663,22 @@ type TeamProvisioningServicePrivateHarness = { pids: readonly number[] ) => Promise>; readRuntimeProcessRowsForUsageSnapshot: (teamName: string) => Promise; + readCachedRuntimeProcessRowsForLiveRuntimeMetadata: ( + teamName: string, + runId: string | null + ) => { rows: RuntimeTelemetryProcessTableRow[] | null } | null; + runtimeProcessRowsForUsageSnapshotByTeam: Map< + string, + { + expiresAtMs: number; + generation: number; + runId: string | null; + sampledAtMs: number; + rows: RuntimeTelemetryProcessTableRow[] | null; + includesWindowsHostRows: boolean; + } + >; + getRuntimeSnapshotCacheGeneration: (teamName: string) => number; invalidateRuntimeSnapshotCaches: (teamName: string) => void; aliveRunByTeam: Map; readRecentBootstrapTranscriptOutcome: ( @@ -3769,6 +3790,41 @@ describe('TeamProvisioningService', () => { vi.useRealTimers(); }); + it('keeps fresh live runtime process rows across snapshot invalidations', () => { + vi.useFakeTimers(); + vi.setSystemTime(new Date('2026-05-03T12:00:00.000Z')); + const svc = new TeamProvisioningService(); + const harness = privateHarness(svc); + const rows: RuntimeTelemetryProcessTableRow[] = [ + { + pid: 111, + ppid: 1, + command: '/usr/bin/node lead.js', + cpuPercent: 3.5, + rssBytes: 123_000_000, + runtimeTelemetrySource: 'native', + }, + ]; + harness.runtimeProcessRowsForUsageSnapshotByTeam.set('runtime-team', { + expiresAtMs: Date.now() + 60_000, + generation: harness.getRuntimeSnapshotCacheGeneration('runtime-team'), + runId: 'run-1', + sampledAtMs: Date.now(), + rows, + includesWindowsHostRows: false, + }); + + harness.invalidateRuntimeSnapshotCaches('runtime-team'); + vi.setSystemTime(new Date('2026-05-03T12:00:04.000Z')); + + const cached = harness.readCachedRuntimeProcessRowsForLiveRuntimeMetadata( + 'runtime-team', + 'run-1' + ); + expect(cached).toEqual({ rows }); + vi.useRealTimers(); + }); + it('skips pidusage by default when process table metrics are missing', async () => { await withRuntimePidusageTelemetryEnv(undefined, async () => { const svc = new TeamProvisioningService();