Skip to content

perf: cache API key validation#139

Open
Genmin wants to merge 2 commits intoXortexAI:mainfrom
Genmin:fix/api-key-validation-cache
Open

perf: cache API key validation#139
Genmin wants to merge 2 commits intoXortexAI:mainfrom
Genmin:fix/api-key-validation-cache

Conversation

@Genmin
Copy link
Copy Markdown

@Genmin Genmin commented May 1, 2026

Summary

  • add a bounded in-memory TTL cache for active MongoDB API-key validation results keyed by SHA-256 key hash
  • avoid repeated MongoDB find_one/update_one calls while a key is cached, keeping last_used fresh in the returned copy and refreshing MongoDB on cache miss/expiry
  • clear the validation cache when API keys are created, revoked, or renamed so same-process changes take effect immediately
  • add focused tests for cache hits, cache expiry, and inactive/missing keys

Why

Authenticated endpoints call validate_api_key on every request. Before this change, each request paid for both a MongoDB lookup and a last_used write. The cache keeps successful validations hot for a short bounded window while preserving existing behavior for invalid keys and in-memory fallback mode.

Fixes #135.

Validation

  • python3 -m pytest -o addopts='' tests/test_api_key_store.py -q
  • python3 -m py_compile src/database/api_key_store.py tests/test_api_key_store.py
  • git diff --check

Note: the repo's default pytest addopts require pytest-cov in the environment; the targeted test was run with addopts disabled because pytest-cov is not installed in this checkout.

@ishaanxgupta ishaanxgupta added the enhancement New feature or request label May 1, 2026
@ved015 ved015 assigned ved015 and unassigned ved015 May 1, 2026
@ved015
Copy link
Copy Markdown
Contributor

ved015 commented May 2, 2026

/gemini review

Copy link
Copy Markdown

@gemini-code-assist gemini-code-assist Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request introduces an in-memory validation cache for API keys in the APIKeyStore class to optimize performance, along with a new test suite. The implementation includes TTL-based expiration and size-limited eviction. Feedback points out critical issues: the use of instance-level caching leads to inconsistency across multiple service instances, the eviction logic lacks thread-safety which could cause runtime errors, and clearing the entire cache during key creation is an unnecessary performance penalty.

self.api_keys = None
self._connected = False
self._in_memory = False
self._validation_cache: Dict[str, tuple[float, Dict[str, Any]]] = {}
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

The _validation_cache is defined as an instance variable, but APIKeyStore is instantiated multiple times in the application (e.g., in src/api/routes/api_keys.py and src/api/dependencies.py). This means each instance maintains its own independent cache.

When a key is revoked or updated in one instance, the cache in the other instance (used by the authentication middleware) will not be cleared, allowing revoked keys to remain valid for the duration of the TTL. Consider making the cache a module-level variable (similar to _in_memory_api_keys) or ensuring a singleton instance is shared across the application.

Comment on lines +108 to +110
if len(self._validation_cache) >= VALIDATION_CACHE_MAX_SIZE:
oldest_key = next(iter(self._validation_cache))
self._validation_cache.pop(oldest_key, None)
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

The cache eviction logic is not thread-safe. In a multi-threaded environment (like FastAPI with multiple worker threads), next(iter(self._validation_cache)) can raise a StopIteration exception if the dictionary is cleared by another thread between the length check and the next() call. It can also raise a RuntimeError if the dictionary size changes during iteration.

Consider using a threading.Lock to synchronize access to the cache or using a more robust eviction pattern that handles these concurrency edge cases.

"is_active": True,
}
result = self.api_keys.insert_one(key_doc)
self._clear_validation_cache()
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

Clearing the entire validation cache when a new API key is created is unnecessary. A newly generated key cannot have a stale entry in the cache. Removing this call will prevent unnecessary cache misses for other active users during key creation.

@ved015
Copy link
Copy Markdown
Contributor

ved015 commented May 2, 2026

@Genmin Please have a look at the suggestions

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

Labels

enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Perf] Cache API key validation to eliminate per-request MongoDB hit

3 participants