mirror of
https://github.com/diegosouzapw/OmniRoute.git
synced 2026-08-03 13:52:09 +03:00
Closes #4438. Dedupes quota_snapshots writes for idle connections via pure quotaSnapshotChanged() gate. TDD 5/5. Admin-merged over a pre-existing, unrelated base red (model-lockout-max-cooldown.test.ts 'markAccountUnavailable local 404 ... maxCooldownMs', from #4530 incomplete wiring) proven to fail on release/v3.8.33 HEAD without this change.
This commit is contained in:
committed by
GitHub
parent
448741fe5f
commit
cf6546e6d3
@@ -17,6 +17,7 @@ _In development — bullets added per PR; finalized at release._
|
||||
- **fix(auto): enforce the quota cutoff before scoring (opt-in)** — auto-routing now evaluates a hard quota cutoff in `buildAutoCandidates` to drop low-quota candidates before scoring, with a 429 guard when all candidates fall below cutoff. The cutoff is **opt-in** behind `QuotaPreflightSettings.enabled` (default OFF via `QUOTA_PREFLIGHT_CUTOFF_ENABLED`), so default behavior is unchanged. ([#4483](https://github.com/diegosouzapw/OmniRoute/pull/4483) — thanks @megamen32)
|
||||
- **fix(antigravity): reasoning/thinking models no longer 400 with `oneOf at '/' not met`** — the Cloud Code envelope passthrough also leaked the Claude/OpenAI-native thinking fields (`thinking`, `reasoning_effort`, `reasoning`, `enable_thinking`, `thinking_budget`) the unified thinking adapter sets at the body root; Google rejected them with `400 Bad input: oneOf at '/' not met`. The whole thinking family is now stripped before the envelope is built; Gemini's own `generationConfig.thinkingConfig` is unaffected. (port from 9router#1926 — thanks @theseven99 / @diegosouzapw)
|
||||
- **fix(integration): restore the codex and memory pipeline contracts** — realigns the CLI fingerprint + memory-tools contracts so the codex and memory pipelines pass their integration checks again. ([#4474](https://github.com/diegosouzapw/OmniRoute/pull/4474) — thanks @KooshaPari)
|
||||
- **perf(quota): stop writing redundant `quota_snapshots` rows from idle connections** — the 60s background refresh persisted a snapshot for every window of every connection regardless of change, generating 400K+ rows/day from idle accounts. `setQuotaCache` now skips the write when a window's `remaining_percentage`/`is_exhausted` is unchanged from the last cached observation; the first observation and every real change still persist. ([#4438](https://github.com/diegosouzapw/OmniRoute/issues/4438) — thanks @oyi77)
|
||||
|
||||
### 📝 Maintenance
|
||||
|
||||
|
||||
@@ -156,6 +156,34 @@ function earliestResetAt(quotas: Record<string, QuotaInfo>): string | null {
|
||||
return earliest;
|
||||
}
|
||||
|
||||
/**
|
||||
* #4438 — Decide whether a quota snapshot row is worth persisting.
|
||||
*
|
||||
* The background refresh ticks every 60s for ALL connections, so idle accounts
|
||||
* (whose quota never changes) were generating 400K+ identical snapshot rows/day.
|
||||
* Returns true only when this window has no prior cached observation, or when its
|
||||
* `remaining_percentage` / `is_exhausted` differs from the last cached entry — so
|
||||
* the first observation and every real change persist, but idle no-op refreshes
|
||||
* stop writing. Pure (no I/O) for trivial unit testing.
|
||||
*/
|
||||
export function quotaSnapshotChanged(
|
||||
prior:
|
||||
| { quotas?: Record<string, { remainingPercentage: number }>; exhausted?: boolean }
|
||||
| null
|
||||
| undefined,
|
||||
windowKey: string,
|
||||
remainingPercentage: number,
|
||||
exhausted: boolean
|
||||
): boolean {
|
||||
if (!prior) return true;
|
||||
const priorWindow = prior.quotas?.[windowKey];
|
||||
if (!priorWindow) return true;
|
||||
return (
|
||||
priorWindow.remainingPercentage !== remainingPercentage ||
|
||||
(prior.exhausted ?? false) !== exhausted
|
||||
);
|
||||
}
|
||||
|
||||
function normalizeQuotas(rawQuotas: Record<string, any>): Record<string, QuotaInfo> {
|
||||
const result: Record<string, QuotaInfo> = {};
|
||||
for (const [key, q] of Object.entries(rawQuotas)) {
|
||||
@@ -183,6 +211,9 @@ export function setQuotaCache(
|
||||
) {
|
||||
const quotas = normalizeQuotas(rawQuotas);
|
||||
const exhausted = isExhausted(quotas);
|
||||
// #4438 — capture the prior entry BEFORE overwriting the cache so we can skip
|
||||
// redundant snapshot writes for idle connections whose quota didn't change.
|
||||
const prior = cache.get(connectionId);
|
||||
const entry: QuotaCacheEntry = {
|
||||
connectionId,
|
||||
provider,
|
||||
@@ -201,6 +232,8 @@ export function setQuotaCache(
|
||||
(quotaInfo.total > 0
|
||||
? Math.round(((quotaInfo.total - (quotaInfo.used || 0)) / quotaInfo.total) * 100)
|
||||
: 0);
|
||||
// #4438 — only persist on the first observation or a real change.
|
||||
if (!quotaSnapshotChanged(prior, windowKey, remainingPercentage, entry.exhausted)) continue;
|
||||
try {
|
||||
saveQuotaSnapshot({
|
||||
provider,
|
||||
|
||||
35
tests/unit/quota-cache-snapshot-dedup-4438.test.ts
Normal file
35
tests/unit/quota-cache-snapshot-dedup-4438.test.ts
Normal file
@@ -0,0 +1,35 @@
|
||||
import { test } from "node:test";
|
||||
import assert from "node:assert/strict";
|
||||
|
||||
import { quotaSnapshotChanged } from "@/domain/quotaCache";
|
||||
|
||||
// Regression guard for #4438: quota_snapshots generated 400K+ rows/day because
|
||||
// setQuotaCache wrote a snapshot row for EVERY window of EVERY connection on each
|
||||
// 60s background refresh, even for idle connections whose quota never changed.
|
||||
// quotaSnapshotChanged() gates the write so unchanged idle connections stop
|
||||
// generating rows, while the first observation and every real change still persist.
|
||||
|
||||
test("#4438 writes when there is no prior cache entry (baseline row)", () => {
|
||||
assert.equal(quotaSnapshotChanged(null, "daily", 100, false), true);
|
||||
assert.equal(quotaSnapshotChanged(undefined, "daily", 42, false), true);
|
||||
});
|
||||
|
||||
test("#4438 writes when the window was never seen before", () => {
|
||||
const prior = { quotas: { weekly: { remainingPercentage: 80 } }, exhausted: false };
|
||||
assert.equal(quotaSnapshotChanged(prior, "daily", 80, false), true);
|
||||
});
|
||||
|
||||
test("#4438 skips when remaining_percentage and is_exhausted are unchanged (idle connection)", () => {
|
||||
const prior = { quotas: { daily: { remainingPercentage: 73 } }, exhausted: false };
|
||||
assert.equal(quotaSnapshotChanged(prior, "daily", 73, false), false);
|
||||
});
|
||||
|
||||
test("#4438 writes when remaining_percentage changed", () => {
|
||||
const prior = { quotas: { daily: { remainingPercentage: 73 } }, exhausted: false };
|
||||
assert.equal(quotaSnapshotChanged(prior, "daily", 72, false), true);
|
||||
});
|
||||
|
||||
test("#4438 writes when is_exhausted flipped even if percentage matches", () => {
|
||||
const prior = { quotas: { daily: { remainingPercentage: 0 } }, exhausted: false };
|
||||
assert.equal(quotaSnapshotChanged(prior, "daily", 0, true), true);
|
||||
});
|
||||
Reference in New Issue
Block a user