perf: cache persisted spawn status reads
This commit is contained in:
parent
fa242d9ff6
commit
35a9b05637
2 changed files with 52 additions and 2 deletions
|
|
@ -3306,6 +3306,7 @@ export class TeamProvisioningService {
|
||||||
private static readonly RUNTIME_PIDUSAGE_SINGLE_TIMEOUT_MS = 750;
|
private static readonly RUNTIME_PIDUSAGE_SINGLE_TIMEOUT_MS = 750;
|
||||||
private static readonly RUNTIME_PIDUSAGE_FALLBACK_CONCURRENCY = 16;
|
private static readonly RUNTIME_PIDUSAGE_FALLBACK_CONCURRENCY = 16;
|
||||||
private static readonly MEMBER_SPAWN_STATUS_SNAPSHOT_CACHE_TTL_MS = 500;
|
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 LAUNCH_STATE_NOOP_REFRESH_MS = 15_000;
|
||||||
private static readonly RETAINED_PROVISIONING_PROGRESS_TTL_MS = 5 * 60_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;
|
private static readonly OPENCODE_RUNTIME_DELIVERY_ADVISORY_EVENT_TTL_MS = 24 * 60 * 60_000;
|
||||||
|
|
@ -3445,7 +3446,7 @@ export class TeamProvisioningService {
|
||||||
{
|
{
|
||||||
expiresAtMs: number;
|
expiresAtMs: number;
|
||||||
generation: number;
|
generation: number;
|
||||||
runId: string;
|
runId: string | null;
|
||||||
snapshot: MemberSpawnStatusesSnapshot;
|
snapshot: MemberSpawnStatusesSnapshot;
|
||||||
}
|
}
|
||||||
>();
|
>();
|
||||||
|
|
@ -13821,6 +13822,17 @@ export class TeamProvisioningService {
|
||||||
source?: 'live' | 'persisted' | 'merged';
|
source?: 'live' | 'persisted' | 'merged';
|
||||||
}> {
|
}> {
|
||||||
const readPersistedStatuses = async (resolvedRunId: string | null) => {
|
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);
|
const repairSnapshot = await this.readTaskActivityRepairLaunchSnapshot(teamName);
|
||||||
this.repairStaleTaskActivityIntervalsOnce(teamName, repairSnapshot);
|
this.repairStaleTaskActivityIntervalsOnce(teamName, repairSnapshot);
|
||||||
const { snapshot, statuses } = await this.reconcilePersistedLaunchState(teamName);
|
const { snapshot, statuses } = await this.reconcilePersistedLaunchState(teamName);
|
||||||
|
|
@ -13842,7 +13854,7 @@ export class TeamProvisioningService {
|
||||||
const summary = expectedMembers
|
const summary = expectedMembers
|
||||||
? summarizeMemberSpawnStatusRecord(expectedMembers, nextStatuses)
|
? summarizeMemberSpawnStatusRecord(expectedMembers, nextStatuses)
|
||||||
: undefined;
|
: undefined;
|
||||||
return {
|
const persistedSnapshot = {
|
||||||
statuses: nextStatuses,
|
statuses: nextStatuses,
|
||||||
runId: resolvedRunId,
|
runId: resolvedRunId,
|
||||||
teamLaunchState: summary
|
teamLaunchState: summary
|
||||||
|
|
@ -13854,6 +13866,20 @@ export class TeamProvisioningService {
|
||||||
summary: summary ?? snapshot?.summary,
|
summary: summary ?? snapshot?.summary,
|
||||||
source: 'persisted' as const,
|
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);
|
const runId = this.getTrackedRunId(teamName);
|
||||||
|
|
|
||||||
|
|
@ -620,6 +620,12 @@ type TeamProvisioningServicePrivateHarness = {
|
||||||
applyProcessBootstrapTransportOverlay: (
|
applyProcessBootstrapTransportOverlay: (
|
||||||
input: Record<string, unknown>
|
input: Record<string, unknown>
|
||||||
) => Record<string, unknown>;
|
) => Record<string, unknown>;
|
||||||
|
reconcilePersistedLaunchState: (
|
||||||
|
teamName: string
|
||||||
|
) => Promise<{
|
||||||
|
snapshot: null;
|
||||||
|
statuses: Record<string, never>;
|
||||||
|
}>;
|
||||||
readProcessUsageStatsByPid: (
|
readProcessUsageStatsByPid: (
|
||||||
pids: readonly number[]
|
pids: readonly number[]
|
||||||
) => Promise<Map<number, { rssBytes?: number; cpuPercent?: 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 () => {
|
it('does not heal cleanup-finalized launch failures from stale bootstrap-state confirmation', async () => {
|
||||||
allowConsoleLogs();
|
allowConsoleLogs();
|
||||||
const teamName = 'zz-unit-cleanup-finalized-stale-bootstrap-ignored';
|
const teamName = 'zz-unit-cleanup-finalized-stale-bootstrap-ignored';
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue