Repository navigation
fix: move usage persistence off the inference response path - #63
Merged
zhengkunwang223 merged 1 commit intoOct 10, 2026
Merged
Conversation
tsonglew
force-pushed
the
codex/async-usage-recording
branch
from
October 10, 2026 10:21
27f66ab to
a28beb7
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
infer()currently waits for usage inserts, the API key'slast_used_atupdate, transaction commit and connection close before returning. On a deployment with slow persistent storage, model prediction took 23–104 ms while backend response times were about 2–3 seconds. An isolated WAL write/commit/close probe took 541–1695 ms on the data volume. Usage persistence can also turn an otherwise successful prediction into503 USAGE_UNAVAILABLE.Change
Move usage persistence to one lifespan-managed background writer per process. Requests enqueue a small event and return the original prediction, including its token usage. The writer reuses its SQLite connection, commits batches of up to 100 events, flushes partial batches after a one-second collection interval, and coalesces last-used updates without moving timestamps backwards.
The queue is bounded to 1,000 pending events plus one in-flight batch. Shutdown stops accepting events and attempts to drain for up to five seconds without blocking the ASGI event loop. API-key authentication, revocation and admin writes remain synchronous; there is no schema change.
Telemetry tradeoff
Persisted usage is now eventually consistent and best-effort, not a billing/quota ledger. Queue overflow drops new statistics with rate-limited warnings. SQLite failures roll back where possible and drop/log the affected batch; later batches reconnect. Failed batches are deliberately not retried because an uncertain commit could double-count them. A forced exit or shutdown timeout can lose pending events, and an in-flight SQLite call may outlive the shutdown wait. These semantics are documented in both READMEs.
Validation
python -m pytest backend/tests -q(55 tests)./v1/systemoneand/internal/playgroundhold the background write behind an event and confirm responses complete before it is released, including simulated storage failure.