Files
mostovik-backend/docs/adr/ADR-011: Idempotency and Retry Strategy.md
Aleksandr Meshchriakov 25176f31b4
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
fix pre-commit
2026-03-17 13:55:34 +01:00

7.6 KiB
Raw Blame History

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

Следующими связанными решениями должны быть:

  • политика дедупликации данных
  • модель частичной загрузки и фиксации прогресса
  • политика конкурентного запуска задач