Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Initial setup of cache #22

Merged
merged 2 commits into from
Dec 23, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 15 additions & 0 deletions backend/app/api/deps.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@

import asyncpg
import jwt
import valkey.asyncio as valkey
from fastapi import Depends, HTTPException
from fastapi.security import OAuth2PasswordBearer
from jwt.exceptions import InvalidTokenError
Expand All @@ -15,6 +16,7 @@
HTTP_503_SERVICE_UNAVAILABLE,
)

from app.core.cache import cache
from app.core.config import settings
from app.core.db import db
from app.core.security import ALGORITHM
Expand All @@ -39,6 +41,19 @@ async def get_db_conn() -> AsyncGenerator[asyncpg.Connection, None]:
DbConn = Annotated[asyncpg.Connection, Depends(get_db_conn)]


async def get_cache_client() -> AsyncGenerator[valkey.Valkey, None]:
if cache.client is None:
logger.error("No cache client created")
raise HTTPException(
status_code=HTTP_503_SERVICE_UNAVAILABLE, detail="The cache is currently unavailable"
)

yield cache.client


CacheClient = Annotated[valkey.Valkey, Depends(get_cache_client)]


async def get_current_user(conn: DbConn, token: TokenDep) -> UserInDb:
try:
logger.debug("Decoding JWT token")
Expand Down
12 changes: 10 additions & 2 deletions backend/app/api/routes/health.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
from loguru import logger

from app.api.deps import DbConn
from app.api.deps import CacheClient, DbConn
from app.core.config import settings
from app.core.utils import APIRouter
from app.services.db_services import ping
Expand All @@ -11,7 +11,7 @@


@router.get("/")
async def health(*, db_conn: DbConn) -> dict[str, str]:
async def health(*, cache_client: CacheClient, db_conn: DbConn) -> dict[str, str]:
"""Check the health of the server."""

logger.debug("Checking health")
Expand All @@ -25,4 +25,12 @@ async def health(*, db_conn: DbConn) -> dict[str, str]:
logger.error(f"Unable to ping the database: {e}")
health["db"] = "unhealthy"

logger.debug("Checking cache health")
try:
await cache_client.ping()
health["cache"] = "healthy"
except Exception as e:
logger.error(f"Unable to ping the cache server: {e}")
health["cache"] = "unhealthy"

return health
35 changes: 35 additions & 0 deletions backend/app/core/cache.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import valkey.asyncio as valkey

from app.core.config import settings


class Cache:
def __init__(self) -> None:
self._pool: valkey.ConnectionPool | None = None
self.client: valkey.Valkey | None = None

async def create_client(self) -> None:
self._pool = await self._create_pool()
self.client = valkey.Valkey.from_pool(self._pool)

async def close_client(self) -> None:
if self.client:
await self.client.aclose()

if self._pool:
await self._pool.aclose()

async def _create_pool(self) -> valkey.ConnectionPool:
return valkey.ConnectionPool(
host=settings.VALKEY_HOST,
port=settings.VALKEY_PORT,
password=settings.VALKEY_PASSWORD.get_secret_value(),
db=0,
)

async def _close_pool(self) -> None:
if self._pool:
await self._pool.aclose()


cache = Cache()
4 changes: 4 additions & 0 deletions backend/app/core/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,9 @@ class Settings(BaseSettings):
POSTGRES_USER: str
POSTGRES_PASSWORD: SecretStr
POSTGRES_DB: str = "scan"
VALKEY_HOST: str
VALKEY_PASSWORD: SecretStr
VALKEY_PORT: int = 6379
OPENAI_API_KEY: SecretStr
DLPFC_MODEL: SecretStr = SecretStr("gpt-3.5-turbo")
VMPFC_MODEL: SecretStr = SecretStr("gpt-3.5-turbo")
Expand Down Expand Up @@ -89,6 +92,7 @@ def _enforce_non_default_secrets(self) -> Self:
)
self._check_default_secret("POSTGRES_USER", self.POSTGRES_USER)
self._check_default_secret("POSTGRES_PASSWORD", self.POSTGRES_PASSWORD.get_secret_value())
self._check_default_secret("VALKEY_PASSWORD", self.VALKEY_PASSWORD.get_secret_value())

return self

Expand Down
8 changes: 8 additions & 0 deletions backend/app/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
from loguru import logger

from app.api.router import api_router
from app.core.cache import cache
from app.core.config import settings
from app.core.db import db

Expand Down Expand Up @@ -40,6 +41,13 @@ async def lifespan(_: FastAPI) -> AsyncGenerator:
logger.error(f"Error creating first superuser: {e}")
raise e

logger.info("Initializing cache client")
try:
await cache.create_client()
except Exception as e:
logger.error(f"Error creating cache client: {e}")
raise

yield
logger.info("Closing database connection pool")
try:
Expand Down
1 change: 1 addition & 0 deletions backend/pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ dependencies = [
"python-multipart==0.0.20",
"uvicorn==0.34.0",
"uvloop==0.21.0",
"valkey==6.0.2",
]

[dependency-groups]
Expand Down
1 change: 1 addition & 0 deletions backend/tests/api/routes/test_health_route.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,4 @@ async def test_health(test_client):
assert result.status_code == 200
assert result.json()["server"] == "healthy"
assert result.json()["db"] == "healthy"
assert result.json()["cache"] == "healthy"
12 changes: 12 additions & 0 deletions backend/tests/conftest.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import pytest
from httpx import ASGITransport, AsyncClient

from app.core.cache import cache
from app.core.config import settings
from app.core.db import db
from app.main import app
Expand Down Expand Up @@ -33,6 +34,17 @@ async def test_db():
await db.close_pool()


@pytest.fixture(autouse=True)
async def test_cache():
await cache.create_client()
yield cache
if cache.client:
await cache.create_client()

await cache.client.flushall() # type: ignore
await cache.close_client()


@pytest.fixture
async def test_client():
async with AsyncClient(
Expand Down
4 changes: 4 additions & 0 deletions backend/tests/core/test_config.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ def test_temperature(temperature):
POSTGRES_HOST="some_host",
POSTGRES_USER="pg",
POSTGRES_PASSWORD=SecretStr("pgpassword"),
VALKEY_HOST="valkey",
VALKEY_PASSWORD=SecretStr("valkeypassword"),
OPENAI_API_KEY=SecretStr("some_key"),
TEMPERATURE=temperature,
)
Expand All @@ -30,6 +32,8 @@ def test_invalid_temperature(temperature):
POSTGRES_HOST="some_host",
POSTGRES_USER="pg",
POSTGRES_PASSWORD=SecretStr("pgpassword"),
VALKEY_HOST="valkey",
VALKEY_PASSWORD=SecretStr("valkeypassword"),
OPENAI_API_KEY=SecretStr("some_key"),
TEMPERATURE=temperature,
)
11 changes: 11 additions & 0 deletions backend/uv.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

11 changes: 11 additions & 0 deletions docker-compose.ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ services:
container_name: backend
depends_on:
- db
- valkey
ports:
- "8000:8000"
environment:
Expand All @@ -16,6 +17,8 @@ services:
- POSTGRES_USER=postgres
- POSTGRES_PASSWORD=test_password
- POSTGRES_DB=scan
- VALKEY_HOST=127.0.0.1
- VALKEY_PASSWORD=test_password
- OPENAI_API_KEY=someKey
db:
image: postgres:17-alpine
Expand All @@ -30,6 +33,14 @@ services:
volumes:
- db-data:/var/lib/postgresql/data

valkey:
image: valkey/valkey:8-alpine
expose:
- 6379
ports:
- 6379:6379
command: valkey-server --requirepass test_password

volumes:
db-data:

Expand Down
9 changes: 9 additions & 0 deletions docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ services:
container_name: backend
depends_on:
- db
- valkey
ports:
- "8000:8000"
env_file:
Expand All @@ -25,6 +26,14 @@ services:
volumes:
- db-data:/var/lib/postgresql/data

valkey:
image: valkey/valkey:8-alpine
expose:
- 6379
ports:
- 6379:6379
command: valkey-server --requirepass test_password

volumes:
db-data:

Expand Down
6 changes: 3 additions & 3 deletions justfile
Original file line number Diff line number Diff line change
Expand Up @@ -61,15 +61,15 @@
cd frontend && \
npm run start

@fronend-line:
@frontend-lint:
cd frontend && \
npm run lint

@docker-up:
docker compose up
docker compose up --build

@docker-up-backend-dev:
docker compose up db
docker compose up db valkey

@docker-up-detached:
docker compose up -d
Expand Down
Loading