Some checks failed
CI/CD Pipeline / Telegram Notify Success (push) Has been cancelled
CI/CD Pipeline / Run Tests (push) Has been cancelled
CI/CD Pipeline / Code Quality Checks (push) Has been cancelled
CI/CD Pipeline / Code Quality Checks (pull_request) Successful in 1m42s
CI/CD Pipeline / Run Tests (pull_request) Successful in 2m25s
CI/CD Pipeline / Telegram Notify Success (pull_request) Successful in 1m34s
129 lines
7.6 KiB
Markdown
129 lines
7.6 KiB
Markdown
# ADR-011: Idempotency and Retry Strategy for Background Tasks
|
||
|
||
## Status
|
||
Accepted
|
||
|
||
## Context
|
||
|
||
Система активно использует фоновые задачи Celery для:
|
||
- загрузки данных из внешних источников
|
||
- инкрементальной синхронизации
|
||
- обработки файлов
|
||
- периодических сканирований и парсинга
|
||
|
||
Внешние источники и фоновые очереди не гарантируют:
|
||
- exactly-once delivery
|
||
- стабильность сети
|
||
- неизменность ответа источника
|
||
- отсутствие повторных запусков задач
|
||
- отсутствие ручных перезапусков оператором
|
||
|
||
Также возможны следующие сценарии:
|
||
- worker завершился после частичной записи данных
|
||
- задача была запущена повторно по retry
|
||
- beat и ручной запуск вызвали одинаковую задачу почти одновременно
|
||
- внешняя система ответила ошибкой после того, как часть данных уже была получена
|
||
- оператор повторно инициировал синхронизацию за тот же период
|
||
|
||
Для такого класса системы идемпотентность является не оптимизацией, а обязательным архитектурным требованием.
|
||
|
||
## Decision
|
||
|
||
Все фоновые задачи, изменяющие данные или взаимодействующие с нестабильными внешними источниками, должны проектироваться как idempotent-first.
|
||
|
||
### Основные правила
|
||
|
||
1. Повторный запуск одной и той же задачи не должен приводить к неконтролируемому дублированию данных.
|
||
2. Результат повторного выполнения должен быть либо:
|
||
- идентичен первому успешному выполнению,
|
||
- либо безопасно приводить систему к тому же целевому состоянию.
|
||
3. Retry рассматривается как нормальный сценарий эксплуатации, а не как исключение.
|
||
|
||
### Идемпотентность обеспечивается за счет
|
||
|
||
- уникальных ограничений на уровне БД для естественных бизнес-ключей
|
||
- upsert/update-or-create подходов там, где это возможно
|
||
- явной привязки загружаемых данных к периоду, источнику, типу выгрузки или внешнему идентификатору
|
||
- дедупликации на уровне сервисного слоя перед записью
|
||
- разделения этапов extract / transform / load
|
||
- фиксации статуса фоновой задачи и контекста её выполнения
|
||
- запрета на "append-only" запись без проверки уникальности для синхронизируемых сущностей
|
||
|
||
### Retry policy
|
||
|
||
Retry допускается только для временных ошибок:
|
||
- сетевые сбои
|
||
- временная недоступность внешнего источника
|
||
- rate limiting
|
||
- временные ошибки брокера или инфраструктуры
|
||
- временные проблемы с файловой системой или внешним сервисом
|
||
|
||
Retry не должен безусловно выполняться для:
|
||
- ошибок валидации входных данных
|
||
- ошибок схемы/контракта источника
|
||
- систематических ошибок парсинга
|
||
- нарушений инвариантов модели
|
||
- ошибок конфигурации
|
||
|
||
Для retry необходимо:
|
||
- использовать ограниченное число повторов
|
||
- использовать backoff
|
||
- логировать причину повтора
|
||
- сохранять контекст периода/источника/операции
|
||
|
||
### Concurrency policy
|
||
|
||
Для задач, работающих по одному и тому же периоду или источнику, должна применяться логическая защита от параллельного конкурентного запуска.
|
||
|
||
Предпочтительный порядок контроля:
|
||
1. блокировка на уровне бизнес-контракта задачи
|
||
2. проверка существующего job-run статуса
|
||
3. защита уникальными ограничениями БД
|
||
4. безопасное повторное выполнение как fallback
|
||
|
||
### Operational policy
|
||
|
||
Задача считается корректной, если после:
|
||
- retry
|
||
- повторного ручного запуска
|
||
- запуска за уже обработанный период
|
||
- частичного падения и повторного восстановления
|
||
|
||
данные остаются консистентными и не требуют ручной чистки в обычном сценарии эксплуатации.
|
||
|
||
## Consequences
|
||
|
||
### Positive
|
||
|
||
- система устойчива к повторным запускам и временным отказам
|
||
- снижается риск дублирования данных
|
||
- упрощается эксплуатация и ручной re-run задач
|
||
- Celery retry становится безопасным штатным механизмом
|
||
- упрощается восстановление после сбоев worker-процессов
|
||
|
||
### Negative
|
||
|
||
- усложняется реализация сервисного слоя и моделей
|
||
- требуется дисциплина в проектировании бизнес-ключей
|
||
- часть задач становится медленнее из-за дополнительных проверок
|
||
- нужны дополнительные ограничения и индексы в БД
|
||
- необходимо явно проектировать поведение при partial success
|
||
|
||
## Alternatives considered
|
||
|
||
### 1. At-most-once execution
|
||
Отклонено, так как не соответствует реальной природе фоновой обработки и нестабильных внешних интеграций.
|
||
|
||
### 2. Полагаться только на Celery retry без идемпотентности на уровне домена
|
||
Отклонено, так как это приводит к дублированию данных и хрупкому поведению при сбоях.
|
||
|
||
### 3. Полная ручная очистка данных перед каждым повторным запуском
|
||
Отклонено, так как не масштабируется и опасно в эксплуатации.
|
||
|
||
## Notes
|
||
|
||
Следующими связанными решениями должны быть:
|
||
- политика дедупликации данных
|
||
- модель частичной загрузки и фиксации прогресса
|
||
- политика конкурентного запуска задач
|