perf: cache persisted spawn status reads

This commit is contained in:
777genius 2026-05-29 13:06:26 +03:00
parent fa242d9ff6
commit 35a9b05637
2 changed files with 52 additions and 2 deletions

View file

@ -3306,6 +3306,7 @@ export class TeamProvisioningService {
private static readonly RUNTIME_PIDUSAGE_SINGLE_TIMEOUT_MS = 750;
private static readonly RUNTIME_PIDUSAGE_FALLBACK_CONCURRENCY = 16;
private static readonly MEMBER_SPAWN_STATUS_SNAPSHOT_CACHE_TTL_MS = 500;
private static readonly PERSISTED_MEMBER_SPAWN_STATUS_SNAPSHOT_CACHE_TTL_MS = 2_000;
private static readonly LAUNCH_STATE_NOOP_REFRESH_MS = 15_000;
private static readonly RETAINED_PROVISIONING_PROGRESS_TTL_MS = 5 * 60_000;
private static readonly OPENCODE_RUNTIME_DELIVERY_ADVISORY_EVENT_TTL_MS = 24 * 60 * 60_000;
@ -3445,7 +3446,7 @@ export class TeamProvisioningService {
{
expiresAtMs: number;
generation: number;
runId: string;
runId: string | null;
snapshot: MemberSpawnStatusesSnapshot;
}
>();
@ -13821,6 +13822,17 @@ export class TeamProvisioningService {
source?: 'live' | 'persisted' | 'merged';
}> {
const readPersistedStatuses = async (resolvedRunId: string | null) => {
const generationAtStart = this.getMemberSpawnStatusesCacheGeneration(teamName);
const cached = this.memberSpawnStatusesSnapshotCache.get(teamName);
if (
cached &&
cached.expiresAtMs > Date.now() &&
cached.runId === resolvedRunId &&
cached.generation === generationAtStart
) {
return this.cloneMemberSpawnStatusesSnapshot(cached.snapshot);
}
const repairSnapshot = await this.readTaskActivityRepairLaunchSnapshot(teamName);
this.repairStaleTaskActivityIntervalsOnce(teamName, repairSnapshot);
const { snapshot, statuses } = await this.reconcilePersistedLaunchState(teamName);
@ -13842,7 +13854,7 @@ export class TeamProvisioningService {
const summary = expectedMembers
? summarizeMemberSpawnStatusRecord(expectedMembers, nextStatuses)
: undefined;
return {
const persistedSnapshot = {
statuses: nextStatuses,
runId: resolvedRunId,
teamLaunchState: summary
@ -13854,6 +13866,20 @@ export class TeamProvisioningService {
summary: summary ?? snapshot?.summary,
source: 'persisted' as const,
};
if (
this.getMemberSpawnStatusesCacheGeneration(teamName) === generationAtStart &&
this.getTrackedRunId(teamName) === resolvedRunId
) {
this.memberSpawnStatusesSnapshotCache.set(teamName, {
expiresAtMs:
Date.now() +
TeamProvisioningService.PERSISTED_MEMBER_SPAWN_STATUS_SNAPSHOT_CACHE_TTL_MS,
generation: generationAtStart,
runId: resolvedRunId,
snapshot: this.cloneMemberSpawnStatusesSnapshot(persistedSnapshot),
});
}
return persistedSnapshot;
};
const runId = this.getTrackedRunId(teamName);

View file

@ -620,6 +620,12 @@ type TeamProvisioningServicePrivateHarness = {
applyProcessBootstrapTransportOverlay: (
input: Record<string, unknown>
) => Record<string, unknown>;
reconcilePersistedLaunchState: (
teamName: string
) => Promise<{
snapshot: null;
statuses: Record<string, never>;
}>;
readProcessUsageStatsByPid: (
pids: readonly number[]
) => Promise<Map<number, { rssBytes?: number; cpuPercent?: number }>>;
@ -21083,6 +21089,24 @@ describe('TeamProvisioningService', () => {
});
});
it('caches persisted member spawn statuses between close polling reads', async () => {
const teamName = 'zz-unit-persisted-status-cache';
const svc = new TeamProvisioningService();
const harness = privateHarness(svc);
harness.reconcilePersistedLaunchState = vi.fn(async () => ({
snapshot: null,
statuses: {},
}));
harness.attachLiveRuntimeMetadataToStatuses = vi.fn(async (_teamName, statuses) => statuses);
const first = await svc.getMemberSpawnStatuses(teamName);
const second = await svc.getMemberSpawnStatuses(teamName);
expect(first).toEqual(second);
expect(harness.reconcilePersistedLaunchState).toHaveBeenCalledTimes(1);
expect(harness.attachLiveRuntimeMetadataToStatuses).toHaveBeenCalledTimes(1);
});
it('does not heal cleanup-finalized launch failures from stale bootstrap-state confirmation', async () => {
allowConsoleLogs();
const teamName = 'zz-unit-cleanup-finalized-stale-bootstrap-ignored';