Skip to content

[WIP] Bound re-enqueue of usage payloads with unresolvable API key hash - #2610

Draft
PawelPeczek-Roboflow wants to merge 2 commits into
mainfrom
fix/inference-bugs-finding-92
Draft

[WIP] Bound re-enqueue of usage payloads with unresolvable API key hash#2610
PawelPeczek-Roboflow wants to merge 2 commits into
mainfrom
fix/inference-bugs-finding-92

Conversation

@PawelPeczek-Roboflow

Copy link
Copy Markdown
Collaborator

🚧 Work in progress — not ready for review

Draft PR from a batch addressing findings from an in-depth engineering review. Fixes review finding 92 (Low, Correctness).

The bug

When usage telemetry is persisted to SQLite, rows are keyed by api_key_hash. After a process restart the in-memory _hashed_api_keys map is empty, so hashes_to_api_keys in _offload_to_api (inference/usage_tracking/collector.py:544) cannot resolve a persisted hash to its plaintext key. send_usage_payload then either marks the hash failed or sends it with the SHA256 hash used as the Bearer token, which the server rejects. The failed hash is kept in the payload and re-enqueued by _offload_to_api (collector.py:594-596 pre-fix). Because SQLiteQueue.full() is hardcoded to False, nothing bounds this, so rows for a key that is never re-seen re-enqueue forever, accumulating in usage.db and wasting a request every flush.

Root cause

The re-enqueue loop treated an unresolvable-key failure identically to a transient failure and retried it indefinitely, even though it can never succeed until the same plaintext key issues another request.

Fix

inference/usage_tracking/collector.py only:

  • Add class constant _MAX_UNRESOLVED_RESEND_ATTEMPTS = 5 and an instance counter self._unresolved_resend_attempts: Dict[APIKeyHash, int].
  • In _offload_to_api, split the re-enqueue decision three ways: delivered payloads are dropped and their counter cleared; failures whose hash is resolvable (transient, e.g. network down) still retry indefinitely with the counter reset; failures whose hash is not resolvable increment the counter and are dropped once it reaches the bound.

No plaintext API keys are persisted to disk (avoiding a security regression), and legitimate retry behaviour for known keys is preserved.

Verification

Standalone simulation of the corrected re-enqueue loop (full package import isn't possible from the sparse worktree):

  • Unresolvable hash, key never re-seen: re-enqueued 4 times then dropped on cycle 5 (previously unbounded).
  • Key becomes resolvable on cycle 3: payload is sent, not dropped, counter reset.
  • Known key with persistent transient failure: never dropped (retry behaviour intact).

python3 -m py_compile inference/usage_tracking/collector.py passes.

Scope

Focused change; one of a coordinated batch (one PR per finding).


🤖 Generated with Claude Code

PawelPeczek-Roboflow and others added 2 commits July 6, 2026 16:28
Persisted usage rows are keyed by api_key_hash. After a restart the plaintext key is unknown until re-seen, so send_usage_payload authenticates with the hash, the server rejects it, and _offload_to_api re-enqueues forever (SQLiteQueue.full() is always False). Drop such rows after a bounded number of attempts; known-key transient failures still retry.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@PawelPeczek-Roboflow PawelPeczek-Roboflow added the skip-claude-review Opt this PR out of automated Claude review label Jul 6, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

skip-claude-review Opt this PR out of automated Claude review

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant