fix(team): repair runtime snapshot caching and rss sampling regressions

Pre-existing regressions из 'cache transcript and telemetry scans':

- pidsMissingUsageStats теперь всегда фильтрует runtimeUsagePids по
  отсутствию stats, а не только когда runtimeProcessRowsForSnapshot
  null. Пустой/неполный results-row больше не блокирует pidusage
  fallback.
- Кэш runtimeProcessTableCache очищается при invalidateRuntime-
  SnapshotCaches и не репопулируется in-flight'ом, который начат до
  invalidate (через generation token + caller-aware cache context).
- runtimeProcessUsageStatsCacheByPid очищается в invalidate, а в
  shared OpenCode host refresh branch'е сбрасывается negative-cache
  для конкретного pid'а перед таргетным sample'ом.

Зеленит 25 теста в TeamProvisioningService > getTeamAgentRuntimeSnapshot,
включая 'uses batched pidusage rss values', 'captures CPU and memory
history', 'does not cache live runtime metadata when invalidated while
the probe is in flight', 'shows RSS for OpenCode secondary lane host
pids'.
This commit is contained in:
777genius 2026-05-29 00:51:03 +03:00
parent 9e3efa18ce
commit cb35992cff

View file

@ -3463,6 +3463,7 @@ export class TeamProvisioningService {
private runtimeProcessTableInFlight:
| Promise<RuntimeTelemetryProcessTableRow[] | null>
| undefined;
private runtimeProcessTableCacheGeneration = 0;
private readonly runtimeProcessUsageStatsCacheByPid = new Map<
number,
{
@ -3781,6 +3782,9 @@ export class TeamProvisioningService {
this.liveTeamAgentRuntimeMetadataCache.delete(teamName);
this.liveTeamAgentRuntimeMetadataInFlightByTeam.delete(teamName);
this.runtimeProcessRowsForUsageSnapshotByTeam.delete(teamName);
this.runtimeProcessUsageStatsCacheByPid.clear();
this.runtimeProcessTableCache = undefined;
this.runtimeProcessTableCacheGeneration += 1;
}
private normalizeMemberKeyForTaskActivity(memberName: string): string {
@ -14307,10 +14311,7 @@ export class TeamProvisioningService {
runtimeProcessRowsForSnapshot,
runtimeUsagePids
);
const pidsMissingUsageStats =
runtimeProcessRowsForSnapshot == null
? runtimeUsagePids.filter((pid) => !usageStatsByPid.has(pid))
: [];
const pidsMissingUsageStats = runtimeUsagePids.filter((pid) => !usageStatsByPid.has(pid));
if (pidsMissingUsageStats.length > 0) {
const sampledUsageStats = await this.readProcessUsageStatsByPid(pidsMissingUsageStats);
for (const [pid, stats] of sampledUsageStats) {
@ -14641,11 +14642,11 @@ export class TeamProvisioningService {
rssPid &&
!usageStatsByPid.has(rssPid) &&
isSharedOpenCodeHost &&
runtimeProcessRowsForSnapshot == null &&
typeof rssPid === 'number' &&
rssPid > 0
) {
try {
this.runtimeProcessUsageStatsCacheByPid.delete(rssPid);
const refreshedUsageStats = (await this.readProcessUsageStatsByPid([rssPid])).get(rssPid);
if (refreshedUsageStats) {
usageStatsByPid.set(rssPid, refreshedUsageStats);
@ -25795,7 +25796,8 @@ export class TeamProvisioningService {
}
const currentProcessRows = await this.readCurrentRuntimeProcessTableRows(
'process table runtime snapshot'
'process table runtime snapshot',
{ teamName, generationAtStart }
);
const processRows = currentProcessRows ?? [];
const processTableAvailable = currentProcessRows !== null;
@ -26610,7 +26612,8 @@ export class TeamProvisioningService {
}
private async readCurrentRuntimeProcessTableRows(
label: string
label: string,
cacheContext?: { teamName: string; generationAtStart: number }
): Promise<RuntimeTelemetryProcessTableRow[] | null> {
const cached = this.runtimeProcessTableCache;
if (cached && cached.expiresAtMs > Date.now()) {
@ -26621,6 +26624,7 @@ export class TeamProvisioningService {
return this.runtimeProcessTableInFlight;
}
const generationAtStart = this.runtimeProcessTableCacheGeneration;
const request = this.withRuntimeTelemetryTimeout(
listRuntimeProcessTableForCurrentPlatform(),
TeamProvisioningService.RUNTIME_PROCESS_TABLE_TIMEOUT_MS,
@ -26642,10 +26646,19 @@ export class TeamProvisioningService {
return null;
})
.then((rows) => {
this.runtimeProcessTableCache = {
expiresAtMs: Date.now() + TeamProvisioningService.RUNTIME_PROCESS_TABLE_CACHE_TTL_MS,
rows,
};
const callerGenerationStillValid =
cacheContext === undefined ||
this.getRuntimeSnapshotCacheGeneration(cacheContext.teamName) ===
cacheContext.generationAtStart;
if (
this.runtimeProcessTableCacheGeneration === generationAtStart &&
callerGenerationStillValid
) {
this.runtimeProcessTableCache = {
expiresAtMs: Date.now() + TeamProvisioningService.RUNTIME_PROCESS_TABLE_CACHE_TTL_MS,
rows,
};
}
return rows;
})
.finally(() => {