perf: avoid stale runtime pid sampling

This commit is contained in:
777genius 2026-05-29 11:10:23 +03:00
parent 906942cb7a
commit 7d21f9bd76
2 changed files with 152 additions and 3 deletions

View file

@ -25620,9 +25620,18 @@ export class TeamProvisioningService {
if (!stat || typeof stat !== 'object') {
return undefined;
}
const candidate = stat as { memory?: unknown; cpu?: unknown };
const rssBytes = normalizeRuntimeTelemetryNumber(candidate.memory);
const cpuPercent = normalizeRuntimeTelemetryNumber(candidate.cpu);
const candidate = stat as {
memory?: unknown;
cpu?: unknown;
rssBytes?: unknown;
cpuPercent?: unknown;
};
const rssBytes =
normalizeRuntimeTelemetryNumber(candidate.memory) ??
normalizeRuntimeTelemetryNumber(candidate.rssBytes);
const cpuPercent =
normalizeRuntimeTelemetryNumber(candidate.cpu) ??
normalizeRuntimeTelemetryNumber(candidate.cpuPercent);
const normalized: RuntimeProcessUsageStats = {
...(rssBytes != null && rssBytes >= 0 ? { rssBytes } : {}),
...(cpuPercent != null && cpuPercent >= 0 ? { cpuPercent } : {}),
@ -25840,6 +25849,10 @@ export class TeamProvisioningService {
const pids: number[] = [];
let truncated = false;
const rootProcessRow = rowByPid.get(rootPid);
if (!rootProcessRow) {
usageTreesByRootPid.set(rootPid, { pids: [], truncated: false });
continue;
}
if (process.platform === 'win32' && rootProcessRow?.runtimeTelemetrySource === 'wsl') {
usageTreesByRootPid.set(rootPid, { pids: [], truncated: false });
continue;

View file

@ -3494,6 +3494,142 @@ describe('TeamProvisioningService', () => {
});
});
it('uses process table CPU and RSS values before falling back to pidusage', async () => {
const svc = new TeamProvisioningService();
(svc as any).configReader = {
getConfig: vi.fn(async () => ({
members: [
{ name: 'team-lead', agentType: 'team-lead' },
{ name: 'alice', model: 'gpt-5.4-mini' },
],
})),
};
(svc as any).readPersistedRuntimeMembers = vi.fn(() => [
{
name: 'alice',
agentId: 'alice@runtime-team',
tmuxPaneId: '%1',
backendType: 'tmux',
},
]);
(svc as any).aliveRunByTeam.set('runtime-team', 'run-1');
(svc as any).runs.set('run-1', {
runId: 'run-1',
child: { pid: 111 },
request: { model: 'gpt-5.4' },
processKilled: false,
cancelRequested: false,
spawnContext: null,
});
vi.mocked(listTmuxPaneRuntimeInfoForCurrentPlatform).mockResolvedValueOnce(
new Map([
[
'%1',
{
paneId: '%1',
panePid: 222,
currentCommand: 'codex',
},
],
])
);
vi.mocked(listRuntimeProcessTableForCurrentPlatform).mockResolvedValue([
{
pid: 111,
ppid: 1,
command: '/usr/bin/node lead.js',
cpuPercent: 3.5,
rssBytes: 123_000_000,
},
{
pid: 222,
ppid: 1,
command:
'/Users/belief/.bun/bin/bun cli.js --agent-id alice@runtime-team --agent-name alice --team-name runtime-team --model gpt-5.4-mini',
cpuPercent: 7,
rssBytes: 456_000_000,
},
]);
const snapshot = await svc.getTeamAgentRuntimeSnapshot('runtime-team');
expect(pidusage).not.toHaveBeenCalled();
expect(snapshot.members['team-lead']).toMatchObject({
pid: 111,
cpuPercent: 3.5,
rssBytes: 123_000_000,
});
expect(snapshot.members.alice).toMatchObject({
pid: 222,
cpuPercent: 7,
rssBytes: 456_000_000,
});
});
it('does not fall back to pidusage for root pids missing from an available process table', async () => {
const svc = new TeamProvisioningService();
(svc as any).configReader = {
getConfig: vi.fn(async () => ({
members: [
{ name: 'team-lead', agentType: 'team-lead' },
{ name: 'alice', model: 'gpt-5.4-mini' },
],
})),
};
(svc as any).readPersistedRuntimeMembers = vi.fn(() => [
{
name: 'alice',
agentId: 'alice@runtime-team',
tmuxPaneId: '%1',
backendType: 'tmux',
},
]);
(svc as any).aliveRunByTeam.set('runtime-team', 'run-1');
(svc as any).runs.set('run-1', {
runId: 'run-1',
child: { pid: 111 },
request: { model: 'gpt-5.4' },
processKilled: false,
cancelRequested: false,
spawnContext: null,
});
vi.mocked(listTmuxPaneRuntimeInfoForCurrentPlatform).mockResolvedValueOnce(
new Map([
[
'%1',
{
paneId: '%1',
panePid: 222,
currentCommand: 'codex',
},
],
])
);
vi.mocked(listRuntimeProcessTableForCurrentPlatform).mockResolvedValue([
{
pid: 999,
ppid: 1,
command: '/usr/bin/node unrelated.js',
cpuPercent: 1.5,
rssBytes: 12_000_000,
},
]);
const snapshot = await svc.getTeamAgentRuntimeSnapshot('runtime-team');
expect(pidusage).not.toHaveBeenCalled();
expect(snapshot.members['team-lead']).toMatchObject({
pid: 111,
});
expect(snapshot.members['team-lead'].cpuPercent).toBeUndefined();
expect(snapshot.members['team-lead'].rssBytes).toBeUndefined();
expect(snapshot.members.alice).toMatchObject({
pid: 222,
});
expect(snapshot.members.alice.cpuPercent).toBeUndefined();
expect(snapshot.members.alice.rssBytes).toBeUndefined();
});
it('captures CPU and memory history on runtime snapshots', async () => {
const svc = new TeamProvisioningService();
(svc as any).configReader = {