Асинхронный сервис постановки и исполнения ETL‑задач поверх очереди в PostgreSQL. Предоставляет HTTP API для триггера задач, мониторинга статуса и отмены. Внутри процесса поднимает пул воркеров, которые конкурируют за задачи через SELECT … FOR UPDATE SKIP LOCKED и обрабатывают их с учётом lease/heartbeat и кооперативной отмены. Для пробуждения воркеров используется LISTEN/NOTIFY.
Сервис решает типовую задачу фоновой обработки задач: один общий пул воркеров, одна очередь в БД, несколько типов задач (пайплайнов), идемпотентность, повторные попытки, контроль конкуренции через advisory‑lock по `lock_key`, кооперативная отмена, возврат «потерянных» задач.
-`WORKERS_JSON` - список конфигураций воркеров, например: `[{"queue":"etl.default","concurrency":2}]`
-`DL_HEARTBEAT_SEC` (def: 10)
-`DL_DEFAULT_LEASE_TTL_SEC` (def: 60)
-`DL_REAPER_PERIOD_SEC` (def: 10)
-`DL_CLAIM_BACKOFF_SEC` (def: 15)
## Архитектура и потоки данных
- HTTP слой: FastAPI‑приложение (`dataloader.api`) с v1 API и инфраструктурными маршрутами (`/health`, `/info`).
- Контекст: `AppContext` инициализирует логирование, `AsyncEngine`, `async_sessionmaker` и предоставляет DI (`get_session`).
- Очередь: одна таблица `dl_jobs` и журнал `dl_job_events` в PostgreSQL. Идемпотентность на уровне `idempotency_key`. Пробуждение воркеров через триггеры NOTIFY в БД и listener на стороне приложения.
- Воркеры: `WorkerManager` поднимает N асинхронных воркеров (`PGWorker`) на основании `WORKERS_JSON`. Каждый воркер:
1) ждёт уведомление (LISTEN/NOTIFY) или таймаут,
2) пытается «захватить» одну задачу (SELECT … FOR UPDATE SKIP LOCKED),
3) выставляет `running`, получает advisory‑lock по `lock_key`,
4) исполняет соответствующий пайплайн с heartbeat,
5) завершает задачу: `succeeded`/`failed`/`canceled` или возвращает в очередь на ретрай.
- Реапер: фоновая задача, периодически возвращает «потерянные» running‑задачи в `queued`.
## Структура
```
src/dataloader/
├── __main__.py # Запуск uvicorn, lifecycle через FastAPI lifespan
│ ├── reaper.py # Requeue_lost на базе репозитория
│ │
│ └── pipelines/
│ ├── __init__.py # Автозагрузка модулей для регистрации
│ ├── registry.py # Реестр обработчиков задач (@register)
│ └── noop.py # Пример эталонного пайплайна
│
└── logger/
└── ... # Логирование
```
## Взаимодействие с БД
- Подключение: `postgresql+asyncpg` через SQLAlchemy AsyncEngine.
- Схемы: логическое имя `queue` маппится на реальную через `schema_translate_map` (см. `engine.py`), имя реальной схемы задаётся `PG_SCHEMA_QUEUE`.
- DDL: см. `DDL.sql`. Ключевые элементы:
-`dl_jobs`с индексами на claim и running‑lease,
-`dl_job_events` как журнал событий,
- триггер `notify_job_ready()` + `LISTEN dl_jobs` для пробуждения воркеров.
- Конкуренция: claim через `FOR UPDATE SKIP LOCKED`, взаимное исключение по бизнес‑сущности через advisory‑lock `pg_try_advisory_lock(hashtext(lock_key))`.
- Надёжность: at‑least‑once. Пайплайны должны быть идемпотентны в части записи в целевые таблицы.
- Пайплайны должны быть идемпотентны (повторный запуск не должен ломать данные).
- Долгие операции разбивайте на шаги с`yield`, чтобы работал heartbeat и отмена.
- Для бизнес‑взаимного исключения выбирайте корректный `lock_key` (например, `customer:{id}`), чтобы параллельные задачи не конфликтовали.
### Добавление ETL‑задачи (шаги)
1. Создать модуль в `workers/pipelines/` и зарегистрировать обработчик через `@register`.
2. Убедиться, что модуль импортируется автоматически (происходит на старте через `load_all()`).
3. При необходимости расширить схему `args` и валидацию на стороне продьюсера (кто вызывает API).
4. При постановке задачи в `POST /api/v1/jobs/trigger` указать `task`с новым именем и желаемые `args`.
5. При необходимости завести отдельную очередь (`queue`) и добавить её в `WORKERS_JSON`с нужной `concurrency`.
## Логирование, метрики, аудит
- Логи: структурированные, через `logger/*`. Middleware (`api/middleware.py`) логирует входящие запросы/исходящие ответы, время обработки, пишет метрики и аудит‑события.
- Один процесс FastAPI с пулом асинхронных воркеров внутри. Масштабирование - количеством реплик (Deployment). Очередь в БД и advisory‑lock обеспечат корректность при гонках между репликами.
- Readiness/Liveness - `/health`.
- Корректное завершение: при SIGTERM менеджер подаёт сигнал остановки, завершает воркеры и reaper, закрывает соединения.