fix(audit): run_in_threadpool для write_audit_row в async middleware (#1202) #1252

Merged
bot-backend merged 1 commit from fix/audit-middleware-run-in-threadpool into main 2026-06-13 05:24:40 +00:00
Collaborator

Summary

Closes #1202 (P2 concurrency). write_audit_row (sync SessionLocal/execute/commit) выполняется напрямую в async audit_log_middleware → блокирует event loop на каждом аудируемом запросе.

Bug

audit_log_middleware зарегистрирован через app.middleware("http") (main.py:92) → async dispatch на event loop. После await call_next(...) response уже отдан, но write_audit_row (sync SessionLocal() + execute(INSERT) + commit()) блокирует event loop на полный DB round-trip.

Под нагрузкой:

  • На активной демо-сессии десятки sensitive-запросов в минуту: каждый poll GET /forecast (раз в 2 сек), каждый /analyze, /export, /insight-write.
  • При исчерпании connection pool (default 5+10, sync analyze держит сессию ~15с inline-wait) checkout ждёт pool_timeout=30с НА loop'е → весь API замирает, включая /health.

Комментарий в коде «off the critical path» закрывал только латентность для самого клиента, но НЕ блокировку loop'а для других запросов.

Fix

await run_in_threadpool(
    write_audit_row,
    username=username, action=action, ...
)

Sync IO в worker-потоке (FastAPI threadpool), event loop свободен. Никаких изменений в самой write_audit_row — остаётся sync def с собственным try/except + rollback.

Test plan

  • pytest tests/test_audit_middleware.py16/16 passed (поведение для caller'а не изменилось — await over run_in_threadpool синхронно с точки зрения dispatch).
  • ruff check → clean.

Closes #1202

## Summary Closes #1202 (P2 concurrency). `write_audit_row` (sync `SessionLocal`/execute/commit) выполняется напрямую в `async audit_log_middleware` → блокирует event loop на каждом аудируемом запросе. ## Bug `audit_log_middleware` зарегистрирован через `app.middleware("http")` (`main.py:92`) → async dispatch на event loop. После `await call_next(...)` response уже отдан, но `write_audit_row` (sync `SessionLocal()` + `execute(INSERT)` + `commit()`) **блокирует event loop** на полный DB round-trip. Под нагрузкой: - На активной демо-сессии десятки sensitive-запросов в минуту: каждый poll `GET /forecast` (раз в 2 сек), каждый /analyze, /export, /insight-write. - При исчерпании connection pool (default 5+10, sync analyze держит сессию ~15с inline-wait) checkout ждёт `pool_timeout=30с` НА loop'е → весь API замирает, включая `/health`. Комментарий в коде «off the critical path» закрывал только латентность для **самого** клиента, но НЕ блокировку loop'а для других запросов. ## Fix ```python await run_in_threadpool( write_audit_row, username=username, action=action, ... ) ``` Sync IO в worker-потоке (FastAPI threadpool), event loop свободен. Никаких изменений в самой `write_audit_row` — остаётся sync def с собственным try/except + rollback. ## Test plan - [x] `pytest tests/test_audit_middleware.py` → **16/16 passed** (поведение для caller'а не изменилось — `await` over `run_in_threadpool` синхронно с точки зрения dispatch). - [x] `ruff check` → clean. Closes #1202
bot-backend added 1 commit 2026-06-13 05:15:25 +00:00
fix(audit): run_in_threadpool для write_audit_row в async middleware (#1202)
All checks were successful
CI / changes (push) Successful in 6s
CI / changes (pull_request) Successful in 5s
CI / frontend-tests (push) Has been skipped
CI / frontend-tests (pull_request) Has been skipped
CI / backend-tests (push) Successful in 6m28s
CI / backend-tests (pull_request) Successful in 6m21s
65c9d145b9
audit_log_middleware зарегистрирован через app.middleware("http") (main.py:92)
→ async dispatch на event loop. После `await call_next(...)` response уже
отдан, но БЛОКИРУЕТ event loop пока идёт sync DB round-trip (SessionLocal/
execute/commit). Это останавливает ВСЕ другие запросы на loop'е до завершения
INSERT'а. При исчерпании connection pool (default 5+10, sync analyze держит
сессию ~15с inline-wait) checkout ждёт pool_timeout=30с НА loop'е → весь API,
включая /health, замирает.

Каждый аудируемый запрос: analyze, КАЖДЫЙ 2с-poll GET /forecast, export,
insight-write. На активной демо-сессии — десятки таких в минуту.

Fix: write_audit_row выполняется через fastapi.concurrency.run_in_threadpool
— sync IO в worker-потоке, event loop свободен для других запросов. Никаких
изменений в самой write_audit_row — она остаётся sync def с собственным
try/except + rollback.

16/16 audit-middleware тестов зелёные (поведение для caller'а не изменилось).

Closes #1202
bot-backend merged commit 323e399593 into main 2026-06-13 05:24:40 +00:00
bot-backend deleted branch fix/audit-middleware-run-in-threadpool 2026-06-13 05:24:41 +00:00
Sign in to join this conversation.
No reviewers
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference: lekss361/gendesign#1252
No description provided.