Source code for corvix.web.storage_provider

"""Process-wide storage backend singleton for the web app.

Production wiring builds a PostgreSQL backend lazily from config on first use
and caches it so its connection pool is reused across requests.  Tests inject a
backend directly via :func:`set_storage_backend` and reset it to ``None``
afterwards.
"""

from __future__ import annotations

import threading

from litestar.exceptions import HTTPException

from corvix.storage import StorageBackend, StorageConfigError, create_storage
from corvix.web.runtime_config import _load_runtime_config


[docs] class _StorageState: """Mutable container for the module-level storage backend."""
[docs] backend: StorageBackend | None = None
[docs] lock: threading.Lock = threading.Lock()
[docs] _storage_state = _StorageState()
[docs] def set_storage_backend(backend: StorageBackend | None) -> None: """Inject the storage backend used by route handlers. Production wiring leaves this unset and the backend is built lazily from config (PostgreSQL is required). Tests inject a backend directly and reset it to ``None`` afterwards. """ _storage_state.backend = backend
[docs] def _get_storage() -> StorageBackend: """Return the injected backend, or lazily build PostgreSQL storage from config. The built backend is cached so its connection pool is reused across requests. Building is guarded by a lock so concurrent first requests don't each create (and leak) a connection pool. Raises ``HTTPException`` (500) when no database is configured. """ if _storage_state.backend is not None: return _storage_state.backend with _storage_state.lock: if _storage_state.backend is not None: return _storage_state.backend config = _load_runtime_config() try: backend = create_storage(config) except StorageConfigError as error: raise HTTPException(status_code=500, detail=str(error)) from error _storage_state.backend = backend return backend