perf: bound notification cooldown tracking (#4194)

This commit is contained in:
Neil
2026-05-31 07:43:29 -07:00
committed by GitHub
parent ec715cc486
commit 702cef384c
2 changed files with 57 additions and 8 deletions
+32
View File
@@ -965,6 +965,38 @@ describe('registerNotificationHandlers', () => {
expect(notificationShowMock).toHaveBeenCalledTimes(2)
})
it('bounds notification cooldown keys during unique worktree bursts', () => {
notificationIsSupportedMock.mockReturnValue(false)
registerNotificationHandlers({
getSettings: () => ({
notifications: {
enabled: true,
agentTaskComplete: true,
terminalBell: true,
suppressWhenFocused: false
}
})
} as never)
const handler = getDispatchHandler()
for (let i = 0; i < 75; i++) {
expect(handler({}, { source: 'terminal-bell', worktreeId: `repo::wt-${i}` })).toEqual({
delivered: false,
reason: 'not-supported'
})
}
expect(handler({}, { source: 'terminal-bell', worktreeId: 'repo::wt-0' })).toEqual({
delivered: false,
reason: 'not-supported'
})
expect(handler({}, { source: 'terminal-bell', worktreeId: 'repo::wt-74' })).toEqual({
delivered: false,
reason: 'cooldown'
})
expect(notificationCtorMock).not.toHaveBeenCalled()
})
it('deduplicates agent-task-complete and terminal-bell for the same worktree', () => {
registerNotificationHandlers({
getSettings: () => ({
+25 -8
View File
@@ -25,6 +25,7 @@ import { buildNotificationOptions } from './notification-options'
import { parsePaneKey } from '../../shared/stable-pane-id'
const NOTIFICATION_COOLDOWN_MS = 5000
const MAX_RECENT_NOTIFICATION_KEYS = 50
const NOTIFICATION_DISPLAY_CONFIRMATION_TIMEOUT_MS = 2500
const NOTIFICATION_RELEASE_FALLBACK_MS = 5 * 60 * 1000
const MAX_NOTIFICATION_SOUND_BYTES = 10 * 1024 * 1024
@@ -175,6 +176,26 @@ function logNativeNotificationFailure(context: string, error?: string): void {
)
}
function pruneRecentNotifications(recentNotifications: Map<string, number>, now: number): void {
if (recentNotifications.size <= MAX_RECENT_NOTIFICATION_KEYS) {
return
}
for (const [key, ts] of recentNotifications) {
if (now - ts >= NOTIFICATION_COOLDOWN_MS) {
recentNotifications.delete(key)
}
}
while (recentNotifications.size > MAX_RECENT_NOTIFICATION_KEYS) {
const oldest = recentNotifications.keys().next()
if (oldest.done) {
break
}
recentNotifications.delete(oldest.value)
}
}
export function registerNotificationHandlers(store: Store, runtime?: OrcaRuntimeService): void {
const recentNotifications = new Map<string, number>()
@@ -245,16 +266,12 @@ export function registerNotificationHandlers(store: Store, runtime?: OrcaRuntime
if (now - lastSentAt < NOTIFICATION_COOLDOWN_MS) {
return { delivered: false, reason: 'cooldown' }
}
recentNotifications.delete(dedupeKey)
recentNotifications.set(dedupeKey, now)
// Evict stale entries so the map doesn't grow unbounded.
if (recentNotifications.size > 50) {
for (const [key, ts] of recentNotifications) {
if (now - ts >= NOTIFICATION_COOLDOWN_MS) {
recentNotifications.delete(key)
}
}
}
// Why: a storm across many worktrees should not make every
// notification dispatch scan an ever-growing cooldown table.
pruneRecentNotifications(recentNotifications, now)
}
const notificationOptions = buildNotificationOptions(args)