Files
ss-tools/specs/032-translate-requests-httpx/data-model.md
busya bc32dd65ef 032: mark all tasks [x] — feature complete
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.
2026-06-04 21:09:14 +03:00

170 lines
7.8 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 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)**:
```python
# 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` | Глобальный perenv семафор (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`)
```json
{
"connection_pool_size": 20,
"connection_pool_timeout": 30,
"request_timeout": 30,
"llm_timeout": 120
}
```
### App-Level Async Runtime Config (global — отдельная секция конфигурации приложения, не perenv)
```json
{
"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)
```json
{
"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`.