7.6 KiB
ADR-011: Idempotency and Retry Strategy for Background Tasks
Status
Accepted
Context
Система активно использует фоновые задачи Celery для:
- загрузки данных из внешних источников
- инкрементальной синхронизации
- обработки файлов
- периодических сканирований и парсинга
Внешние источники и фоновые очереди не гарантируют:
- exactly-once delivery
- стабильность сети
- неизменность ответа источника
- отсутствие повторных запусков задач
- отсутствие ручных перезапусков оператором
Также возможны следующие сценарии:
- worker завершился после частичной записи данных
- задача была запущена повторно по retry
- beat и ручной запуск вызвали одинаковую задачу почти одновременно
- внешняя система ответила ошибкой после того, как часть данных уже была получена
- оператор повторно инициировал синхронизацию за тот же период
Для такого класса системы идемпотентность является не оптимизацией, а обязательным архитектурным требованием.
Decision
Все фоновые задачи, изменяющие данные или взаимодействующие с нестабильными внешними источниками, должны проектироваться как idempotent-first.
Основные правила
- Повторный запуск одной и той же задачи не должен приводить к неконтролируемому дублированию данных.
- Результат повторного выполнения должен быть либо:
- идентичен первому успешному выполнению,
- либо безопасно приводить систему к тому же целевому состоянию.
- Retry рассматривается как нормальный сценарий эксплуатации, а не как исключение.
Идемпотентность обеспечивается за счет
- уникальных ограничений на уровне БД для естественных бизнес-ключей
- upsert/update-or-create подходов там, где это возможно
- явной привязки загружаемых данных к периоду, источнику, типу выгрузки или внешнему идентификатору
- дедупликации на уровне сервисного слоя перед записью
- разделения этапов extract / transform / load
- фиксации статуса фоновой задачи и контекста её выполнения
- запрета на "append-only" запись без проверки уникальности для синхронизируемых сущностей
Retry policy
Retry допускается только для временных ошибок:
- сетевые сбои
- временная недоступность внешнего источника
- rate limiting
- временные ошибки брокера или инфраструктуры
- временные проблемы с файловой системой или внешним сервисом
Retry не должен безусловно выполняться для:
- ошибок валидации входных данных
- ошибок схемы/контракта источника
- систематических ошибок парсинга
- нарушений инвариантов модели
- ошибок конфигурации
Для retry необходимо:
- использовать ограниченное число повторов
- использовать backoff
- логировать причину повтора
- сохранять контекст периода/источника/операции
Concurrency policy
Для задач, работающих по одному и тому же периоду или источнику, должна применяться логическая защита от параллельного конкурентного запуска.
Предпочтительный порядок контроля:
- блокировка на уровне бизнес-контракта задачи
- проверка существующего job-run статуса
- защита уникальными ограничениями БД
- безопасное повторное выполнение как 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
Следующими связанными решениями должны быть:
- политика дедупликации данных
- модель частичной загрузки и фиксации прогресса
- политика конкурентного запуска задач