mirror of
https://github.com/stablyai/orca.git
synced 2026-09-30 16:02:56 +00:00
perf: bound notification cooldown tracking (#4194)
This commit is contained in:
@@ -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,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)
|
||||
|
||||
Reference in New Issue
Block a user