Skip to content

fix(runtime): auto-refresh stale exhausted quota and back off probing - #312

Merged
caigee-cmd merged 3 commits into
caigee-cmd:mainfrom
favoroo:fix/runtime-quota-recovery
Oct 19, 2026
Merged

caigee-cmd merged 3 commits into
caigee-cmd:mainfrom
favoroo:fix/runtime-quota-recovery

Conversation

@favoroo

@favoroo favoroo commented Oct 18, 2026

Copy link
Copy Markdown
Contributor

Summary

A stale Exceeded=true quota snapshot kept an account out of routing until an operator manually refreshed it, and an all-exhausted pool produced only a generic "no accounts available".

  • The maintenance loop now re-fetches quota for accounts whose cached snapshot still reports Exceeded and is stale; healthy accounts are never touched.
  • pick() returns 429 insufficient_quota when every account that could serve the model is blocked only by exhausted quota.
  • Per-account probe backoff: 5m → 5m → 5m → 15m → 30m → 1h → 3h → 6h → 12h → 24h, so a permanently empty account stops costing ~288 quota calls/day. The ladder resets on a non-zero balance; a failed probe keeps its level.
  • routeBaseMatches split into the quota gate + routeReadyAndScoped so the quota-blocked check reuses scoping without duplication.

Tests

  • go build ./..., go vet ./..., go test ./..., git diff --check
  • New quota-blocked routing and quota-backoff tests.

…ocked routing

A stale Exceeded=true quota snapshot kept an account out of routing until an
operator manually refreshed it: nothing re-fetched quota in the background,
and a pool with every account quota-exhausted produced no routing hint, so
clients saw a generic "no accounts available" instead of a quota error.

- maintenance loop re-fetches quota every 5 minutes for accounts whose cached
  snapshot still reports Exceeded and is older than 5 minutes; healthy
  accounts are never touched (they are not in scope), disabled accounts are
  already absent from the pool, and dead accounts are skipped
- pick() returns 429 insufficient_quota (cooldown to local midnight) when
  every account that could serve the model is blocked only by exhausted
  quota, instead of "no accounts available"
- routeBaseMatches split into quota gate + routeReadyAndScoped so the
  quota-blocked check reuses the scoping conditions without duplication
- unit tests for both; changelog fragment (bilingual)
The stale-quota sweep re-fetched every Exceeded account on a fixed five
minute cadence, so an account that stayed empty for days cost ~288 quota
calls a day for no routing change. Provider balances reset on a daily
cadence, so that polling rate buys nothing.

Each account now carries a consecutive-exhausted level that selects the
next probe delay: 5m, 5m, 5m, 15m, 30m, 1h, 3h, 6h, 12h, then 24h. The
first three probes stay fast so a transient false Exceeded or an
out-of-band top-up still recovers within minutes. The level resets to the
fast end as soon as a snapshot reports a non-zero balance, and a failed
probe keeps its level so a transient outage is not escalated into an
hours-long wait. Level state is pruned to the current pool so it cannot
grow without bound.

Healthy accounts are still never touched by the sweep.
One bilingual fragment per PR, per changelog/unreleased/README.md.
@caigee-cmd
caigee-cmd merged commit dccfb54 into caigee-cmd:main Oct 19, 2026
6 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants