Task · CXO-0044

Per-account quota and key-eligibility gauges from Postgres, covering accounts that serve no traffic

Description

Every quota gauge this service emits is derived from codex.rate_limits events in the archive, so an account only has a series while it is serving traffic. On 2026-09-12 the deployment held five accounts and Grafana carried codexlb_rate_limit_used_percent for two of them. The other three - including one configured burn_first with a completely fresh quota - were invisible.

That blind spot is not theoretical. A burn_first account added that morning was never routed to, because both API keys have account_assignment_scope_enabled true and the account was in neither keys api_key_accounts rows. Account scoping is applied to the candidate pool BEFORE routing policy is consulted, so the policy never got a look in. Nothing in telemetry could show it: the account had zero requests, therefore zero events, therefore no series. Diagnosis required a direct database session.

Postgres already holds the complete picture, refreshed roughly every two minutes for every account regardless of traffic.

usage_history: account_id, recorded_at, window, used_percent, reset_at, window_minutes, credits_has, credits_unlimited, credits_balance. 68,891 rows, about 120 per hour. window is primary or secondary; input_tokens and output_tokens are null on every row. reset_at and credits_balance are null on the secondary rows. additional_usage_history: account_id, recorded_at, quota_key, limit_name, metered_feature, window, used_percent, reset_at, window_minutes. 73,157 rows, about 270 per hour. quota_key takes two values, limit_name two, metered_feature two, window primary or secondary. accounts: id, plan_type, status, routing_policy, limit_warmup_enabled, security_work_authorized, deactivation_reason. status is a Postgres enum with six labels: active, rate_limited, quota_exceeded, paused, reauth_required, deactivated. routing_policy observed as burn_first, normal, preserve. api_keys and api_key_accounts: give the eligibility join. An account is reachable by a key when the key is active AND (the keys account_assignment_scope_enabled is false OR a matching api_key_accounts row exists).

The read-only role codexlb2otel_ro was granted SELECT on usage_history, additional_usage_history and api_key_accounts on 2026-09-12; it already had request_logs, api_keys and accounts. Six tables total. The service still never creates roles or changes grants.

Traps, all measured:

The new metrics are a separate family from the archive-derived codexlb.rate_limit.* gauges and do not replace them. Different source, different coverage, different freshness: merging them would make an absent series ambiguous. Frozen 2026-09-12 by the operator.

codexlb.account.quota_used_percent gauge, %, by account id, window, plan type codexlb.account.quota_reset_after gauge, s, by account id, window codexlb.account.model_quota_used_percent gauge, %, by account id, quota key, window codexlb.account.credits_balance gauge, by account id codexlb.account.info gauge 1, by account id, status, plan type, routing policy codexlb.account.api_key_eligible gauge 0|1, by account id, api key name

Measured query cost against the live database: the latest-per-account reads plan on the recorded_at indexes and execute in under 1 ms each; the eligibility join is a five-by-two cross join executing in 0.11 ms.

Acceptance Criteria

Definition of Done

Implementation Plan

  1. Freeze account-poller config, snapshot and metric contracts.

  2. Implement a scheduled read-only poller publishing immutable snapshots.

  3. Wire non-blocking observable gauges and verify focused, integration and live evidence.

  4. Reproduce the live int4 reset_at scan failure test-first and make both quota queries return timestamp values without changing the snapshot contract.

  5. Run focused and integrated gates, obtain a fresh read-only review, push exact code, redeploy with the authorized Camden stop/pull/up path, and require non-empty account-family series.

  6. Wave 5: add the real registered-callback non-blocking seam regression, publish the bounded poll outcome counter, wire it through the application, and prove it live after exact-SHA deployment.

Implementation Notes

Park boundary: add an end-to-end test that publishes a real Poller snapshot and collects it through the registered observable callbacks while proving collection cannot wait on poller database I/O or locks; add the missing frozen poller self-observability counter contract; then enable account_poller in Camden configuration under deployment authority and verify codexlb_account_info live. The current code, read-only Postgres statements, disabled-by-default config, and dashboard panels are present, but AC2 and AC7 remain unproven and Camden configuration is intentionally unchanged.

Deployment-config authority was granted on 2026-09-12. Camden was backed up, account_poller was enabled with the existing read-only DSN, Compose validation passed, and the authorized stop/pull/up completed. The first real poll failed safely with: accountpoll: scan usage history: cannot scan int4 (OID 23) in binary format into **time.Time. The task is reopened for this production-shape bug; no account data was exposed.

Resumed deployment evidence: the existing DSN was reused, the poller config was enabled after a timestamped backup, and the first old-image poll exposed reset_at as int4 rather than timestamptz. A failing regression pinned to_timestamp(reset_at) on both quota statements; focused race tests, a real DSN-gated Poller scan, integrated just check, CodeRabbit with zero findings, and fresh read-only L7 review followed. Exact CI 34694991455 and publish 34694991713 succeeded at 88773a47c9e56005ea759e73a7774c6971f4d04a. The healthy deployed image has manifest digest sha256:69c38f727568d63ea61527e03de6bb6e9df70bc8e2e13801b70b896654068bef, zero account-poll failures since start, and Grafana read-back returned aggregate series counts: account info 5, API-key eligibility 10, quota used 8, model quota used 9, reset-after 4, credits balance 4. AC2 and AC7 remain unchecked at the recorded test and self-observability boundary.

Wave 5 closure: a registered ManualReader callback collected through a real Poller while a second poll was blocked inside Store.Query, returned the previously published snapshot within 250 ms, and performed no callback database IO. Poll outcomes are now the bounded success/error/disabled counter and the deployed success series advanced from 1 to 4 across three two-minute intervals. CodeRabbit pass 2 had zero findings and L4 accepted all recurring-defect challenges.

Final Summary

Implemented the scheduled read-only account poller, immutable snapshots, account and eligibility gauges, disabled-by-default configuration, wiring, tests, dashboards, and live SQL proof. Parked because the required real Poller-to-registered-callback non-blocking seam test and a frozen poller self-observability counter are absent; Camden account_poller is also not enabled, so account gauges cannot be verified live.

Correction after resumed authority: Camden account polling is now enabled and live. The production int4 reset_at scan defect was repaired at 88773a47, exact CI and publication passed, the container is healthy, and all six account metric families are non-empty in Grafana. The task remains Parked only because AC2 lacks a real Poller-to-registered-callback collection test and AC7 lacks the frozen self-observability counter.

Wave 5 closed AC2 and AC7 at commit 13d3fe0: the observable seam is empirically non-blocking under real Poller database contention, failures remain isolated, and the outcome counter plus all six account families were proven live after exact-head CI and deployment.

View the source file on GitHub