Skip to content

[OAuth] Missing storage in-memory cache #192

@bgaidioz

Description

@bgaidioz

Storage performance wrappers (read cache + write optimizer)

We keep the TokenStore single-op contract as the required API and add optional capabilities that wrappers can exploit.

  • Read-cache wrapper (CachingTokenStore):
    • Wraps any TokenStore; keeps per-process in-memory caches for sessions, states, auth codes, and clients.
    • Read-through with TTL = min(record expiry, cap) minus a small skew; negative-cache misses briefly.
    • Invalidate on store/delete/consume; one asyncio.Lock to guard cache maps.
    • Optional metrics: hit/miss/invalidation counters (no tokens/PII).
  • Write-optimizer wrapper (BufferedTokenStore):
    • Queues single-op writes; flush triggers: max_batch_size or max_delay.
    • Coalesce duplicate writes (keep last per key); reads bypass the buffer.
    • On flush: if the delegate exposes bulk hooks, use them; else run single-op calls inside one transaction/single worker to reduce lock/fsync churn.
    • Graceful shutdown: drain queue on close; retry or surface errors (no silent loss).
  • Optional bulk capability (feature-detected):
    • Define an optional protocol (e.g., BulkSessionStore) with:
      • bulk_store_sessions(records: list[StoredSession]) -> None
      • bulk_delete_sessions_by_hash(hashes: list[str]) -> None
      • optionally bulk_cleanup_expired() -> dict[str, int]
    • Backends may implement these; wrappers check isinstance(delegate, BulkSessionStore) (or hasattr) and fall back when absent.
  • SQLite backend updates:
    • Implement the bulk hooks using one transaction + executemany / DELETE … WHERE access_token_hash IN (…).
    • Keep existing single-op methods as-is; bulk is an optimization only.
  • Tests to add (make them backend-agnostic via parametrized fixtures):
    • Core TokenStore (keep existing): states/auth-codes one-time, expiry pruning, session load/store/delete by token/id/refresh, encryption/plaintext opt-in, cleanup.
    • Caching wrapper suite:
      • Hit/miss/negative-cache: first miss hits delegate, subsequent read hits cache; expired entries are evicted and not returned.
      • Invalidation: store/delete/consume paths evict relevant cache keys; no stale reads after writes/deletes/consumes.
      • TTL skew: cache TTL is slightly shorter than record expiry; ensure expired sessions/auth-codes/states are not served.
    • Buffered write wrapper suite:
      • Flush triggers: size threshold and time threshold both flush to delegate; after flush, data is persisted.
      • Coalescing: multiple writes to the same key before flush result in a single persisted value (last write wins).
      • Shutdown drain: close() drains the queue; no queued writes are lost.
      • Error propagation: delegate errors surface (or are retried once); no silent drops.
    • Bulk capability suite (run only if backend advertises bulk):
      • bulk_store_sessions / bulk_delete_sessions_by_hash persist/delete all items; results match looping single-op calls.
      • Fallback parity: when bulk is absent, wrapper falls back to single-op (ideally in one transaction) with identical outcomes.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions