From a93526c12ca212caca3e945ce56ff1d8d474f32f Mon Sep 17 00:00:00 2001 From: Artem Reznichenko Date: Wed, 11 Feb 2026 11:05:41 +0300 Subject: [PATCH] fix: db --- bot/database.py | 54 +++++++++++++++++++++++++++++++++------- bot/handlers/__init__.py | 1 - 2 files changed, 45 insertions(+), 10 deletions(-) delete mode 100644 bot/handlers/__init__.py diff --git a/bot/database.py b/bot/database.py index 74ea6ce..6fc3c85 100644 --- a/bot/database.py +++ b/bot/database.py @@ -1,9 +1,11 @@ """Database models and session management.""" +import asyncio from typing import AsyncGenerator from sqlalchemy import create_engine, BigInteger, String, Integer, Boolean, ForeignKey, Column from sqlalchemy.orm import declarative_base, sessionmaker, relationship, Session from sqlalchemy.schema import UniqueConstraint from sqlalchemy.ext.asyncio import create_async_engine, AsyncSession, async_sessionmaker +import socket from bot.config import Config from bot.logger import get_logger @@ -68,7 +70,23 @@ class UserChat(Base): # Database engine and session (async) # Convert postgresql:// to postgresql+asyncpg:// async_database_url = Config.DATABASE_URL.replace("postgresql://", "postgresql+asyncpg://", 1) -async_engine = create_async_engine(async_database_url, echo=False) + +# Configure connection pool with better timeout settings +async_engine = create_async_engine( + async_database_url, + echo=False, + pool_pre_ping=True, # Verify connections before using them + pool_size=5, # Number of connections to maintain + max_overflow=10, # Maximum overflow connections + pool_recycle=3600, # Recycle connections after 1 hour + pool_timeout=30, # Timeout for getting connection from pool + connect_args={ + "server_settings": { + "application_name": "bdbot", + }, + "command_timeout": 60, # Command timeout + } +) AsyncSessionLocal = async_sessionmaker(async_engine, class_=AsyncSession, expire_on_commit=False) @@ -82,15 +100,33 @@ async def get_db() -> AsyncGenerator[AsyncSession, None]: async def init_db() -> None: - """Initialize database - create all tables.""" + """Initialize database - create all tables with retry logic.""" logger.info("Initializing database...") - try: - async with async_engine.begin() as conn: - await conn.run_sync(Base.metadata.create_all) - logger.info("Database initialized successfully") - except Exception as e: - logger.error(f"Error initializing database: {e}", exc_info=True) - raise + + max_retries = 5 + base_delay = 2 # Start with 2 seconds + + for attempt in range(max_retries): + try: + async with async_engine.begin() as conn: + await conn.run_sync(Base.metadata.create_all) + logger.info("Database initialized successfully") + return + except (socket.gaierror, OSError) as e: + # DNS resolution or network errors + if attempt < max_retries - 1: + delay = base_delay * (2 ** attempt) # Exponential backoff: 2, 4, 8, 16, 32 + logger.warning( + f"Database connection failed (attempt {attempt + 1}/{max_retries}): {e}. " + f"Retrying in {delay} seconds..." + ) + await asyncio.sleep(delay) + else: + logger.error(f"Failed to connect to database after {max_retries} attempts: {e}", exc_info=True) + raise + except Exception as e: + logger.error(f"Error initializing database: {e}", exc_info=True) + raise def get_db_session(): diff --git a/bot/handlers/__init__.py b/bot/handlers/__init__.py deleted file mode 100644 index 0f6f20e..0000000 --- a/bot/handlers/__init__.py +++ /dev/null @@ -1 +0,0 @@ -# Handlers package