perf: cache persisted bootstrap outcome lookups

This commit is contained in:
777genius 2026-05-29 14:19:04 +03:00
parent d0c6fdd28c
commit 9be096f864
2 changed files with 152 additions and 2 deletions

View file

@ -693,6 +693,11 @@ interface BootstrapTranscriptOutcomeCacheEntry {
outcome: BootstrapTranscriptOutcome | null;
}
interface BootstrapTranscriptOutcomeLookupCacheEntry {
expiresAtMs: number;
outcome: BootstrapTranscriptOutcome | null;
}
import type {
ActiveToolCall,
AgentActionMode,
@ -3295,9 +3300,10 @@ export class TeamProvisioningService {
private static readonly SAME_TEAM_RUN_START_SKEW_MS = 1_000;
private static readonly SAME_TEAM_PERSIST_RETRY_MS = 2_000;
private static readonly AGENT_RUNTIME_SNAPSHOT_CACHE_TTL_MS = 2_000;
private static readonly PERSISTED_AGENT_RUNTIME_SNAPSHOT_CACHE_TTL_MS = 5_000;
private static readonly PERSISTED_AGENT_RUNTIME_SNAPSHOT_CACHE_TTL_MS = 10_000;
private static readonly AGENT_RUNTIME_RESOURCE_HISTORY_LIMIT = 60;
private static readonly BOOTSTRAP_TRANSCRIPT_OUTCOME_CACHE_MAX_ENTRIES = 2_048;
private static readonly PERSISTED_BOOTSTRAP_TRANSCRIPT_OUTCOME_LOOKUP_CACHE_TTL_MS = 10_000;
private static readonly MAX_RUNTIME_TREE_PIDS_PER_ROOT = 64;
private static readonly MAX_RUNTIME_USAGE_PIDS_PER_SNAPSHOT = 512;
private static readonly RUNTIME_PROCESS_TABLE_TIMEOUT_MS = 1_500;
@ -3363,6 +3369,10 @@ export class TeamProvisioningService {
string,
BootstrapTranscriptOutcomeCacheEntry
>();
private readonly bootstrapTranscriptOutcomeLookupCache = new Map<
string,
BootstrapTranscriptOutcomeLookupCacheEntry
>();
private readonly teamOpLocks = new Map<string, Promise<void>>();
private readonly leadInboxRelayInFlight = new Map<string, Promise<number>>();
private readonly relayedLeadInboxMessageIds = new Map<string, Set<string>>();
@ -30101,6 +30111,19 @@ export class TeamProvisioningService {
memberName: string,
sinceMs: number | null
): Promise<BootstrapTranscriptOutcome | null> {
const lookupCacheKey = this.buildBootstrapTranscriptOutcomeLookupCacheKey(
teamName,
memberName,
sinceMs
);
const cachedLookup = this.getPersistedBootstrapTranscriptOutcomeLookupCacheEntry(
teamName,
lookupCacheKey
);
if (cachedLookup !== undefined) {
return this.cloneBootstrapTranscriptOutcome(cachedLookup);
}
let summaries: Awaited<ReturnType<TeamMemberLogsFinder['findMemberLogs']>>;
try {
summaries = await this.memberLogsFinder.findMemberLogs(teamName, memberName, sinceMs);
@ -30127,7 +30150,69 @@ export class TeamProvisioningService {
...(await this.readBootstrapTranscriptOutcomesInProjectRoot(teamName, memberName, sinceMs))
);
return this.selectLatestBootstrapTranscriptOutcome(outcomes);
const outcome = this.selectLatestBootstrapTranscriptOutcome(outcomes);
this.setPersistedBootstrapTranscriptOutcomeLookupCacheEntry(teamName, lookupCacheKey, outcome);
return outcome;
}
private cloneBootstrapTranscriptOutcome(
outcome: BootstrapTranscriptOutcome | null
): BootstrapTranscriptOutcome | null {
return outcome ? { ...outcome } : null;
}
private buildBootstrapTranscriptOutcomeLookupCacheKey(
teamName: string,
memberName: string,
sinceMs: number | null
): string {
return [teamName.trim().toLowerCase(), memberName.trim().toLowerCase(), sinceMs ?? ''].join(
'\0'
);
}
private getPersistedBootstrapTranscriptOutcomeLookupCacheEntry(
teamName: string,
cacheKey: string
): BootstrapTranscriptOutcome | null | undefined {
if (this.getTrackedRunId(teamName) || this.runtimeAdapterRunByTeam.has(teamName)) {
return undefined;
}
const cached = this.bootstrapTranscriptOutcomeLookupCache.get(cacheKey);
if (!cached) {
return undefined;
}
if (cached.expiresAtMs <= Date.now()) {
this.bootstrapTranscriptOutcomeLookupCache.delete(cacheKey);
return undefined;
}
return cached.outcome;
}
private setPersistedBootstrapTranscriptOutcomeLookupCacheEntry(
teamName: string,
cacheKey: string,
outcome: BootstrapTranscriptOutcome | null
): void {
if (this.getTrackedRunId(teamName) || this.runtimeAdapterRunByTeam.has(teamName)) {
return;
}
if (
!this.bootstrapTranscriptOutcomeLookupCache.has(cacheKey) &&
this.bootstrapTranscriptOutcomeLookupCache.size >=
TeamProvisioningService.BOOTSTRAP_TRANSCRIPT_OUTCOME_CACHE_MAX_ENTRIES
) {
const oldestKey = this.bootstrapTranscriptOutcomeLookupCache.keys().next().value;
if (oldestKey) {
this.bootstrapTranscriptOutcomeLookupCache.delete(oldestKey);
}
}
this.bootstrapTranscriptOutcomeLookupCache.set(cacheKey, {
expiresAtMs:
Date.now() +
TeamProvisioningService.PERSISTED_BOOTSTRAP_TRANSCRIPT_OUTCOME_LOOKUP_CACHE_TTL_MS,
outcome: this.cloneBootstrapTranscriptOutcome(outcome),
});
}
private async readRecentBootstrapTranscriptOutcome(

View file

@ -3405,6 +3405,11 @@ describe('TeamProvisioningService', () => {
vi.setSystemTime(new Date('2026-05-03T12:00:06.000Z'));
await (svc as any).getLiveTeamAgentRuntimeMetadata('runtime-team');
expect(listRuntimeProcessTableForCurrentPlatform).toHaveBeenCalledTimes(1);
vi.setSystemTime(new Date('2026-05-03T12:00:11.000Z'));
await (svc as any).getLiveTeamAgentRuntimeMetadata('runtime-team');
expect(listRuntimeProcessTableForCurrentPlatform).toHaveBeenCalledTimes(2);
});
@ -21134,6 +21139,66 @@ describe('TeamProvisioningService', () => {
});
});
it('caches persisted bootstrap transcript outcome lookup between close polling reads', async () => {
vi.useFakeTimers();
vi.setSystemTime(new Date('2026-05-03T12:00:00.000Z'));
const teamName = 'zz-unit-bootstrap-transcript-lookup-cache';
const memberName = 'tom';
const transcriptPath = path.join(tempProjectsBase, 'bootstrap-lookup-cache.jsonl');
const svc = new TeamProvisioningService();
const harness = svc as any;
const findMemberLogs = vi.fn(async () => [{ filePath: transcriptPath }]);
const readRecentBootstrapTranscriptOutcome = vi.fn(async () => ({
kind: 'success',
observedAt: '2026-05-24T09:25:42.904Z',
source: 'member_briefing',
}));
const readBootstrapTranscriptOutcomesInProjectRoot = vi.fn(async () => []);
harness.memberLogsFinder = { findMemberLogs };
harness.readRecentBootstrapTranscriptOutcome = readRecentBootstrapTranscriptOutcome;
harness.readBootstrapTranscriptOutcomesInProjectRoot =
readBootstrapTranscriptOutcomesInProjectRoot;
const firstOutcome = await harness.findBootstrapTranscriptOutcome(teamName, memberName, 123);
vi.setSystemTime(new Date('2026-05-03T12:00:06.000Z'));
const secondOutcome = await harness.findBootstrapTranscriptOutcome(teamName, memberName, 123);
expect(secondOutcome).toEqual(firstOutcome);
expect(findMemberLogs).toHaveBeenCalledTimes(1);
expect(readRecentBootstrapTranscriptOutcome).toHaveBeenCalledTimes(1);
expect(readBootstrapTranscriptOutcomesInProjectRoot).toHaveBeenCalledTimes(1);
vi.setSystemTime(new Date('2026-05-03T12:00:11.000Z'));
await harness.findBootstrapTranscriptOutcome(teamName, memberName, 123);
expect(findMemberLogs).toHaveBeenCalledTimes(2);
expect(readRecentBootstrapTranscriptOutcome).toHaveBeenCalledTimes(2);
});
it('does not use persisted bootstrap transcript outcome lookup cache for tracked runs', async () => {
const teamName = 'zz-unit-bootstrap-transcript-active-lookup-cache';
const memberName = 'tom';
const transcriptPath = path.join(tempProjectsBase, 'bootstrap-active-lookup-cache.jsonl');
const svc = new TeamProvisioningService();
const harness = svc as any;
const findMemberLogs = vi.fn(async () => [{ filePath: transcriptPath }]);
const readRecentBootstrapTranscriptOutcome = vi.fn(async () => ({
kind: 'success',
observedAt: '2026-05-24T09:25:42.904Z',
source: 'member_briefing',
}));
harness.memberLogsFinder = { findMemberLogs };
harness.readRecentBootstrapTranscriptOutcome = readRecentBootstrapTranscriptOutcome;
harness.readBootstrapTranscriptOutcomesInProjectRoot = vi.fn(async () => []);
harness.aliveRunByTeam.set(teamName, 'run-1');
await harness.findBootstrapTranscriptOutcome(teamName, memberName, 123);
await harness.findBootstrapTranscriptOutcome(teamName, memberName, 123);
expect(findMemberLogs).toHaveBeenCalledTimes(2);
expect(readRecentBootstrapTranscriptOutcome).toHaveBeenCalledTimes(2);
});
it('caches persisted member spawn statuses between close polling reads', async () => {
const teamName = 'zz-unit-persisted-status-cache';
const svc = new TeamProvisioningService();