refactor(team): extract spawn status warning throttle
This commit is contained in:
parent
38b0a87d5d
commit
34902059b2
3 changed files with 116 additions and 8 deletions
|
|
@ -58,6 +58,12 @@ import {
|
|||
isMemberSpawnStatusesIpcBackoffActive,
|
||||
recordMemberSpawnStatusesIpcRetryBackoff,
|
||||
} from '../team/teamMemberSpawnStatusBackoff';
|
||||
import {
|
||||
clearAllMemberSpawnUiEqualLastWarns,
|
||||
clearMemberSpawnUiEqualLastWarn,
|
||||
hasMemberSpawnUiEqualLastWarn,
|
||||
shouldLogMemberSpawnUiEqualSuppressed,
|
||||
} from '../team/teamMemberSpawnUiEqualWarningThrottle';
|
||||
import {
|
||||
areInboxMessageArraysEquivalent,
|
||||
clearTeamMessageSelectorCaches,
|
||||
|
|
@ -184,7 +190,6 @@ const teamRefreshBurstDiagnostics = new Map<
|
|||
string,
|
||||
{ windowStartedAt: number; count: number; lastWarnAt: number }
|
||||
>();
|
||||
const memberSpawnUiEqualLastWarnAtByTeam = new Map<string, number>();
|
||||
interface RefreshTeamDataOptions {
|
||||
withDedup?: boolean;
|
||||
}
|
||||
|
|
@ -249,7 +254,7 @@ export function __resetTeamSliceModuleStateForTests(): void {
|
|||
clearAllTeamLocalStateEpochs();
|
||||
clearAllMemberSpawnStatusesIpcBackoffs();
|
||||
teamRefreshBurstDiagnostics.clear();
|
||||
memberSpawnUiEqualLastWarnAtByTeam.clear();
|
||||
clearAllMemberSpawnUiEqualLastWarns();
|
||||
resolvedMembersSelectorCache.clear();
|
||||
resolvedMemberSelectorCache.clear();
|
||||
clearTeamMessageSelectorCaches();
|
||||
|
|
@ -282,7 +287,7 @@ function clearTeamScopedTransientState(teamName: string): void {
|
|||
clearLastResolvedTeamDataRefreshAt(teamName);
|
||||
clearMemberSpawnStatusesIpcBackoff(teamName);
|
||||
teamRefreshBurstDiagnostics.delete(teamName);
|
||||
memberSpawnUiEqualLastWarnAtByTeam.delete(teamName);
|
||||
clearMemberSpawnUiEqualLastWarn(teamName);
|
||||
clearTeamScopedSelectorCaches(teamName);
|
||||
}
|
||||
|
||||
|
|
@ -668,7 +673,7 @@ export function __getTeamScopedTransientStateForTests(teamName: string): {
|
|||
hasCurrentLocalStateEpoch: hasTeamLocalStateEpoch(teamName),
|
||||
hasMemberSpawnStatusesIpcBackoff: hasMemberSpawnStatusesIpcBackoff(teamName),
|
||||
hasTeamRefreshBurstDiagnostics: teamRefreshBurstDiagnostics.has(teamName),
|
||||
hasMemberSpawnUiEqualLastWarn: memberSpawnUiEqualLastWarnAtByTeam.has(teamName),
|
||||
hasMemberSpawnUiEqualLastWarn: hasMemberSpawnUiEqualLastWarn(teamName),
|
||||
};
|
||||
}
|
||||
|
||||
|
|
@ -957,12 +962,14 @@ function maybeLogMemberSpawnUiEqualSuppressed(
|
|||
teamName: string,
|
||||
runId: string | null | undefined
|
||||
): void {
|
||||
const now = Date.now();
|
||||
const lastWarnAt = memberSpawnUiEqualLastWarnAtByTeam.get(teamName) ?? 0;
|
||||
if (now - lastWarnAt < MEMBER_SPAWN_UI_EQUAL_WARN_THROTTLE_MS) {
|
||||
if (
|
||||
!shouldLogMemberSpawnUiEqualSuppressed(
|
||||
teamName,
|
||||
MEMBER_SPAWN_UI_EQUAL_WARN_THROTTLE_MS
|
||||
)
|
||||
) {
|
||||
return;
|
||||
}
|
||||
memberSpawnUiEqualLastWarnAtByTeam.set(teamName, now);
|
||||
logger.debug(
|
||||
`[perf] member-spawn snapshot suppressed team=${teamName} runId=${runId ?? 'none'} reason=member-spawn-ui-equal`
|
||||
);
|
||||
|
|
|
|||
|
|
@ -0,0 +1,30 @@
|
|||
const memberSpawnUiEqualLastWarnAtByTeam = new Map<string, number>();
|
||||
|
||||
export function getMemberSpawnUiEqualLastWarnAt(teamName: string): number | undefined {
|
||||
return memberSpawnUiEqualLastWarnAtByTeam.get(teamName);
|
||||
}
|
||||
|
||||
export function hasMemberSpawnUiEqualLastWarn(teamName: string): boolean {
|
||||
return memberSpawnUiEqualLastWarnAtByTeam.has(teamName);
|
||||
}
|
||||
|
||||
export function shouldLogMemberSpawnUiEqualSuppressed(
|
||||
teamName: string,
|
||||
throttleMs: number,
|
||||
now = Date.now()
|
||||
): boolean {
|
||||
const lastWarnAt = memberSpawnUiEqualLastWarnAtByTeam.get(teamName) ?? 0;
|
||||
if (now - lastWarnAt < throttleMs) {
|
||||
return false;
|
||||
}
|
||||
memberSpawnUiEqualLastWarnAtByTeam.set(teamName, now);
|
||||
return true;
|
||||
}
|
||||
|
||||
export function clearMemberSpawnUiEqualLastWarn(teamName: string): void {
|
||||
memberSpawnUiEqualLastWarnAtByTeam.delete(teamName);
|
||||
}
|
||||
|
||||
export function clearAllMemberSpawnUiEqualLastWarns(): void {
|
||||
memberSpawnUiEqualLastWarnAtByTeam.clear();
|
||||
}
|
||||
|
|
@ -0,0 +1,71 @@
|
|||
import { afterEach, describe, expect, it, vi } from 'vitest';
|
||||
|
||||
import {
|
||||
clearAllMemberSpawnUiEqualLastWarns,
|
||||
clearMemberSpawnUiEqualLastWarn,
|
||||
getMemberSpawnUiEqualLastWarnAt,
|
||||
hasMemberSpawnUiEqualLastWarn,
|
||||
shouldLogMemberSpawnUiEqualSuppressed,
|
||||
} from '../../../src/renderer/store/team/teamMemberSpawnUiEqualWarningThrottle';
|
||||
|
||||
afterEach(() => {
|
||||
vi.useRealTimers();
|
||||
clearAllMemberSpawnUiEqualLastWarns();
|
||||
});
|
||||
|
||||
describe('teamMemberSpawnUiEqualWarningThrottle', () => {
|
||||
it('preserves the existing zero fallback boundary for unknown teams', () => {
|
||||
expect(shouldLogMemberSpawnUiEqualSuppressed('my-team', 2_000, 1_999)).toBe(false);
|
||||
expect(hasMemberSpawnUiEqualLastWarn('my-team')).toBe(false);
|
||||
|
||||
expect(shouldLogMemberSpawnUiEqualSuppressed('my-team', 2_000, 2_000)).toBe(true);
|
||||
expect(getMemberSpawnUiEqualLastWarnAt('my-team')).toBe(2_000);
|
||||
});
|
||||
|
||||
it('throttles repeated warnings until the boundary is reached', () => {
|
||||
expect(shouldLogMemberSpawnUiEqualSuppressed('my-team', 2_000, 10_000)).toBe(true);
|
||||
expect(shouldLogMemberSpawnUiEqualSuppressed('my-team', 2_000, 11_999)).toBe(false);
|
||||
expect(getMemberSpawnUiEqualLastWarnAt('my-team')).toBe(10_000);
|
||||
|
||||
expect(shouldLogMemberSpawnUiEqualSuppressed('my-team', 2_000, 12_000)).toBe(true);
|
||||
expect(getMemberSpawnUiEqualLastWarnAt('my-team')).toBe(12_000);
|
||||
});
|
||||
|
||||
it('tracks teams independently', () => {
|
||||
expect(shouldLogMemberSpawnUiEqualSuppressed('my-team', 2_000, 10_000)).toBe(true);
|
||||
expect(shouldLogMemberSpawnUiEqualSuppressed('other-team', 2_000, 10_500)).toBe(true);
|
||||
|
||||
expect(getMemberSpawnUiEqualLastWarnAt('my-team')).toBe(10_000);
|
||||
expect(getMemberSpawnUiEqualLastWarnAt('other-team')).toBe(10_500);
|
||||
});
|
||||
|
||||
it('uses Date.now by default for production callers', () => {
|
||||
vi.setSystemTime(new Date('2026-05-22T07:30:00.000Z'));
|
||||
|
||||
expect(shouldLogMemberSpawnUiEqualSuppressed('my-team', 2_000)).toBe(true);
|
||||
|
||||
expect(getMemberSpawnUiEqualLastWarnAt('my-team')).toBe(
|
||||
new Date('2026-05-22T07:30:00.000Z').getTime()
|
||||
);
|
||||
});
|
||||
|
||||
it('clears one team without touching other teams', () => {
|
||||
shouldLogMemberSpawnUiEqualSuppressed('my-team', 2_000, 10_000);
|
||||
shouldLogMemberSpawnUiEqualSuppressed('other-team', 2_000, 10_500);
|
||||
|
||||
clearMemberSpawnUiEqualLastWarn('my-team');
|
||||
|
||||
expect(hasMemberSpawnUiEqualLastWarn('my-team')).toBe(false);
|
||||
expect(getMemberSpawnUiEqualLastWarnAt('other-team')).toBe(10_500);
|
||||
});
|
||||
|
||||
it('clears all tracked warnings', () => {
|
||||
shouldLogMemberSpawnUiEqualSuppressed('my-team', 2_000, 10_000);
|
||||
shouldLogMemberSpawnUiEqualSuppressed('other-team', 2_000, 10_500);
|
||||
|
||||
clearAllMemberSpawnUiEqualLastWarns();
|
||||
|
||||
expect(hasMemberSpawnUiEqualLastWarn('my-team')).toBe(false);
|
||||
expect(hasMemberSpawnUiEqualLastWarn('other-team')).toBe(false);
|
||||
});
|
||||
});
|
||||
Loading…
Reference in a new issue