Устранение проблем с утечками соединений в FastAPI с помощью SQLAlchemy и PostgreSQLPython

Программы на Python
Ответить
Anonymous
 Устранение проблем с утечками соединений в FastAPI с помощью SQLAlchemy и PostgreSQL

Сообщение Anonymous »

Все!
В настоящее время я столкнулся с проблемой утечки соединения в моем проекте FastAPI. Соединения не закрываются должным образом и остаются в состоянии ожидания. Несмотря на мои попытки тщательно управлять подключениями, количество простаивающих подключений продолжает расти, что в конечном итоге приводит к проблемам при достижении максимального предела подключений.
Подробности проекта:
Framework: FastAPI (v0.104.0)
Веб-сервер: Uvicorn (v0.23.0) с 24 работниками
Библиотека базы данных: SQLAlchemy (v2.0.21)
База данных: PostgreSQL (через Docker)
Конфигурация PostgreSQL в docker-compose.yml:

Код: Выделить всё

postgres:
image: postgres:16
container_name: postgres_db
ports:
volumes:
- postgres_data:/var/lib/postgresql/data
environment:
POSTGRES_USER: ${POSTGRES_USER}
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
POSTGRES_DB: ${POSTGRES_DB}
healthcheck:
test: ["CMD-SHELL", "pg_isready -U ${POSTGRES_USER} -d ${POSTGRES_DB}"]
interval: 300s
timeout: 5s
retries: 3

Конфигурация базы данных:

Код: Выделить всё

DATABASE_URL = os.environ.get("DATABASE_URL")

engine = create_engine(
DATABASE_URL,
pool_size=24,  # Number of connections in the pool
max_overflow=12,  # Additional connections allowed beyond the pool size
pool_timeout=30,  # Wait time for a connection before timeout
pool_recycle=1800,  # Recycle connections after 1800 seconds (30 minutes)
pool_pre_ping=True,  # Ensure connections are valid before using
)
Менеджер базы данных:

Код: Выделить всё

class DatabaseManager:
_instance = None

def __new__(cls):
if not cls._instance:
cls._instance = super(DatabaseManager, cls).__new__(cls)
cls._instance.engine = engine
cls._instance.session_maker = scoped_session(
sessionmaker(autocommit=False, autoflush=False, bind=cls._instance.engine)
)
return cls._instance

def get_session(self):
session = self._instance.session_maker()
try:
yield session
finally:
session.close()
self._instance.session_maker.remove()

маршрутизатор входа:

Код: Выделить всё

@user_router.post("/login")
def login_user(
request: Request,
login_data: LoginRequest,
usermanager=Depends(UserManager),
db_session: Session = Depends(DatabaseManager().get_session),
):
return usermanager.login_user(request, login_data, db_session)

логика входа:

Код: Выделить всё

def login_user(self, request, login_data, db_session: Session):
try:
user = self.authenticate_user(request, login_data, db_session)
if not user:
raise HTTPException(status_code=401, detail="Incorrect username or password")

if user.is_active:
tokens = self.create_tokens_for_user(user)
return tokens
else:
raise HTTPException(status_code=401, detail="User is inactive")

except HTTPException as e:
self.log_exception("login", e.status_code, e.detail, traceback.format_exc())
raise e

except Exception as e:
try:
self.log_exception("login retry", 500, str(e), traceback.format_exc())
db_session = next(DatabaseManager().get_session())
user = self.authenticate_user(request, login_data, db_session)
if user and user.is_active:
return self.create_tokens_for_user(user)
finally:
db_session.close()

raise HTTPException(status_code=500, detail="An error occurred while logging in")

Увеличение количества простаивающих соединений:
Я отслеживаю соединения PostgreSQL с помощью следующего запроса внутри контейнера:

Код: Выделить всё

SELECT state, count(*)
FROM pg_stat_activity
GROUP BY state;

выход:

Код: Выделить всё

state  | count
-------+-------
|     5
active |     1
idle   |    73

и количество простаивающих соединений растет!
Хотя я ожидаю максимум 24 простаивающих соединения (из-за размера пула), это число продолжает расти и достигает максимума лимит подключений.
Даже до того, как количество простаивающих подключений достигнет максимума,
иногда я получаю эту ошибку

Код: Выделить всё

sqlalchemy.exc.InvalidRequestError: This session is provisioning a new connection;
concurrent operations are not permitted
и когда он достигнет максимума, я получу следующее:

Код: Выделить всё

psycopg2.OperationalError: connection to server at "postgres", port 5432 failed:
FATAL:  sorry, too many clients already

Замеченное поведение:
Даже когда количество простаивающих соединений достигает максимума, логика повтора в функции login_user, похоже, работает правильно.
/>Я гарантирую, что все сеансы закрываются правильно (например, с помощью наконец), если я не использую внедрение зависимостей FastAPI. Кроме того, я использую сеанс с ограниченной областью действия, поэтому для каждого потока поддерживается один сеанс. Однако проблема остается.
Я застрял в этом вопросе уже неделю и перепробовал все, что мог придумать, в том числе обратился за помощью ко всем инструментам искусственного интеллекта, которые смог найти. К сожалению, ничего не помогло. Вот почему я пришел сюда, надеясь на помощь неискусственного интеллекта. Будем очень признательны за любые советы или идеи по решению этой проблемы. Спасибо!

Подробнее здесь: https://stackoverflow.com/questions/793 ... postgresql
Ответить

Быстрый ответ

Изменение регистра текста: 
Смайлики
:) :( :oops: :roll: :wink: :muza: :clever: :sorry: :angel: :read: *x)
Ещё смайлики…
   
К этому ответу прикреплено по крайней мере одно вложение.

Если вы не хотите добавлять вложения, оставьте поля пустыми.

Максимально разрешённый размер вложения: 15 МБ.

Вернуться в «Python»