Files
ss-tools/docs/adr/ADR-0011-async-backend.md
busya 24d3b7d1f9 refactor(task-manager): implement task resilience and execution lifecycle improvements
Enhance the reliability and observability of the task execution engine
by introducing retry mechanisms, idempotency, and structured progress
tracking.

- Implement centralized retry logic with exponential backoff support
  in `JobLifecycle`.
- Add `retry_task` API endpoint and `TaskManager` method for manual
  task restarts.
- Introduce task idempotency using `_idempotency_key` to prevent
  duplicate executions.
- Add `retry_count`, `max_retries`, `last_error`, and `progress` fields
  to the `Task` model and ensure persistence via `TaskPersistenceService`.
- Upgrade `SchedulerService` to use differential synchronization with
  the persistent `SQLAlchemyJobStore` for better job durability.
- Implement structured heartbeat logging to support real-time progress
  updates.
- Update project documentation and ADRs to reflect the new plugin
  runtime and task resilience patterns.
- Add comprehensive unit and integration tests for the new task
  lifecycle features.
2026-07-12 15:31:56 +03:00

5.5 KiB
Raw Blame History

[DEF:ADR-0011:ADR]

@STATUS PARTIALLY IMPLEMENTED

@PURPOSE Документировать async-first направление backend: httpx.AsyncClient для новых внешних вызовов, bounded executors для блокирующих операций и Task Manager на asyncio.

@RATIONALE superset-tools одновременно взаимодействует с несколькими внешними системами (Superset REST API, LLM API, SMTP, Telegram, Slack) и выполняет блокирующие операции (Git, файловый I/O, SQLAlchemy). При одном воркере uvicorn синхронные блокирующие вызовы останавливают все входящие HTTP-запросы. Единственный способ обеспечить конкурентность — сделать весь I/O неблокирующим.

@REJECTED aiohttp — отвергнут в пользу httpx, который уже был в зависимостях (0.28.1) и имеет лучший API для async HTTP.

@REJECTED Полный переход на async SQLAlchemy — потребовал бы переписывания всех моделей и репозиториев, что выходит за рамки этой фичи. run_blocking с bounded executor достаточен.

@REJECTED Dual-stack (sync + async) — потребовал бы поддержки двух транспортов, увеличивая surface для ошибок и усложняя код.

@REJECTED Wrapper-only (asyncio.to_thread на всё) — rejected, т.к. asyncio.to_thread не принимает executor и использует default пул без backpressure.

@REJECTED Multiprocessing / --workers N — rejected, т.к. увеличение воркеров не решает проблему блокировки внутри одного воркера и увеличивает потребление памяти.

Decision

Новые конкурентные пути backend строятся по async-first модели. Утверждение о полной миграции всех существующих путей снято: в репозитории остаются синхронные совместимые клиенты и адаптеры, пока они не будут переведены отдельными изменениями с тестами.

Transport Layer

  1. HTTP-клиент: requests.Session → httpx.AsyncClient через класс AsyncAPIClient в core/utils/async_network.py.
  2. SupersetClient: sync + AsyncSupersetClient объединены в единый асинхронный клиент. Все 13 миксинов — async def.
  3. Семафоры: Глобальный per‑env asyncio.Semaphore через синглтон SupersetClientRegistry (core/utils/client_registry.py). При исчерпании — таймаут 30s → 503.
  4. LLM-клиент: Унифицированный LLMHttpClient в plugins/translate/_llm_async_http.py на httpx.AsyncClient. Rate-limit backoff через asyncio.sleep.

Blocking Operations

  1. Bounded executors: Именованные ThreadPoolExecutor (db, file, git) через helper run_blocking(kind, fn) в core/utils/executors.py. Вместо asyncio.to_thread.
  2. Graceful shutdown: Все клиенты закрываются при shutdown через SupersetClientRegistry.shutdown(). Незавершённые задачи помечаются CANCELLED.

Task Manager

  1. Запуск задач: ThreadPoolExecutor → asyncio.create_task. Задачи разделяют основной event loop.
  2. EventBus: queue.Queue + call_soon_threadsafe → asyncio.Queue(maxsize=10000) с fan‑out на подписчиков.
  3. Lifecycle: Thread-based → async context manager.

Конфигурация

  1. Per‑env: connection_pool_size, connection_pool_timeout, request_timeout, llm_timeout в EnvironmentConfig.
  2. App‑level: db_executor_workers, file_executor_workers, git_executor_workers, graceful_shutdown_timeout в AppAsyncRuntimeConfig.

Тестирование

  1. requests_mock → pytest-httpx для мокирования асинхронных HTTP-вызовов.
  2. Тесты конкурентности с asyncio.gather для всех C3+ контрактов.
  3. Rejected-path regression тесты для каждого запрещённого паттерна.

Модули, затронутые рефакторингом

  • core/utils/: async_network.py (+semaphore), client_registry.py (new), executors.py (new), fileio.py (+async wrappers), network.py → Tombstone, superset_compilation_adapter.py → async
  • core/superset_client/: _base.py (+13 mixins) → все async
  • core/task_manager/: manager.py, event_bus.py, lifecycle.py → все async
  • api/routes/: dashboards, assistant, migration, datasets, git, environments, profile → все async
  • plugins/translate/: _llm_async_http.py (new), все файлы → async
  • plugins/: backup.py, git_plugin.py, llm_analysis/plugin.py, storage/plugin.py → async
  • services/: notifications/providers.py, notifications/service.py, git/_base.py → async
  • services/: git/_base.py, git/_branch.py, git/_sync.py, git/_status.py, git/_merge.py → async

[/DEF:ADR-0011:ADR]