perf(main): reuse cached runtime process table
This commit is contained in:
parent
53a4c0e9e6
commit
6bb8d87bc7
5 changed files with 58 additions and 10 deletions
|
|
@ -1,5 +1,6 @@
|
||||||
import { TmuxStatusSourceAdapter } from '../adapters/output/sources/TmuxStatusSourceAdapter';
|
import { TmuxStatusSourceAdapter } from '../adapters/output/sources/TmuxStatusSourceAdapter';
|
||||||
import {
|
import {
|
||||||
|
type ListRuntimeProcessesOptions,
|
||||||
type RuntimeProcessTableRow,
|
type RuntimeProcessTableRow,
|
||||||
type TmuxPaneRuntimeInfo,
|
type TmuxPaneRuntimeInfo,
|
||||||
TmuxPlatformCommandExecutor,
|
TmuxPlatformCommandExecutor,
|
||||||
|
|
@ -34,10 +35,10 @@ export async function listTmuxPaneRuntimeInfoForCurrentPlatform(
|
||||||
return runtimeCommandExecutor.listPaneRuntimeInfo(paneIds);
|
return runtimeCommandExecutor.listPaneRuntimeInfo(paneIds);
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function listRuntimeProcessTableForCurrentPlatform(): Promise<
|
export async function listRuntimeProcessTableForCurrentPlatform(
|
||||||
RuntimeProcessTableRow[]
|
options: ListRuntimeProcessesOptions = {}
|
||||||
> {
|
): Promise<RuntimeProcessTableRow[]> {
|
||||||
return runtimeCommandExecutor.listRuntimeProcesses();
|
return runtimeCommandExecutor.listRuntimeProcesses(options);
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function sendKeysToTmuxPaneForCurrentPlatform(
|
export async function sendKeysToTmuxPaneForCurrentPlatform(
|
||||||
|
|
|
||||||
|
|
@ -32,6 +32,10 @@ export interface RuntimeProcessTableRow {
|
||||||
rssBytes?: number;
|
rssBytes?: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface ListRuntimeProcessesOptions {
|
||||||
|
bypassCache?: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Short-lived cache window for the global process table.
|
* Short-lived cache window for the global process table.
|
||||||
*
|
*
|
||||||
|
|
@ -218,9 +222,11 @@ export class TmuxPlatformCommandExecutor {
|
||||||
return new Map([...info.entries()].map(([paneId, pane]) => [paneId, pane.panePid]));
|
return new Map([...info.entries()].map(([paneId, pane]) => [paneId, pane.panePid]));
|
||||||
}
|
}
|
||||||
|
|
||||||
async listRuntimeProcesses(): Promise<RuntimeProcessTableRow[]> {
|
async listRuntimeProcesses(
|
||||||
|
options: ListRuntimeProcessesOptions = {}
|
||||||
|
): Promise<RuntimeProcessTableRow[]> {
|
||||||
const cached = this.#runtimeProcessTableCache;
|
const cached = this.#runtimeProcessTableCache;
|
||||||
if (cached && cached.expiresAtMs > Date.now()) {
|
if (options.bypassCache !== true && cached && cached.expiresAtMs > Date.now()) {
|
||||||
return cached.rows;
|
return cached.rows;
|
||||||
}
|
}
|
||||||
if (this.#runtimeProcessTableInFlight) {
|
if (this.#runtimeProcessTableInFlight) {
|
||||||
|
|
|
||||||
|
|
@ -152,4 +152,39 @@ describe('TmuxPlatformCommandExecutor', () => {
|
||||||
]);
|
]);
|
||||||
expect(childProcess.execFile).not.toHaveBeenCalled();
|
expect(childProcess.execFile).not.toHaveBeenCalled();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('can bypass the runtime process table cache for fresh process reads', async () => {
|
||||||
|
setPlatform('win32');
|
||||||
|
const execInPreferredDistro = vi
|
||||||
|
.fn()
|
||||||
|
.mockResolvedValueOnce({
|
||||||
|
exitCode: 0,
|
||||||
|
stdout: ' 42 1 1.0 128 opencode runtime --team-name demo --agent-id alice@demo\n',
|
||||||
|
stderr: '',
|
||||||
|
})
|
||||||
|
.mockResolvedValueOnce({
|
||||||
|
exitCode: 0,
|
||||||
|
stdout: ' 43 1 1.0 128 opencode runtime --team-name demo --agent-id alice@demo\n',
|
||||||
|
stderr: '',
|
||||||
|
});
|
||||||
|
const executor = new TmuxPlatformCommandExecutor(
|
||||||
|
{
|
||||||
|
execInPreferredDistro,
|
||||||
|
getPersistedPreferredDistroSync: () => 'Ubuntu',
|
||||||
|
} as never,
|
||||||
|
{} as never
|
||||||
|
);
|
||||||
|
|
||||||
|
await expect(executor.listRuntimeProcesses()).resolves.toEqual([
|
||||||
|
expect.objectContaining({ pid: 42 }),
|
||||||
|
]);
|
||||||
|
await expect(executor.listRuntimeProcesses()).resolves.toEqual([
|
||||||
|
expect.objectContaining({ pid: 42 }),
|
||||||
|
]);
|
||||||
|
await expect(executor.listRuntimeProcesses({ bypassCache: true })).resolves.toEqual([
|
||||||
|
expect.objectContaining({ pid: 43 }),
|
||||||
|
]);
|
||||||
|
|
||||||
|
expect(execInPreferredDistro).toHaveBeenCalledTimes(2);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -10522,6 +10522,7 @@ export class TeamProvisioningService {
|
||||||
peekAutoResumeService()?.cancelPendingAutoResume(teamName);
|
peekAutoResumeService()?.cancelPendingAutoResume(teamName);
|
||||||
this.clearOpenCodeRuntimeToolApprovals(teamName, { emitDismiss: true });
|
this.clearOpenCodeRuntimeToolApprovals(teamName, { emitDismiss: true });
|
||||||
this.invalidateRuntimeSnapshotCaches(teamName);
|
this.invalidateRuntimeSnapshotCaches(teamName);
|
||||||
|
this.runtimeProcessRowsForUsageSnapshotByTeam.delete(teamName);
|
||||||
this.retainedClaudeLogsByTeam.delete(teamName);
|
this.retainedClaudeLogsByTeam.delete(teamName);
|
||||||
this.persistedTranscriptClaudeLogsCache.delete(teamName);
|
this.persistedTranscriptClaudeLogsCache.delete(teamName);
|
||||||
this.leadInboxRelayInFlight.delete(teamName);
|
this.leadInboxRelayInFlight.delete(teamName);
|
||||||
|
|
@ -25554,7 +25555,10 @@ export class TeamProvisioningService {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (processRowsReadForMetadata) {
|
if (
|
||||||
|
processRowsReadForMetadata &&
|
||||||
|
this.getRuntimeSnapshotCacheGeneration(teamName) === generationAtStart
|
||||||
|
) {
|
||||||
const sampledAtMs = Date.now();
|
const sampledAtMs = Date.now();
|
||||||
this.runtimeProcessRowsForUsageSnapshotByTeam.set(teamName, {
|
this.runtimeProcessRowsForUsageSnapshotByTeam.set(teamName, {
|
||||||
expiresAtMs:
|
expiresAtMs:
|
||||||
|
|
|
||||||
|
|
@ -60,10 +60,12 @@ export async function cleanupManagedOpenCodeServeProcesses(
|
||||||
diagnostics: [],
|
diagnostics: [],
|
||||||
};
|
};
|
||||||
|
|
||||||
const rows = await (
|
const listProcessRows =
|
||||||
options.listProcessRows ??
|
options.listProcessRows ??
|
||||||
(platform === 'win32' ? listWindowsProcessTable : listRuntimeProcessTableForCurrentPlatform)
|
(platform === 'win32'
|
||||||
)();
|
? listWindowsProcessTable
|
||||||
|
: () => listRuntimeProcessTableForCurrentPlatform({ bypassCache: true }));
|
||||||
|
const rows = await listProcessRows();
|
||||||
const excludePids = options.excludePids ?? new Set<number>();
|
const excludePids = options.excludePids ?? new Set<number>();
|
||||||
const requiredDetailsMarkers = options.requiredDetailsMarkers ?? [];
|
const requiredDetailsMarkers = options.requiredDetailsMarkers ?? [];
|
||||||
const readDetails =
|
const readDetails =
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue