# Итоги: 022 — остаток п. 9: синхронная БД в async-коде Источник — ревью `docs/reviews/2026-09-28-1243-codebase-review.md`, п. 9 (частично закрыт в 019). ## Сделано - Разведка по AST нашла 14 async-функций с прямыми синхронными обращениями к SQLite (в ревью были названы 2) — все переведены на `asyncio.to_thread`; исключение — `lifespan` (выполняется до приёма запросов). - `jobs.start_jobs` — `async`: БД и события `job.created` — синхронная `_create_jobs` в потоке, `create_task` — в event loop; все вызовы с `await`. - `ops._conn` — чтение устройства и расшифровка пароля в потоке во всех операциях (опрос, бэкап, канал, обновления ROS/FW); `refresh_many` — список устройств в потоке. - Группировка нескольких запросов одного ответа в один `to_thread`: `backups.search`, `backups_page`, `device_create`. - API (`refresh_device`, `put_channel`, `refresh_all`, `list_backups`, `batch`, `batch_channel`, запуск задач) и UI (`refresh`, `batch`, `device_action`, `device_create`, `backups_page`) — без синхронной БД в event loop. - Тест-линтер `test_no_sync_db_calls_in_async_functions`: AST-обход `app/` — в `async def` нет прямых вызовов функций с `session_scope` (напрямую или через одну ступень); ссылки в `asyncio.to_thread(fn, …)` и вложенные `def` не считаются; сообщение называет файл, функцию и вызов. ## Проверено - `pytest`: 29 из 29. - Мутация: прямой `get_device` в `refresh_device` → линтер падает с точным местом; файл восстановлен. - Стенд (8001): бэкап и групповая смена канала для временного устройства → 202, задачи `failed` (`ConnectTimeout`, ожидаемо), актор всех событий задач — `api` (контекст в потоке сохраняется); во время 4-секундного `refresh` параллельные `GET /devices` — 0,011 с, `GET /backups` — 0,125 с. - Боевые данные: группы 3/3, устройства 14/14; добавлены только данные проверки (2 задачи, запись неудачного бэкапа, события); временное устройство удалено. ## Оговорки - Линтер видит одну ступень транзитивности: функция, обращающаяся к БД через две и более промежуточных, не будет обнаружена. - Ручная проверка UI пользователем на момент коммита не подтверждена.