Refactor backend to use request-scoped SQLite connections
This commit is contained in:
@@ -7,6 +7,7 @@ directly — to keep coupling explicit and testable.
|
||||
"""
|
||||
|
||||
import time
|
||||
from collections.abc import AsyncGenerator
|
||||
from typing import Annotated, Protocol, cast
|
||||
|
||||
import aiosqlite
|
||||
@@ -61,26 +62,35 @@ def invalidate_session_cache(token: str) -> None:
|
||||
_session_cache.pop(token, None)
|
||||
|
||||
|
||||
async def get_db(request: Request) -> aiosqlite.Connection:
|
||||
"""Provide the shared :class:`aiosqlite.Connection` from ``app.state``.
|
||||
async def get_db(request: Request) -> AsyncGenerator[aiosqlite.Connection, None]:
|
||||
"""Provide a request-scoped :class:`aiosqlite.Connection` for the current request.
|
||||
|
||||
Opens a fresh connection for every request and closes it when the request
|
||||
is finished. This avoids contention and locking issues from a single shared
|
||||
SQLite connection across concurrent requests.
|
||||
|
||||
Args:
|
||||
request: The current FastAPI request (injected automatically).
|
||||
|
||||
Returns:
|
||||
The application-wide aiosqlite connection opened during startup.
|
||||
|
||||
Raises:
|
||||
HTTPException: 503 if the database has not been initialised.
|
||||
Yields:
|
||||
An open :class:`aiosqlite.Connection` for the request.
|
||||
"""
|
||||
db: aiosqlite.Connection | None = getattr(request.app.state, "db", None)
|
||||
if db is None:
|
||||
log.error("database_not_initialised")
|
||||
from app.db import open_db # noqa: PLC0415
|
||||
|
||||
settings = cast("AppState", request.app.state).settings
|
||||
try:
|
||||
db = await open_db(settings.database_path)
|
||||
except Exception as exc:
|
||||
log.error("database_open_failed", error=str(exc))
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_503_SERVICE_UNAVAILABLE,
|
||||
detail="Database is not available.",
|
||||
)
|
||||
return db
|
||||
) from exc
|
||||
|
||||
try:
|
||||
yield db
|
||||
finally:
|
||||
await db.close()
|
||||
|
||||
|
||||
async def get_settings(request: Request) -> Settings:
|
||||
|
||||
Reference in New Issue
Block a user