# 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 Следующими связанными решениями должны быть: - политика дедупликации данных - модель частичной загрузки и фиксации прогресса - политика конкурентного запуска задач