perf(main): reuse runtime process rows across refreshes
This commit is contained in:
parent
4457c6f61b
commit
cb60fca258
2 changed files with 60 additions and 6 deletions
|
|
@ -26006,15 +26006,13 @@ export class TeamProvisioningService {
|
||||||
): { rows: RuntimeTelemetryProcessTableRow[] | null } | null {
|
): { rows: RuntimeTelemetryProcessTableRow[] | null } | null {
|
||||||
const cached = this.runtimeProcessRowsForUsageSnapshotByTeam.get(teamName);
|
const cached = this.runtimeProcessRowsForUsageSnapshotByTeam.get(teamName);
|
||||||
const nowMs = Date.now();
|
const nowMs = Date.now();
|
||||||
if (
|
if (!cached || cached.expiresAtMs <= nowMs || cached.runId !== runId) {
|
||||||
!cached ||
|
|
||||||
cached.expiresAtMs <= nowMs ||
|
|
||||||
cached.runId !== runId ||
|
|
||||||
cached.generation !== this.getRuntimeSnapshotCacheGeneration(teamName)
|
|
||||||
) {
|
|
||||||
return null;
|
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 =
|
const sampledAtMs =
|
||||||
typeof cached.sampledAtMs === 'number' && Number.isFinite(cached.sampledAtMs)
|
typeof cached.sampledAtMs === 'number' && Number.isFinite(cached.sampledAtMs)
|
||||||
? cached.sampledAtMs
|
? cached.sampledAtMs
|
||||||
|
|
|
||||||
|
|
@ -144,6 +144,7 @@ import {
|
||||||
listTmuxPanePidsForCurrentPlatform,
|
listTmuxPanePidsForCurrentPlatform,
|
||||||
listTmuxPaneRuntimeInfoForCurrentPlatform,
|
listTmuxPaneRuntimeInfoForCurrentPlatform,
|
||||||
sendKeysToTmuxPaneForCurrentPlatform,
|
sendKeysToTmuxPaneForCurrentPlatform,
|
||||||
|
type RuntimeProcessTableRow,
|
||||||
} from '@features/tmux-installer/main';
|
} from '@features/tmux-installer/main';
|
||||||
import { agentTeamsMcpHttpServer } from '@main/services/team/AgentTeamsMcpHttpServer';
|
import { agentTeamsMcpHttpServer } from '@main/services/team/AgentTeamsMcpHttpServer';
|
||||||
import {
|
import {
|
||||||
|
|
@ -204,6 +205,10 @@ const EXPECTED_RUNTIME_PIDUSAGE_OPTIONS =
|
||||||
process.platform === 'win32' ? { maxage: 10_000 } : { maxage: 0 };
|
process.platform === 'win32' ? { maxage: 10_000 } : { maxage: 0 };
|
||||||
const ORIGINAL_RUNTIME_PIDUSAGE_ENABLED = process.env.CLAUDE_TEAM_RUNTIME_PIDUSAGE_ENABLED;
|
const ORIGINAL_RUNTIME_PIDUSAGE_ENABLED = process.env.CLAUDE_TEAM_RUNTIME_PIDUSAGE_ENABLED;
|
||||||
|
|
||||||
|
type RuntimeTelemetryProcessTableRow = RuntimeProcessTableRow & {
|
||||||
|
runtimeTelemetrySource?: 'native' | 'wsl' | 'windows-host';
|
||||||
|
};
|
||||||
|
|
||||||
function restoreRuntimePidusageTelemetryEnv() {
|
function restoreRuntimePidusageTelemetryEnv() {
|
||||||
if (ORIGINAL_RUNTIME_PIDUSAGE_ENABLED === undefined) {
|
if (ORIGINAL_RUNTIME_PIDUSAGE_ENABLED === undefined) {
|
||||||
delete process.env.CLAUDE_TEAM_RUNTIME_PIDUSAGE_ENABLED;
|
delete process.env.CLAUDE_TEAM_RUNTIME_PIDUSAGE_ENABLED;
|
||||||
|
|
@ -658,6 +663,22 @@ type TeamProvisioningServicePrivateHarness = {
|
||||||
pids: readonly number[]
|
pids: readonly number[]
|
||||||
) => Promise<Map<number, { rssBytes?: number; cpuPercent?: number }>>;
|
) => Promise<Map<number, { rssBytes?: number; cpuPercent?: number }>>;
|
||||||
readRuntimeProcessRowsForUsageSnapshot: (teamName: string) => Promise<unknown[] | null>;
|
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;
|
invalidateRuntimeSnapshotCaches: (teamName: string) => void;
|
||||||
aliveRunByTeam: Map<string, string>;
|
aliveRunByTeam: Map<string, string>;
|
||||||
readRecentBootstrapTranscriptOutcome: (
|
readRecentBootstrapTranscriptOutcome: (
|
||||||
|
|
@ -3769,6 +3790,41 @@ describe('TeamProvisioningService', () => {
|
||||||
vi.useRealTimers();
|
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 () => {
|
it('skips pidusage by default when process table metrics are missing', async () => {
|
||||||
await withRuntimePidusageTelemetryEnv(undefined, async () => {
|
await withRuntimePidusageTelemetryEnv(undefined, async () => {
|
||||||
const svc = new TeamProvisioningService();
|
const svc = new TeamProvisioningService();
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue