Updated status in spec.md, plan.md, tasks.md, research.md, data-model.md, ux_reference.md, quickstart.md. All 72 tasks completed. 34/34 tests passing.
7.8 KiB
Data Model: Async Backend Refactoring
Feature: 032-translate-requests-httpx
Date: 2026-06-04
Status: ✅ Implemented
Overview
Рефакторинг не добавляет новых бизнес-таблиц (SQLAlchemy models) и не меняет публичные request/response Pydantic-схемы. Добавляются транспортные/runtime-сущности (SupersetClientRegistry, run_blocking, bounded executors) и расширяется конфигурация окружений (EnvironmentConfig) и приложения (AppAsyncRuntimeConfig). Требование миграции БД зависит от формата хранения EnvironmentConfig: если JSON/Pydantic blob — defaults через Field(default=...) достаточны; если SQL‑колонки — нужна Alembic-миграция.
1. Changed Entities (Transport Layer)
AsyncAPIClient (replaces APIClient)
| Attribute | Type | Change |
|---|---|---|
session |
requests.Session → httpx.AsyncClient |
Замена sync клиента на async |
base_url |
str |
Без изменений |
csrf_token |
str |
Без изменений (кешируется) |
semaphore |
NEW asyncio.Semaphore |
Ограничение конкурентных соединений |
timeout |
NEW httpx.Timeout |
Настраиваемый таймаут (default: 30s) |
Lifecycle: Клиент создаётся через async with AsyncAPIClient(env_config) as client: — контекстный менеджер управляет жизненным циклом httpx.AsyncClient (открытие/закрытие connection pool).
SupersetClient (merged sync + async)
| Attribute | Type | Change |
|---|---|---|
network |
APIClient → AsyncAPIClient |
Замена sync на async |
| All mixin methods | def → async def |
Все 13 миксинов становятся async |
Methods (typical signature change):
# Before (sync)
def get_dashboards(self, query=None) -> tuple[int, list[dict]]: ...
# After (async)
async def get_dashboards(self, query=None) -> tuple[int, list[dict]]: ...
SupersetSqlLabExecutor
| Attribute | Type | Change |
|---|---|---|
_client |
SupersetClient (sync) |
Замена на async |
execute_sql() |
def → async def |
Асинхронный HTTP + поллинг |
poll_execution_status() |
def → async def |
time.sleep → asyncio.sleep |
max_polls |
int (60) |
Без изменений |
poll_interval_seconds |
float (2.0) |
Без изменений |
TaskManager
| Attribute | Type | Change |
|---|---|---|
executor |
ThreadPoolExecutor |
Удалён |
_tasks |
dict[str, asyncio.Task] |
NEW — словарь активных asyncio.Task |
run_task() |
def → async def |
Запуск через asyncio.create_task |
cancel_task() |
def → async def |
Отмена через task.cancel() |
EventBus
| Attribute | Type | Change |
|---|---|---|
_queue |
queue.Queue → asyncio.Queue |
Замена sync очереди на async |
publish() |
def → async def |
put_nowait → await put |
subscribe() |
def → async def |
get → await get |
LLM Client (unified)
| Attribute | Type | Change |
|---|---|---|
call_openai_compatible() |
def → async def |
Асинхронный HTTP |
_do_http_request() |
requests.post + time.sleep |
httpx.AsyncClient.post + asyncio.sleep |
timeout |
int (180) → httpx.Timeout(180) |
Настраиваемый таймаут |
Notification Providers
| Provider | Method | Change |
|---|---|---|
| SMTPProvider | send() |
smtplib.SMTP → aiosmtplib.SMTP |
| TelegramProvider | send() |
requests.post → httpx.AsyncClient.post |
| SlackProvider | send() |
requests.post → httpx.AsyncClient.post |
2. New Entities
SupersetClientRegistry [C:4]
Синглтон-реестр долгоживущих асинхронных клиентов по env_id:
| Attribute | Type | Description |
|---|---|---|
_clients |
dict[str, _ClientSlot] |
env_id → slot с shared ресурсами |
_ClientSlot.client |
httpx.AsyncClient |
Долгоживущий HTTP-клиент (connection pooling) |
_ClientSlot.semaphore |
asyncio.Semaphore |
Глобальный per‑env семафор (connection_pool_size) |
_ClientSlot.auth_lock |
asyncio.Lock |
Защита конкурентного обновления CSRF/auth‑токенов |
Lifecycle: Клиенты создаются при первом обращении к env_id, живут до shutdown. async with — только для тестов.
DB Threading Contract
SQLAlchemy‑сессии создаются, используются, коммитятся/откатываются и закрываются внутри одного worker‑потока (через asyncio.to_thread(db_executor, ...)). Никакие ORM‑объекты не пересекают границу потока без явного detach/serialization. DB‑адаптер владеет жизненным циклом сессии.
3. Configuration Parameters
Environment Config (per Superset instance — EnvironmentConfig)
{
"connection_pool_size": 20,
"connection_pool_timeout": 30,
"request_timeout": 30,
"llm_timeout": 120
}
App-Level Async Runtime Config (global — отдельная секция конфигурации приложения, не per‑env)
{
"db_executor_workers": 10,
"file_executor_workers": 10,
"git_executor_workers": 5,
"graceful_shutdown_timeout": 30
}
Эти параметры добавляются в модель EnvironmentConfig (Pydantic) и хранятся в БД как часть конфигурации окружения.
3. Unchanged Entities
Следующие сущности не меняют свою структуру, только способ взаимодействия с внешними сервисами:
| Entity | Type | Notes |
|---|---|---|
TranslationJob |
SQLAlchemy Model | Без изменений |
TranslationPreviewSession |
SQLAlchemy Model | Без изменений |
TranslationPreviewRecord |
SQLAlchemy Model | Без изменений |
TranslationPreviewLanguage |
SQLAlchemy Model | Без изменений |
Dictionary |
SQLAlchemy Model | Без изменений |
LLMProvider |
SQLAlchemy Model | Без изменений |
EnvironmentConfig |
SQLAlchemy Model | Добавлены pool-параметры (см. выше) |
| Все Pydantic request/response schemas | Pydantic | Без изменений (FR-018) |
| Все API routes (URL, method, status codes) | FastAPI Router | Без изменений |
PluginBase.execute() contract |
ABC | Уже async def, без изменений |
4. Task State Transitions (Unchanged)
PENDING → RUNNING → COMPLETED
→ FAILED
→ CANCELLED
Переходы состояний не меняются. Меняется только механизм запуска: ThreadPoolExecutor.submit() → asyncio.create_task().
5. WebSocket Message Format (Unchanged)
{
"type": "log",
"task_id": "uuid",
"timestamp": "ISO-8601",
"level": "INFO|WARNING|ERROR",
"message": "...",
"trace_id": "uuid"
}
Формат сообщений WebSocket не меняется. Меняется только транспорт отправки: queue.Queue + call_soon_threadsafe → asyncio.Queue + await.