Files
ss-tools/specs/032-translate-requests-httpx/plan.md
busya ec6421de35 rename ss-tools to superset-tools across the entire project
- 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
2026-06-16 11:15:19 +03:00

7.5 KiB
Raw Permalink Blame History

Implementation Plan: Async Backend Refactoring

Branch: 032-translate-requests-httpx | Date: 2026-06-04 | Spec: spec.md | Status: Implemented Input: Feature specification from /specs/032-translate-requests-httpx/spec.md

Status

Реализовано. Полный рефакторинг backend на асинхронный неблокирующий ввод-вывод.

Полный рефакторинг backend на асинхронный неблокирующий ввод-вывод. Big-bang замена синхронного requests.Session на httpx.AsyncClient в центральном APIClient, слияние sync SupersetClient и AsyncSupersetClient в единый асинхронный клиент, перевод всех 13 миксинов SupersetClient на async, замена time.sleep на asyncio.sleep во всех поллинг-циклах, обёртка блокирующих операций БД и файлового I/O в run_blocking (bounded executors), полный переход Task Manager'а с ThreadPoolExecutor на asyncio.create_task. Фронтенд не меняется.

Technical Context

Language/Version: Python 3.9+ (backend, canonical — ADR-0001 note: Python 3.13+ preferred but 3.9+ is the minimum supported), TypeScript (frontend Svelte 5 runes-only) Primary Dependencies: FastAPI 0.126, SQLAlchemy, APScheduler 3.11, httpx 0.28 (already present), anyio 4.12 (already present) New Dependencies: pytest-httpx (test mocking), aiofiles (optional — file I/O), aiosmtplib (optional — SMTP) Storage: PostgreSQL 16 (unchanged — DB operations via asyncio.to_thread) Testing: pytest (backend), pytest-httpx (new), pytest-asyncio (existing) Target Platform: Linux server (Docker), single uvicorn worker Project Type: web application backend (FastAPI REST + WebSocket) Performance Goals: <100ms p95 для лёгких запросов во время тяжёлых операций; конкурентность 3+ тяжёлых запросов при 1 worker Constraints: API-контракты неизменны; все тесты проходят; нулевые утечки соединений; обратная совместимость WebSocket-логирования Scale/Scope: ~16 файлов, ~45 блокирующих вызовов, 1 uvicorn worker, 20 одновременных соединений к Superset (семафор)

Constitution Check

GATE: Must pass before Phase 0 research. Re-check after Phase 1 design.

# Principle Status Justification
I Semantic Contract First PASS Все изменённые функции получат/обновят C3-C4 контракты. Новый AsyncAPIClient — C4 с @PRE/@POST/@SIDE_EFFECT.
II Decision Memory PASS ADR-0011 будет создан с записью rejected-путей (aiohttp, multiprocessing, async SQLAlchemy). @RATIONALE/@REJECTED в каждом C4+ контракте.
III External Orchestrator PASS Топология не меняется. superset-tools остаётся внешним оркестратором над Superset.
IV Module Discipline PASS network.py (542 строки) → Tombstone. async_network.py (<400 строк) — canonical async client, расширяется семафором. SupersetAuthCache уже выделен.
V RBAC Enforcement PASS Проверки прав не затрагиваются. Ручки сохраняют Depends(has_permission(...)).
VI Frontend Svelte 5 Runes Only N/A Фронтенд не меняется.
VII Test-Driven for C3+ PASS Тесты на конкурентность (asyncio.gather + pytest_httpx) написаны для всех C3+ контрактов.

Project Structure

Documentation (this feature)

specs/032-translate-requests-httpx/
├── plan.md              # This file
├── research.md          # Phase 0 output
├── data-model.md        # Phase 1 output
├── quickstart.md        # Phase 1 output
├── contracts/           # Phase 1 output
│   └── modules.md       # GRACE contracts for new/changed modules
└── tasks.md             # Phase 2 output (/speckit.tasks)

Source Code (repository root — files touched)

backend/
├── src/
│   ├── core/
│   │   ├── utils/
│   │   │   ├── network.py           → Tombstone (sync APIClient replaced)
│   │   │   └── async_network.py      → extended: semaphore + pool config added
│   │   │   ├── fileio.py            → async wrappers for file operations
│   │   │   └── superset_compilation_adapter.py → async migration
│   │   ├── superset_client/
│   │   │   ├── _base.py             → merged with AsyncSupersetClient
│   │   │   ├── async_superset_client.py → absorbed into _base.py
│   │   │   └── _mixins/*.py         → all 13 mixins become async
│   │   └── task_manager/
│   │       ├── manager.py           → ThreadPoolExecutor → asyncio.create_task
│   │       ├── lifecycle.py         → async lifecycle
│   │       └── event_bus.py         → async WebSocket logging
│   ├── api/routes/
│   │   ├── dashboards/_detail_routes.py → async SupersetClient
│   │   └── translate/*.py           → already async def, now truly non-blocking
│   ├── plugins/
│   │   ├── translate/
│   │   │   ├── _llm_http.py         → httpx.AsyncClient
│   │   │   ├── preview_llm_client.py → httpx.AsyncClient
│   │   │   ├── superset_executor.py → async execute + asyncio.sleep
│   │   │   ├── _run_source.py       → async chart data fetch
│   │   │   └── preview_executor.py  → async sample row fetch
│   │   ├── backup.py                → async SupersetClient + file I/O
│   │   ├── git_plugin.py            → asyncio.to_thread for shutil + git
│   │   ├── llm_analysis/plugin.py   → async dashboard logs fetch
│   │   └── storage/plugin.py        → asyncio.to_thread for file ops
│   └── services/
│       ├── notifications/providers.py → aiosmtplib + httpx
│       └── git/_base.py             → asyncio.to_thread for git/file ops
└── tests/                            → pytest_httpx replaces requests_mock

Complexity Tracking

Violation Why Needed Simpler Alternative Rejected Because
network.py > 400 строк (542) Центральный HTTP-клиент со всей логикой аутентификации, пагинации, загрузки файлов network.py → Tombstone. async_network.py (уже существует, <400 строк) — canonical async client. SupersetAuthCache уже выделен и переиспользуется обоими клиентами.
Task Manager рефакторинг Переход с ThreadPoolExecutor на asyncio.create_task требует изменения lifecycle и event_bus Оставить ThreadPoolExecutor — rejected, т.к. задачи должны разделять event loop для конкурентности