refactor(team): extract refresh burst diagnostics

This commit is contained in:
777genius 2026-05-22 10:27:22 +03:00
parent 34902059b2
commit 9bd8585617
3 changed files with 141 additions and 27 deletions

View file

@ -80,6 +80,12 @@ import {
clearPendingReplyRefreshWaits,
setPendingReplyRefreshEnabled,
} from '../team/teamPendingReplyWaits';
import {
clearAllTeamRefreshBurstDiagnostics,
clearTeamRefreshBurstDiagnostics,
hasTeamRefreshBurstDiagnostics,
noteTeamRefreshBurst,
} from '../team/teamRefreshBurstDiagnostics';
import { noteTeamRefreshFanout } from '../teamRefreshFanoutDiagnostics';
import { getWorktreeNavigationState } from '../utils/stateResetHelpers';
@ -186,10 +192,6 @@ const pendingFreshTeamMemberActivityMetaRefreshes = new Set<string>();
const pendingTeamPendingReplyRefreshTimers = new Map<string, ReturnType<typeof setTimeout>>();
let inFlightGlobalTasksRefresh: Promise<void> | null = null;
let pendingFreshGlobalTasksRefresh = false;
const teamRefreshBurstDiagnostics = new Map<
string,
{ windowStartedAt: number; count: number; lastWarnAt: number }
>();
interface RefreshTeamDataOptions {
withDedup?: boolean;
}
@ -253,7 +255,7 @@ export function __resetTeamSliceModuleStateForTests(): void {
clearAllLastResolvedTeamDataRefreshes();
clearAllTeamLocalStateEpochs();
clearAllMemberSpawnStatusesIpcBackoffs();
teamRefreshBurstDiagnostics.clear();
clearAllTeamRefreshBurstDiagnostics();
clearAllMemberSpawnUiEqualLastWarns();
resolvedMembersSelectorCache.clear();
resolvedMemberSelectorCache.clear();
@ -286,7 +288,7 @@ function clearTeamScopedTransientState(teamName: string): void {
pendingFreshTeamMemberActivityMetaRefreshes.delete(teamName);
clearLastResolvedTeamDataRefreshAt(teamName);
clearMemberSpawnStatusesIpcBackoff(teamName);
teamRefreshBurstDiagnostics.delete(teamName);
clearTeamRefreshBurstDiagnostics(teamName);
clearMemberSpawnUiEqualLastWarn(teamName);
clearTeamScopedSelectorCaches(teamName);
}
@ -672,7 +674,7 @@ export function __getTeamScopedTransientStateForTests(teamName: string): {
hasLastResolvedTeamDataRefresh: hasLastResolvedTeamDataRefreshAt(teamName),
hasCurrentLocalStateEpoch: hasTeamLocalStateEpoch(teamName),
hasMemberSpawnStatusesIpcBackoff: hasMemberSpawnStatusesIpcBackoff(teamName),
hasTeamRefreshBurstDiagnostics: teamRefreshBurstDiagnostics.has(teamName),
hasTeamRefreshBurstDiagnostics: hasTeamRefreshBurstDiagnostics(teamName),
hasMemberSpawnUiEqualLastWarn: hasMemberSpawnUiEqualLastWarn(teamName),
};
}
@ -838,25 +840,6 @@ function fetchTeamDataFresh(
);
}
function noteTeamRefreshBurst(teamName: string): number {
const now = Date.now();
const diagnostic = teamRefreshBurstDiagnostics.get(teamName) ?? {
windowStartedAt: now,
count: 0,
lastWarnAt: 0,
};
if (now - diagnostic.windowStartedAt > TEAM_REFRESH_BURST_WINDOW_MS) {
diagnostic.windowStartedAt = now;
diagnostic.count = 0;
}
diagnostic.count += 1;
teamRefreshBurstDiagnostics.set(teamName, diagnostic);
return diagnostic.count;
}
function areLaunchSummaryCountsEqual(
left: PersistedTeamLaunchSummary | undefined,
right: PersistedTeamLaunchSummary | undefined
@ -4203,7 +4186,7 @@ export const createTeamSlice: StateCreator<AppState, [], [], TeamSlice> = (set,
const refreshToken = beginInFlightTeamDataRefresh(teamName);
// Silent refresh — update data without showing loading skeleton.
// Only selectTeam() sets loading: true (for initial load).
noteTeamRefreshBurst(teamName);
noteTeamRefreshBurst(teamName, TEAM_REFRESH_BURST_WINDOW_MS);
if (reusedInFlightRequest) {
pendingFreshTeamDataRefreshes.add(teamName);
}

View file

@ -0,0 +1,48 @@
interface TeamRefreshBurstDiagnostic {
windowStartedAt: number;
count: number;
lastWarnAt: number;
}
const teamRefreshBurstDiagnostics = new Map<string, TeamRefreshBurstDiagnostic>();
export function hasTeamRefreshBurstDiagnostics(teamName: string): boolean {
return teamRefreshBurstDiagnostics.has(teamName);
}
export function getTeamRefreshBurstDiagnosticForTests(
teamName: string
): TeamRefreshBurstDiagnostic | undefined {
const diagnostic = teamRefreshBurstDiagnostics.get(teamName);
return diagnostic ? { ...diagnostic } : undefined;
}
export function noteTeamRefreshBurst(
teamName: string,
windowMs: number,
now = Date.now()
): number {
const diagnostic = teamRefreshBurstDiagnostics.get(teamName) ?? {
windowStartedAt: now,
count: 0,
lastWarnAt: 0,
};
if (now - diagnostic.windowStartedAt > windowMs) {
diagnostic.windowStartedAt = now;
diagnostic.count = 0;
}
diagnostic.count += 1;
teamRefreshBurstDiagnostics.set(teamName, diagnostic);
return diagnostic.count;
}
export function clearTeamRefreshBurstDiagnostics(teamName: string): void {
teamRefreshBurstDiagnostics.delete(teamName);
}
export function clearAllTeamRefreshBurstDiagnostics(): void {
teamRefreshBurstDiagnostics.clear();
}

View file

@ -0,0 +1,83 @@
import { afterEach, describe, expect, it } from 'vitest';
import {
clearAllTeamRefreshBurstDiagnostics,
clearTeamRefreshBurstDiagnostics,
getTeamRefreshBurstDiagnosticForTests,
hasTeamRefreshBurstDiagnostics,
noteTeamRefreshBurst,
} from '../../../src/renderer/store/team/teamRefreshBurstDiagnostics';
afterEach(() => {
clearAllTeamRefreshBurstDiagnostics();
});
describe('teamRefreshBurstDiagnostics store', () => {
it('creates a window on the first refresh note', () => {
expect(noteTeamRefreshBurst('my-team', 4_000, 10_000)).toBe(1);
expect(getTeamRefreshBurstDiagnosticForTests('my-team')).toEqual({
windowStartedAt: 10_000,
count: 1,
lastWarnAt: 0,
});
expect(hasTeamRefreshBurstDiagnostics('my-team')).toBe(true);
});
it('increments inside the active burst window', () => {
expect(noteTeamRefreshBurst('my-team', 4_000, 10_000)).toBe(1);
expect(noteTeamRefreshBurst('my-team', 4_000, 13_999)).toBe(2);
expect(noteTeamRefreshBurst('my-team', 4_000, 14_000)).toBe(3);
expect(getTeamRefreshBurstDiagnosticForTests('my-team')).toEqual({
windowStartedAt: 10_000,
count: 3,
lastWarnAt: 0,
});
});
it('resets only after now is strictly beyond the burst window', () => {
expect(noteTeamRefreshBurst('my-team', 4_000, 10_000)).toBe(1);
expect(noteTeamRefreshBurst('my-team', 4_000, 14_001)).toBe(1);
expect(getTeamRefreshBurstDiagnosticForTests('my-team')).toEqual({
windowStartedAt: 14_001,
count: 1,
lastWarnAt: 0,
});
});
it('tracks each team independently', () => {
noteTeamRefreshBurst('my-team', 4_000, 10_000);
noteTeamRefreshBurst('my-team', 4_000, 10_500);
noteTeamRefreshBurst('other-team', 4_000, 11_000);
expect(getTeamRefreshBurstDiagnosticForTests('my-team')?.count).toBe(2);
expect(getTeamRefreshBurstDiagnosticForTests('other-team')?.count).toBe(1);
});
it('clears one team or all diagnostics', () => {
noteTeamRefreshBurst('my-team', 4_000, 10_000);
noteTeamRefreshBurst('other-team', 4_000, 11_000);
clearTeamRefreshBurstDiagnostics('my-team');
expect(hasTeamRefreshBurstDiagnostics('my-team')).toBe(false);
expect(hasTeamRefreshBurstDiagnostics('other-team')).toBe(true);
clearAllTeamRefreshBurstDiagnostics();
expect(hasTeamRefreshBurstDiagnostics('other-team')).toBe(false);
});
it('returns defensive diagnostic snapshots for tests', () => {
noteTeamRefreshBurst('my-team', 4_000, 10_000);
const snapshot = getTeamRefreshBurstDiagnosticForTests('my-team');
if (snapshot) {
snapshot.count = 99;
}
expect(getTeamRefreshBurstDiagnosticForTests('my-team')?.count).toBe(1);
});
});