feat(01-02): set up PostgreSQL with Docker and async session factory
- Add docker-compose.yml with PostgreSQL 16 container (port 5433) - Create async database session factory with connection pooling - Configure SQLAlchemy 2.0 DeclarativeBase for models - Update .env.example with correct database URL Connection pool settings from research: pool_size=10, max_overflow=20, pool_recycle=1800 (30 min), pool_pre_ping=True for validation.
This commit is contained in:
parent
519333e598
commit
fbcd2bbb8e
5 changed files with 95 additions and 1 deletions
|
|
@ -1,6 +1,6 @@
|
||||||
# Database Configuration
|
# Database Configuration
|
||||||
# PostgreSQL connection string using asyncpg driver
|
# PostgreSQL connection string using asyncpg driver
|
||||||
DATABASE_URL=postgresql+asyncpg://debate:debate@localhost:5432/debate
|
DATABASE_URL=postgresql+asyncpg://debate:debate_dev@localhost:5433/debate
|
||||||
|
|
||||||
# Security
|
# Security
|
||||||
# Generate with: openssl rand -hex 32
|
# Generate with: openssl rand -hex 32
|
||||||
|
|
|
||||||
6
backend/app/db/__init__.py
Normal file
6
backend/app/db/__init__.py
Normal file
|
|
@ -0,0 +1,6 @@
|
||||||
|
"""Database package - exports key database components."""
|
||||||
|
|
||||||
|
from backend.app.db.base import Base
|
||||||
|
from backend.app.db.session import async_session_maker, engine, get_db
|
||||||
|
|
||||||
|
__all__ = ["Base", "engine", "async_session_maker", "get_db"]
|
||||||
21
backend/app/db/base.py
Normal file
21
backend/app/db/base.py
Normal file
|
|
@ -0,0 +1,21 @@
|
||||||
|
"""SQLAlchemy 2.0 declarative base for all models."""
|
||||||
|
|
||||||
|
from sqlalchemy.orm import DeclarativeBase
|
||||||
|
|
||||||
|
|
||||||
|
class Base(DeclarativeBase):
|
||||||
|
"""Base class for all SQLAlchemy models.
|
||||||
|
|
||||||
|
All models should inherit from this class for Alembic autogenerate
|
||||||
|
to discover them.
|
||||||
|
"""
|
||||||
|
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
# Import all models here for Alembic autogenerate to discover them
|
||||||
|
# This ensures all models are registered with Base.metadata
|
||||||
|
# Note: Models are imported at the bottom to avoid circular imports
|
||||||
|
def import_models() -> None:
|
||||||
|
"""Import all models to register them with Base.metadata."""
|
||||||
|
from backend.app.db.models import build # noqa: F401
|
||||||
45
backend/app/db/session.py
Normal file
45
backend/app/db/session.py
Normal file
|
|
@ -0,0 +1,45 @@
|
||||||
|
"""Async database session management with connection pooling."""
|
||||||
|
|
||||||
|
from collections.abc import AsyncGenerator
|
||||||
|
|
||||||
|
from sqlalchemy.ext.asyncio import (
|
||||||
|
AsyncSession,
|
||||||
|
async_sessionmaker,
|
||||||
|
create_async_engine,
|
||||||
|
)
|
||||||
|
|
||||||
|
from backend.app.core.config import settings
|
||||||
|
|
||||||
|
# Create async engine with connection pooling settings from research
|
||||||
|
# See: 01-RESEARCH.md Pattern 1: Async Database Session Management
|
||||||
|
engine = create_async_engine(
|
||||||
|
settings.database_url,
|
||||||
|
pool_size=10,
|
||||||
|
max_overflow=20,
|
||||||
|
pool_timeout=30,
|
||||||
|
pool_recycle=1800, # 30 minutes - refresh connections
|
||||||
|
pool_pre_ping=True, # Validate connections before use
|
||||||
|
echo=False, # Set True for SQL logging in development
|
||||||
|
)
|
||||||
|
|
||||||
|
# Session factory for creating async sessions
|
||||||
|
async_session_maker = async_sessionmaker(
|
||||||
|
engine,
|
||||||
|
class_=AsyncSession,
|
||||||
|
expire_on_commit=False,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
async def get_db() -> AsyncGenerator[AsyncSession, None]:
|
||||||
|
"""FastAPI dependency for database sessions.
|
||||||
|
|
||||||
|
Yields an async database session and ensures proper cleanup.
|
||||||
|
|
||||||
|
Usage:
|
||||||
|
@app.get("/items")
|
||||||
|
async def get_items(db: AsyncSession = Depends(get_db)):
|
||||||
|
# Use db session here
|
||||||
|
pass
|
||||||
|
"""
|
||||||
|
async with async_session_maker() as session:
|
||||||
|
yield session
|
||||||
22
docker-compose.yml
Normal file
22
docker-compose.yml
Normal file
|
|
@ -0,0 +1,22 @@
|
||||||
|
services:
|
||||||
|
postgres:
|
||||||
|
image: postgres:16-alpine
|
||||||
|
container_name: debate-postgres
|
||||||
|
environment:
|
||||||
|
POSTGRES_USER: debate
|
||||||
|
POSTGRES_PASSWORD: debate_dev
|
||||||
|
POSTGRES_DB: debate
|
||||||
|
ports:
|
||||||
|
- "5433:5432"
|
||||||
|
volumes:
|
||||||
|
- postgres_data:/var/lib/postgresql/data
|
||||||
|
healthcheck:
|
||||||
|
test: ["CMD-SHELL", "pg_isready -U debate -d debate"]
|
||||||
|
interval: 10s
|
||||||
|
timeout: 5s
|
||||||
|
retries: 5
|
||||||
|
start_period: 10s
|
||||||
|
restart: unless-stopped
|
||||||
|
|
||||||
|
volumes:
|
||||||
|
postgres_data:
|
||||||
Loading…
Add table
Reference in a new issue