RAGChatbot / db /postgres_client.py
Shurem's picture
Add application file
9c13f8a
Raw
History Blame Contribute Delete
3.04 kB
from sqlalchemy.ext.asyncio import create_async_engine, AsyncSession, AsyncEngine
from sqlalchemy.orm import sessionmaker
from typing import AsyncGenerator, Optional
import os
from contextlib import asynccontextmanager
# Lazy-initialized engine and session maker
_engine: Optional[AsyncEngine] = None
_AsyncSessionLocal: Optional[sessionmaker] = None
def _get_database_url() -> str:
"""Get and format the database URL for async connection"""
url = os.getenv("DATABASE_URL", "postgresql://user:password@localhost/rag_chatbot")
# Convert to asyncpg format if needed
if url.startswith("postgresql://"):
url = url.replace("postgresql://", "postgresql+asyncpg://", 1)
elif url.startswith("postgres://"):
url = url.replace("postgres://", "postgresql+asyncpg://", 1)
# Remove sslmode parameter (asyncpg uses 'ssl' instead)
# Also remove channel_binding as it's not supported by asyncpg
if "?" in url:
base_url, params = url.split("?", 1)
param_list = params.split("&")
filtered_params = []
for param in param_list:
# Skip sslmode and channel_binding - asyncpg handles SSL differently
if not param.startswith("sslmode=") and not param.startswith("channel_binding="):
filtered_params.append(param)
if filtered_params:
url = base_url + "?" + "&".join(filtered_params)
else:
url = base_url
return url
def get_engine() -> AsyncEngine:
"""Get or create the async engine (lazy initialization)"""
global _engine
if _engine is None:
database_url = _get_database_url()
# For Neon Postgres and other cloud providers, enable SSL
_engine = create_async_engine(
database_url,
pool_size=5,
max_overflow=10,
pool_pre_ping=True,
pool_recycle=300,
connect_args={"ssl": True} # Enable SSL for asyncpg
)
return _engine
def get_session_maker() -> sessionmaker:
"""Get or create the session maker (lazy initialization)"""
global _AsyncSessionLocal
if _AsyncSessionLocal is None:
_AsyncSessionLocal = sessionmaker(
get_engine(),
class_=AsyncSession,
expire_on_commit=False
)
return _AsyncSessionLocal
async def get_db_session() -> AsyncGenerator[AsyncSession, None]:
"""
Get database session for dependency injection
"""
session_maker = get_session_maker()
async with session_maker() as session:
yield session
await session.close()
async def init_db():
"""
Initialize the database by creating all tables
"""
from .models import Base
engine = get_engine()
async with engine.begin() as conn:
# Create all tables
await conn.run_sync(Base.metadata.create_all)
async def close_db():
"""
Close the database engine
"""
global _engine
if _engine is not None:
await _engine.dispose()
_engine = None