perf(main): reuse runtime process rows across refreshes

This commit is contained in:
777genius 2026-05-31 10:02:43 +03:00
parent 4457c6f61b
commit cb60fca258
2 changed files with 60 additions and 6 deletions

View file

@ -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

View file

@ -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<Map<number, { rssBytes?: number; cpuPercent?: number }>>;
readRuntimeProcessRowsForUsageSnapshot: (teamName: string) => Promise<unknown[] | null>;
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<string, string>;
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();