diff --git a/changelog.d/fixes/10346-empty-pool-warn-once.md b/changelog.d/fixes/10346-empty-pool-warn-once.md new file mode 100644 index 0000000000..e4b50ef3ff --- /dev/null +++ b/changelog.d/fixes/10346-empty-pool-warn-once.md @@ -0,0 +1 @@ +- **fix(backend):** log `auto/ matched no connected models` once per process per label instead of every minute ([#10346](https://github.com/diegosouzapw/OmniRoute/issues/10346)) diff --git a/open-sse/services/autoCombo/virtualFactory.ts b/open-sse/services/autoCombo/virtualFactory.ts index a55a70686b..f468f23005 100644 --- a/open-sse/services/autoCombo/virtualFactory.ts +++ b/open-sse/services/autoCombo/virtualFactory.ts @@ -44,21 +44,19 @@ export interface AutoComboSpec { family?: ModelFamily; } -/** Rate-limit empty-pool AUTO warns (same label can be resolved many times/min). */ -const emptyPoolWarnAt = new Map(); -export const EMPTY_POOL_WARN_INTERVAL_MS = 60_000; +/** Once-per-process empty-pool AUTO warns (steady empty is not a metronome). */ +const emptyPoolWarned = new Set(); -export function warnEmptyAutoPoolOnce(label: string, message: string, now = Date.now()): boolean { - const last = emptyPoolWarnAt.get(label) ?? 0; - if (now - last < EMPTY_POOL_WARN_INTERVAL_MS) return false; - emptyPoolWarnAt.set(label, now); +export function warnEmptyAutoPoolOnce(label: string, message: string, _now = Date.now()): boolean { + if (emptyPoolWarned.has(label)) return false; + emptyPoolWarned.add(label); log.warn("AUTO", message); return true; } -/** Test-only: reset the debounce map. */ +/** Test-only: reset the once-per-label set (also models emptiness reappearing). */ export function resetEmptyAutoPoolWarnStateForTests(): void { - emptyPoolWarnAt.clear(); + emptyPoolWarned.clear(); } /** Minimal connection shape needed for virtual auto-combo factory */ diff --git a/tests/unit/auto-empty-pool-warn-once.test.ts b/tests/unit/auto-empty-pool-warn-once.test.ts index 016b2e0997..9d2436aad1 100644 --- a/tests/unit/auto-empty-pool-warn-once.test.ts +++ b/tests/unit/auto-empty-pool-warn-once.test.ts @@ -1,18 +1,24 @@ +import test from "node:test"; import assert from "node:assert/strict"; -import { test } from "node:test"; import { - EMPTY_POOL_WARN_INTERVAL_MS, resetEmptyAutoPoolWarnStateForTests, warnEmptyAutoPoolOnce, } from "../../open-sse/services/autoCombo/virtualFactory.ts"; -test("warnEmptyAutoPoolOnce emits at most once per label per interval", () => { +test("warnEmptyAutoPoolOnce emits at most once per label per process", () => { resetEmptyAutoPoolWarnStateForTests(); - const t0 = 1_000_000; + const t0 = 1_700_000_000_000; assert.equal(warnEmptyAutoPoolOnce("auto/zai", "empty", t0), true); assert.equal(warnEmptyAutoPoolOnce("auto/zai", "empty", t0 + 1), false); - assert.equal(warnEmptyAutoPoolOnce("auto/zai", "empty", t0 + EMPTY_POOL_WARN_INTERVAL_MS - 1), false); + assert.equal(warnEmptyAutoPoolOnce("auto/zai", "empty", t0 + 60_000), false); + assert.equal(warnEmptyAutoPoolOnce("auto/zai", "empty", t0 + 3_600_000), false); assert.equal(warnEmptyAutoPoolOnce("auto/other", "empty", t0 + 1), true); - assert.equal(warnEmptyAutoPoolOnce("auto/zai", "empty", t0 + EMPTY_POOL_WARN_INTERVAL_MS), true); +}); + +test("resetEmptyAutoPoolWarnStateForTests allows a later warn (emptiness reappeared)", () => { + resetEmptyAutoPoolWarnStateForTests(); + assert.equal(warnEmptyAutoPoolOnce("auto/zai", "empty"), true); + resetEmptyAutoPoolWarnStateForTests(); + assert.equal(warnEmptyAutoPoolOnce("auto/zai", "empty"), true); });