diff --git a/backend/python_service/db.py b/backend/python_service/db.py index 26f5a71..36feba9 100644 --- a/backend/python_service/db.py +++ b/backend/python_service/db.py @@ -3,6 +3,7 @@ Database connection module for Python service Uses asyncpg for async PostgreSQL connections """ import os +import asyncio import asyncpg from typing import Optional from dotenv import load_dotenv @@ -12,9 +13,12 @@ load_dotenv() # Database connection pool _pool: Optional[asyncpg.Pool] = None +# Connection timeout in seconds (reduced from default 60s to fail faster) +CONNECTION_TIMEOUT = 10 + async def get_pool() -> asyncpg.Pool: - """Get or create database connection pool""" + """Get or create database connection pool with timeout""" global _pool if _pool is None: @@ -47,12 +51,29 @@ async def get_pool() -> asyncpg.Pool: ' - OR DATABASE_URL' ) - _pool = await asyncpg.create_pool( - **config, - min_size=5, - max_size=20, - command_timeout=60 - ) + # Create pool with timeout to prevent hanging + # Use min_size=1 to speed up initial connection (pool will grow as needed) + try: + _pool = await asyncio.wait_for( + asyncpg.create_pool( + **config, + min_size=1, # Start with 1 connection, pool grows as needed + max_size=20, + command_timeout=60, + timeout=CONNECTION_TIMEOUT # Connection timeout per connection + ), + timeout=CONNECTION_TIMEOUT # Overall timeout for pool creation + ) + except asyncio.TimeoutError: + raise ConnectionError( + f'Database connection timeout after {CONNECTION_TIMEOUT}s. ' + f'Check if database is reachable at {config.get("host", "unknown")}:{config.get("port", "unknown")}' + ) + except Exception as e: + raise ConnectionError( + f'Failed to create database connection pool: {str(e)}. ' + f'Check database configuration and network connectivity.' + ) return _pool diff --git a/backend/python_service/main.py b/backend/python_service/main.py index 43b46d7..ee08f16 100644 --- a/backend/python_service/main.py +++ b/backend/python_service/main.py @@ -47,7 +47,18 @@ class OptionsFlowResponse(BaseModel): @app.on_event("startup") async def startup(): """Initialize database pool on startup""" - await get_pool() + try: + logger.info("Initializing database connection pool...") + pool = await get_pool() + # Test the connection with a quick query + async with pool.acquire() as conn: + await conn.fetchval("SELECT 1") + logger.info("✅ Database connection pool initialized successfully") + except Exception as e: + logger.error(f"⚠️ Failed to initialize database pool on startup: {str(e)}") + logger.warning("Service will start but database operations may fail. Connection will be retried on first request.") + # Don't raise - allow service to start and retry on first request + # This makes the service more resilient to temporary DB issues @app.on_event("shutdown")