Skip to content

feat(core): add QuotaManager for unified in-memory quota cache#20

Open
iceteaSA wants to merge 1 commit into
cortexkit:mainfrom
iceteaSA:feat/quota-manager
Open

feat(core): add QuotaManager for unified in-memory quota cache#20
iceteaSA wants to merge 1 commit into
cortexkit:mainfrom
iceteaSA:feat/quota-manager

Conversation

@iceteaSA
Copy link
Copy Markdown

Summary

Adds a QuotaManager class in packages/core that provides a single in-memory cache for main + fallback account quota data. All consumers share one instance so they see the same cache.

Key features

  • Inflight deduplication — concurrent refreshMain() calls return the same promise
  • Global 429 backoff — on rate-limit from the usage API, all quota fetches back off for 60s
  • Staleness queriesisMainStale(), isFallbackStale(id), needsRefresh(requestCount)
  • Seeding from diskseedFallbacksFromAccounts() hydrates cache from persisted account.quota without API calls
  • Fire-and-forgetrefreshMainInBackground() for non-blocking background refresh
  • Optional request-count triggersshouldRefreshOnRequestCount(n) for catching fast quota drops between timer checks

Integration with FallbackAccountManager

FallbackAccountManager accepts an optional quotaManager in AccountManagerOptions. When provided:

  • getUsableFallbackAccounts() and refreshQuotaForDueAccounts() use quotaManager.isFallbackStale() instead of per-account disk staleness
  • Seeds QuotaManager from persisted quota before staleness checks
  • Syncs writes to QuotaManager after each successful quota refresh

Non-breaking: the parameter is optional. Existing callers that don't pass it get the existing behavior unchanged.

Motivation

Without a shared cache, each quota consumer (routing, commands, background timers) independently fetches from the usage API, leading to:

  • Redundant concurrent API calls (especially on the Nth request where killswitch + routing both fire)
  • 429 rate-limit cascades when multiple consumers hit the API simultaneously
  • Stale reads where one consumer has fresh data but another doesn't

QuotaManager solves all three by being the single gateway to the usage API with built-in deduplication and backoff.

Files Changed

  • packages/core/src/quota-manager.tsnew: QuotaManager class (~300 lines)
  • packages/core/src/accounts.tsAccountManagerOptions.quotaManager, seedFallbackQuota(), staleness integration
  • packages/core/src/index.ts — re-export

Testing

All 214 existing tests pass. QuotaManager is opt-in — no existing behavior changes when quotaManager is not provided.

All quota consumers (routing, commands, background timers) can share a
single QuotaManager instance with one in-memory cache. This eliminates:

- Redundant API calls via inflight request deduplication
- 429 rate-limit cascades via global 60s backoff
- Stale quota reads across consumers

QuotaManager is purely in-memory with no file I/O. FallbackAccountManager
accepts an optional quotaManager in its constructor and syncs writes to
it after each refresh. Seeding from persisted account.quota prevents
unnecessary API calls when on-disk snapshots are still fresh.

Non-breaking: quotaManager is optional in AccountManagerOptions.
Consumers that don't use it get the existing behavior unchanged.
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.

1 participant