Initialize git, add Apache-2.0 LICENSE, .gitattributes (LF line endings), AGENTS.md (entry points, stack, discovery order, baseline checks), RUNBOOK.md (dev boot, prod deploy with overlay, ingestion, failures, rollback, scaling notes), .env.prod.example with rotated credential placeholders, and dev-only warnings on .env.example. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
67 lines
1.5 KiB
Python
67 lines
1.5 KiB
Python
"""SQLAlchemy engine and session factory."""
|
|
|
|
from __future__ import annotations
|
|
|
|
from collections.abc import Iterator
|
|
from contextlib import contextmanager
|
|
|
|
from sqlalchemy import create_engine
|
|
from sqlalchemy.engine import Engine
|
|
from sqlalchemy.orm import Session, sessionmaker
|
|
|
|
from app.config import settings
|
|
|
|
_engine: Engine | None = None
|
|
_SessionFactory: sessionmaker[Session] | None = None
|
|
|
|
|
|
def get_engine() -> Engine:
|
|
global _engine
|
|
if _engine is None:
|
|
_engine = create_engine(
|
|
settings.database_url,
|
|
pool_pre_ping=True,
|
|
pool_size=10,
|
|
max_overflow=20,
|
|
future=True,
|
|
)
|
|
return _engine
|
|
|
|
|
|
def get_session_factory() -> sessionmaker[Session]:
|
|
global _SessionFactory
|
|
if _SessionFactory is None:
|
|
_SessionFactory = sessionmaker(
|
|
bind=get_engine(),
|
|
autoflush=False,
|
|
autocommit=False,
|
|
expire_on_commit=False,
|
|
future=True,
|
|
)
|
|
return _SessionFactory
|
|
|
|
|
|
@contextmanager
|
|
def session_scope() -> Iterator[Session]:
|
|
"""Provide a transactional scope: commits on success, rolls back on error."""
|
|
factory = get_session_factory()
|
|
session = factory()
|
|
try:
|
|
yield session
|
|
session.commit()
|
|
except Exception:
|
|
session.rollback()
|
|
raise
|
|
finally:
|
|
session.close()
|
|
|
|
|
|
def get_db() -> Iterator[Session]:
|
|
"""FastAPI dependency."""
|
|
factory = get_session_factory()
|
|
session = factory()
|
|
try:
|
|
yield session
|
|
finally:
|
|
session.close()
|