app.services.apikey_cache_backend module

Pluggable cache backends for the API key header lookup.

The in-memory backend preserves the original per-process dict (zero dependency, fast, but one cache per gunicorn worker – revocations only reach the worker that handled the request and other workers serve the cached value until TTL expires). The Redis backend stores entries in a single shared keyspace so every worker observes revocations on the next request.

Selection is driven by configs.APIKEY_CACHE_BACKEND (“memory” or “redis”) and wired in app/core/container.py.

class app.services.apikey_cache_backend.ApiKeyCacheBackend(*args, **kwargs)[source]

Bases: Protocol

Cache the resolved (apiKey, active) tuple keyed by sha256 hash.

async get(cache_key)[source]

Return the cached value for cache_key, or None if absent.

Parameters:

cache_key (str)

Return type:

SimpleNamespace | None

async set(cache_key, value, ttl_seconds)[source]

Store value under cache_key with a ttl_seconds lifetime.

Parameters:
  • cache_key (str)

  • value (SimpleNamespace)

  • ttl_seconds (int)

Return type:

None

async delete(cache_key)[source]

Remove the entry for cache_key if present.

Parameters:

cache_key (str)

Return type:

None

async clear()[source]

Remove every cached entry.

Return type:

None

class app.services.apikey_cache_backend.InMemoryApiKeyCacheBackend[source]

Bases: object

Per-process dict cache with monotonic TTL and lazy eviction.

The asyncio lock is lazy-bound on first use and reset by sync_clear so tests that swap event loops between cases don’t trip “attached to a different loop”. This mirrors the behavior the cache had when it lived on ApiKeyService directly.

async get(cache_key)[source]

Return the cached value for cache_key, evicting it if expired.

Parameters:

cache_key (str) – The lookup key (sha256 of the API key plaintext).

Returns:

Optional[SimpleNamespace] – The cached (apiKey, active) value, or None if absent or past its TTL.

Return type:

SimpleNamespace | None

async set(cache_key, value, ttl_seconds)[source]

Cache value under cache_key for ttl_seconds (no-op if ≤ 0).

Parameters:
  • cache_key (str) – The lookup key.

  • value (SimpleNamespace) – The (apiKey, active) value to store.

  • ttl_seconds (int) – Lifetime in seconds; values ≤ 0 are ignored.

Return type:

None

async delete(cache_key)[source]

Remove the cached entry for cache_key if present.

Parameters:

cache_key (str) – The lookup key to evict.

Return type:

None

async clear()[source]

Remove every cached entry (async wrapper over sync_clear).

Return type:

None

sync_clear()[source]

Clear the store synchronously and reset the lock for a new loop.

Return type:

None

class app.services.apikey_cache_backend.RedisApiKeyCacheBackend(client, key_prefix='game:apikey:')[source]

Bases: object

Redis-backed cache shared across workers. Values are stored as compact JSON ({"apiKey": <prefix>, "active": <bool>}) with TTL set via the EX argument on SET so Redis handles expiration server-side.

Parameters:

key_prefix (str)

async get(cache_key)[source]

Read and deserialize a cached value from Redis.

Parameters:

cache_key (str) – The lookup key.

Returns:

Optional[SimpleNamespace] – The decoded (apiKey, active) value, or None if the key is absent.

Return type:

SimpleNamespace | None

async set(cache_key, value, ttl_seconds)[source]

Store value as JSON in Redis with a server-side TTL (no-op if ≤ 0).

Parameters:
  • cache_key (str) – The lookup key.

  • value (SimpleNamespace) – The (apiKey, active) value to store.

  • ttl_seconds (int) – Expiry passed to Redis SET ... EX.

Return type:

None

async delete(cache_key)[source]

Delete a single cached entry from Redis.

Parameters:

cache_key (str) – The lookup key to evict.

Return type:

None

async clear()[source]

Delete every entry under the key prefix via a bounded SCAN + DEL.

Return type:

None

app.services.apikey_cache_backend.build_apikey_cache_backend(backend_name, redis_url, redis_key_prefix)[source]

Select the configured backend. Falls back to the in-memory backend with a warning when Redis is requested but REDIS_URL is missing – so a misconfigured deploy still authenticates (just with the per-process consistency caveat) instead of failing requests outright.

Parameters:
  • backend_name (str)

  • redis_url (str | None)

  • redis_key_prefix (str)

Return type:

ApiKeyCacheBackend