Files
api-copp/bd/__init__.py
jze9 94e78a9e77 fix: единый singleton engine в bd, убраны 17 локальных make_engine()
- bd/__init__.py: добавлены get_engine() и get_session() — один SQLAlchemy engine на весь процесс
- make_engine(): pool_size=5, max_overflow=5, pool_timeout=30, pool_recycle=1800
- Все 17 route-файлов: убраны локальные get_session()/make_engine(), импорт из bd
- transfer_crud.py: _get_session() → get_session() из bd
- init_data_base.py: get_engine() вместо make_engine()
- Результат: 78 idle соединений → 1
2026-05-18 11:13:28 +00:00

96 lines
3.2 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Database configuration package
from pydantic import BaseModel
import re
import os
import urllib.parse
class Settings(BaseModel):
DB_HOST: str = os.getenv("DB_HOST", "postgres")
DB_PORT: int = int(os.getenv("DB_PORT", "5432"))
DB_USER: str = os.getenv("DB_USER", "postgres")
DB_PASS: str = os.getenv("DB_PASS", "")
DB_NAME: str = os.getenv("DB_NAME", "profi")
@classmethod
def validate_db_port(cls, v):
# Проверка и преобразование DB_PORT
if isinstance(v, str):
# Используем регулярное выражение для извлечения целого числа
match = re.search(r'\d+', v)
if match:
return int(match.group())
else:
raise ValueError(f"Invalid DB_PORT value: {v}")
return v
@property
def DATABASE_URL_asyncpg(self) -> str:
pwd = urllib.parse.quote_plus(str(self.DB_PASS))
return f"postgresql+asyncpg://{self.DB_USER}:{pwd}@{self.DB_HOST}:{self.DB_PORT}/{self.DB_NAME}"
@property
def DATABASE_URL_syncpg(self) -> str:
pwd = urllib.parse.quote_plus(str(self.DB_PASS))
return f"postgresql+pg8000://{self.DB_USER}:{pwd}@{self.DB_HOST}:{self.DB_PORT}/{self.DB_NAME}"
@property
def PG8000_CONNECT_ARGS(self) -> dict:
"""connect_args для pg8000 — отключаем SSL (сервер его не поддерживает)."""
return {"ssl_context": None}
def make_engine():
"""Создаёт SQLAlchemy engine с правильными параметрами для pg8000."""
from sqlalchemy import create_engine
s = Settings()
return create_engine(
s.DATABASE_URL_syncpg,
connect_args=s.PG8000_CONNECT_ARGS,
future=True,
pool_pre_ping=True,
pool_size=5,
max_overflow=5,
pool_timeout=30,
pool_recycle=1800,
)
def wait_for_db(retries: int = 15, delay: float = 2.0) -> None:
"""Ждёт, пока PostgreSQL станет доступен. Вызывается при старте приложения."""
import time
import logging
from sqlalchemy import text
logger = logging.getLogger("bd")
engine = make_engine()
for attempt in range(1, retries + 1):
try:
with engine.connect() as conn:
conn.execute(text("SELECT 1"))
logger.info("PostgreSQL is ready.")
return
except Exception as exc:
logger.warning("DB not ready (attempt %d/%d): %s", attempt, retries, exc)
if attempt < retries:
time.sleep(delay)
raise RuntimeError("PostgreSQL did not become ready in time.")
# ── Singleton engine + session factory (один движок на весь процесс) ───────
_engine = None
_SessionFactory = None
def get_engine():
global _engine
if _engine is None:
_engine = make_engine()
return _engine
def get_session():
global _SessionFactory
if _SessionFactory is None:
from sqlalchemy.orm import sessionmaker
_SessionFactory = sessionmaker(bind=get_engine(), future=True)
return _SessionFactory