Skip to content

[feature] Window-aware preemptive quota deferral instead of the flat 2h cap #656

Description

@choa712

Problem Statement

The preemptive quota scheduler defers a near/fully exhausted account for Math.min(longestWait, maxDeferralMs) (lib/preemptive-quota-scheduler.ts:290), with DEFAULT_MAX_DEFERRAL_MS = 2h (lib/preemptive-quota-scheduler.ts:30, config preemptiveQuotaMaxDeferralMs).

For a weekly (7d) or monthly window that is fully exhausted with a cached reset timestamp days away, this means: the preemptive deferral expires after 2 hours → the account becomes preemptively eligible again → the next selection can route a live request into a guaranteed 429 → the reactive path then re-benches it from the reset headers (getQuotaNearExhaustionWaitMs clamps at 7d). Net effect: a periodic wasted request + cooldown churn against an account that is known (from cached reset data) to be dead for days. The reset timestamps are already fetched and cached since 2.7.0, so the information needed to defer correctly exists.

(We hit this with a weekly-exhausted account in a 3-account pool; the churn is modest but systematic, and it makes forecast/selection behavior harder to reason about during incidents.)

Proposed Solution

Make the deferral bound window-aware instead of a flat cap:

  • When the exhausted window's cached resetAtMs is finite and trusted, allow deferral up to that reset (optionally with a safety ceiling, e.g. the existing 7d MAX_RATE_LIMIT_DELAY_MS clamp).
  • Keep the current 2h cap as the fallback when reset data is missing, bogus, or stale — preserving the existing defense-in-depth rationale against bogus upstream reset headers.
  • Optionally: make preemptiveQuotaMaxDeferralMs per-window (5h vs 7d/monthly) instead of global.

Expected impact: fully-exhausted long-window accounts stop cycling through "preemptive release → live 429 → reactive re-bench" every 2 hours; selection and forecast stay consistent with the cached quota reality.

Alternatives Considered

  • Raising preemptiveQuotaMaxDeferralMs globally (works today as an operator override, but a flat global value either under-defers long windows or over-defers 5h windows).
  • Pausing the exhausted account manually until its weekly reset (our current workaround; requires operator attention).
  • Doing nothing: the reactive 429 path does self-correct, at the cost of one live failed request per cycle.

Scope

In scope: the deferral bound computation in the preemptive quota scheduler and its config surface. Out of scope: reactive rate-limit handling, quota probing, scoring.

Validation Plan

  • Unit: snapshot with usedPercent=100 and resetAtMs = now + 72h → deferral equals 72h (window-aware path), not 2h; snapshot with missing/invalid resetAtMs → falls back to the 2h cap.
  • Command-level: with a weekly-exhausted account, observe (e.g. via report --explain / router logs) that no live request is routed to it between preemptive deferrals until the cached reset passes.

Compliance Confirmation

  • This proposal is for legitimate personal development workflows.
  • This proposal does not request bypassing OpenAI controls or policies.
  • This proposal does not introduce multi-tenant resale behavior.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions