diff --git a/backend/app/core/audit_middleware.py b/backend/app/core/audit_middleware.py index fa18f36e..b7c894af 100644 --- a/backend/app/core/audit_middleware.py +++ b/backend/app/core/audit_middleware.py @@ -37,6 +37,7 @@ import re from collections.abc import Awaitable, Callable from fastapi import Request +from fastapi.concurrency import run_in_threadpool from fastapi.responses import Response from sqlalchemy import text @@ -171,10 +172,16 @@ async def audit_log_middleware( # но если мы тут — нечего атрибутировать, аудит пропускаем. return response - # Write после готового response — off the critical path. Любой сбой внутри - # write_audit_row проглатывается там же; здесь дополнительная страховка. + # Write после готового response — off the critical path для САМОГО клиента + # (response уже отдан выше), но БЛОКИРУЕТ event loop пока идёт sync DB + # round-trip (SessionLocal/execute/commit). Это останавливает ВСЕ другие + # запросы на loop'е до завершения insert'а — а при исчерпании connection + # pool checkout ждёт `pool_timeout=30s` НА loop'е, замораживая весь API + # включая /health (#1202). Выполняем write в threadpool через + # `run_in_threadpool` — sync IO в worker-потоке, event loop свободен. try: - write_audit_row( + await run_in_threadpool( + write_audit_row, username=username, action=action, method=request.method,