- Replace all occurrences of 'ss-tools' with 'superset-tools' in 104 files - Rename git bundle file ss-tools.bundle → superset-tools.bundle - Update .gitignore pattern accordingly - Preserve variable names (hasSsTools etc.) and code identifiers
15 KiB
UX Reference: Async Backend Refactoring
Feature Branch: 032-translate-requests-httpx
Created: 2026-06-04
Status: ✅ Implemented — all scenarios verified
1. User Persona & Context
- Who is the user?:
- Оператор superset-tools — настраивает и запускает задачи (переводы, бэкапы, деплои, миграции). Может быть несколько операторов одновременно.
- Администратор — мониторит состояние системы, просматривает дашборды, управляет окружениями.
- API-клиент — внешние системы, вызывающие API superset-tools (например, CI/CD pipeline для деплоя).
- What is their goal?: Выполнять несколько операций параллельно без блокировок. Видеть отзывчивый интерфейс независимо от того, какие фоновые задачи выполняются.
- Context: Работа через веб-интерфейс SvelteKit SPA или напрямую через REST API. Один экземпляр сервера обслуживает нескольких пользователей.
2. The «Happy Path» Narratives
Narrative A: Translate Setup (основная жалоба)
Оператор открывает переводческую задачу. На вкладке Preview нажимает «Run Preview» (sample_size=10). Видит спиннер. Не дожидаясь, переключается на Target Config → жмёт «Check Target Schema». Через 2 секунды — зелёная галочка. Возвращается на Preview — переводы уже загрузились. Всё заняло ~8 секунд параллельно вместо 11 последовательно.
Narrative B: Multi-Operator (командная работа)
Оператор А запускает бэкап 100 дашбордов из продакшен-Superset. Оператор Б в это же время настраивает переводческую задачу — нажимает предпросмотр, проверяет схему. Администратор открывает список джоб. Все три пользователя работают без задержек — их запросы не стоят в очереди за бэкапом.
Narrative C: CI/CD Deploy + Monitoring (API-клиент)
CI/CD пайплайн дёргает POST /git/deploy для деплоя дашборда в стейджинг. Одновременно администратор через UI проверяет статус дашбордов в LLM Analysis. Деплой (15 секунд на экспорт/импорт + Git-операции) и анализ (скриншоты + LLM) выполняются параллельно.
3. Interface Mockups
API Interaction Flow — Multi-Plugin Concurrency
Клиент A (Backup) Сервер (1 worker) Клиент B (Translate) Клиент C (Dashboard)
│ │ │ │
│ POST /backup/run ───────►│ │ │
│ │ ┌─ Superset export ──┐ │ │
│ │ │ get_dashboards() │ │ │
│ │ │ (async, не блок.) │ │ │
│ │ └─────────────────────┘ │ │
│ │ │ │
│ │◄── POST /preview ────────│ │
│ │ │ │
│ │ ┌─ Superset chart data ┐│ │
│ │ │ LLM call (async) ││ │
│ │ └──────────────────────┘│ │
│ │ │ │
│ │ │◄── GET /dashboards ─────│
│ │ │ │
│ │ ┌─ Все три выполняются ┐│ │
│ │ │ конкурентно, ни один ││ │
│ │ │ не ждёт другой ││ │
│ │ └──────────────────────┘│ │
│ │ │ │
│◄── backup result ────────│ │ │
│ (100 dashboards OK) │ │ │
│ │◄── preview result ───────│ │
│ │ (10 rows + cost) │ │
│ │ │◄── dashboard list ──────│
До vs После (тайминг при 1 worker)
ДО рефакторинга (sync, 1 worker):
┌────────────┬──────────────────┬───────────────┬──────────┐
│ Backup 45s │ Preview 8s │ SchemaCheck 3s│ GET /jobs│ = 56.5s total
└────────────┴──────────────────┴───────────────┴──────────┘
ПОСЛЕ рефакторинга (async, 1 worker):
┌──────────────────────────────────────────────────────────┐
│ Backup 45s (экспорт дашбордов, файловый I/O) │
│ ┌──────────────────┐ │
│ │ Preview 8s │ │
│ │ ┌──────────────┐ │
│ │ │SchemaCheck 3s│ │
│ │ └──────────────┘ │
│ └──────────────────┘ │
│ ┌── GET /jobs 50ms│
└────────────────────────────────────────┴─────────────────┘ = 45s total
Существующие UI-компоненты (без изменений)
Контракты компонентов не меняются. Меняется только временная характеристика — компоненты больше не ждут друг друга:
| Компонент | Файл | Состояния (без изменений) | Что изменилось |
|---|---|---|---|
| TranslationPreview | TranslationPreview.svelte |
idle → loading → preview_loaded / error | Может быть в loading одновременно с TargetSchemaHint в checking |
| TargetSchemaHint | TargetSchemaHint.svelte |
idle → checking → complete / error | Не ждёт окончания preview |
| DashboardGrid | DashboardGrid.svelte |
Загрузка списка | Не ждёт backup/git/deploy |
| BackupConfig | BackupConfig.svelte |
Запуск бэкапа | Не блокирует другие вкладки |
| GitDeployPanel | Компонент деплоя | Деплой дашборда | Не блокирует просмотр дашбордов |
| StorageUpload | Загрузка файлов | Upload progress | Не блокирует фоновые задачи |
4. The «Error» Experience
Scenario A: Таймаут Superset API при бэкапе
- User Action: Запущен бэкап 100 дашбордов. Superset начинает отвечать медленно.
- System Response:
- Конкретный вызов
export_dashboard(id)получает таймаут (30 секунд). - В отчёте бэкапа этот дашборд помечается как FAILED с причиной «timeout».
- Бэкап продолжается со следующим дашбордом.
- Другие запросы не затронуты — админ всё ещё может смотреть список дашбордов.
- Конкретный вызов
- Recovery: Повторный запуск бэкапа только для упавших дашбордов.
Scenario B: Отмена запроса (браузер закрыт)
- User Action: Оператор запустил предпросмотр, закрыл вкладку.
- System Response:
- FastAPI обнаруживает
asyncio.CancelledError. - Активный HTTP-вызов к LLM отменяется (
httpx.AsyncClientзакрывает соединение). - Поллинг SQL Lab прекращается.
- Если БД-транзакция не закоммичена — откатывается.
- FastAPI обнаруживает
- Recovery: Ноль утечек. При следующем открытии страницы — состояние «idle».
Scenario C: Сетевая ошибка LLM-провайдера
- User Action: Запущен предпросмотр, LLM-провайдер недоступен (DNS failure).
- System Response:
- Исключение только в запросе предпросмотра.
- UI показывает
preview_error: «Preview failed: connection to LLM provider failed». - Параллельный запрос проверки схемы (к Superset) выполняется нормально.
- Recovery: Кнопка «Retry Preview».
Scenario D: Конкурентный deploy + backup одного дашборда
- User Action: CI/CD запустил деплой дашборда X. Оператор запустил бэкап всех дашбордов (включая X).
- System Response: Бэкап и деплой работают с разными версиями дашборда. Бэкап экспортирует текущую версию из Superset, деплой импортирует новую. Конфликта данных не возникает — каждый работает со своим снимком.
- Recovery: Не требуется. Операции идемпотентны.
Scenario E: SMTP-сервер недоступен при отправке отчёта
- User Action: Задача завершилась, scheduler пытается отправить email-отчёт.
- System Response:
aiosmtplibполучает connection timeout (10 секунд).- Уведомление не отправлено, ошибка залогирована.
- Основной API продолжает работать — пользователи не замечают проблему.
- Recovery: Отчёт доступен в UI. Администратор проверяет логи, чинит SMTP-сервер.
5. Tone & Voice
- Style: Технический, лаконичный. Аудитория — DevOps-инженеры и операторы данных.
- Terminology: «Предпросмотр» (Preview), «Бэкап» (Backup), «Деплой» (Deploy), «Поллинг» (Polling), «Event loop», «Конкурентность» (Concurrency), «Таймаут» (Timeout).
6. Backward Compatibility Contract
Все публичные API-эндпоинты сохраняют обратную совместимость. Меняется только транспортный слой и временные характеристики.
| Модуль | Endpoint | Method | Тело запроса | Тело ответа | Коды |
|---|---|---|---|---|---|
| Translate | /jobs/{id}/preview |
POST | Без изменений | Без изменений | 201, 400, 502 |
| Translate | /check-target-schema |
POST | Без изменений | Без изменений | 200, 502, 503, 504 |
| Notifications | (scheduler, no public API) | — | Без изменений | Без изменений | — |
Error Mapping (единый для всех эндпоинтов, взаимодействующих с Superset):
| Условие | HTTP Status |
|---|---|
| Pool acquire timeout (семафор исчерпан) | 503 Service Unavailable |
| Superset network unreachable / connection error | 502 Bad Gateway |
| Superset request timeout (upstream) | 504 Gateway Timeout |
| Superset auth failure после retry | 502 Bad Gateway |
| Superset rate-limit (429 от Superset) | 502 Bad Gateway (не раскрываем upstream 429) |
Единственное изменение — временные характеристики: лёгкие запросы больше не ждут тяжёлых.
7. Файловые операции (File I/O Contract)
Файловые операции, вызываемые из async def-ручек, должны быть неблокирующими:
| Операция | До рефакторинга | После рефакторинга |
|---|---|---|
| Экспорт дашборда (ZIP до 50MB) | shutil.copy + open(wb) |
asyncio.to_thread(…) или aiofiles |
| Сохранение загруженного файла | shutil.copyfileobj |
asyncio.to_thread(…) или aiofiles |
| Чтение дашборда с диска | path.read_bytes() |
asyncio.to_thread(…) |
| Git-клонирование репозитория | GitPython (sync) | asyncio.to_thread(…) |
| Удаление директории | shutil.rmtree |
asyncio.to_thread(…) |