Как решать проблему дублирования задач в Celery при масштабировании
Что проверяют
Понимаете ли вы, что Celery даёт доставку «хотя бы один раз», а не «ровно один раз», и что при нескольких воркерах повторы неизбежны: их можно только обезвредить. Следом обычно спрашивают, что будет с задачей, если воркер упадёт посреди выполнения.
Как отвечать
Начните с источников дублей, потом переходите к защите:
acks_late=True: подтверждение уходит брокеру после выполнения, поэтому задачу, на которой умер воркер, получит другой. Потерь меньше, повторов больше;visibility_timeout: у брокера Redis по умолчанию час. Задачу сcountdownилиetaдольше этого срока, как и слишком долгую задачу с поздним подтверждением, брокер выдаст второму воркеру, пока первый ещё работает;- несколько экземпляров
celery beat: каждый шлёт периодические задачи, и после масштабирования деплоем их внезапно два. Планировщик должен быть один, либо нужен планировщик с распределённой блокировкой; - повтор со стороны вызывающего кода: ретрай HTTP-запроса или отправка задачи до коммита транзакции.
Защита строится на идемпотентности. Ключ вычисляется из смысла задачи (например, order_id плюс тип операции), а не генерируется внутри неё. Дальше варианты:
- уникальное ограничение в базе:
INSERT ... ON CONFLICT DO NOTHINGв той же транзакции, что и полезная работа. Самый надёжный способ; - блокировка в Redis:
SET key value NX EX ttlперед стартом, TTL больше максимального времени выполнения; - идемпотентная операция по смыслу: «установить статус оплачен» вместо «прибавить сумму».
В Django задачи ставят через transaction.on_commit, иначе воркер прочитает ещё не записанные данные.
Где спотыкаются
Генерируют uuid4() внутри задачи и проверяют его по базе. Каждый вызов получает новый идентификатор, и проверка не срабатывает никогда.
Считают, что явный task_id в apply_async защищает от повторов. Celery не дедуплицирует сообщения по task_id, брокер спокойно примет два одинаковых.
Говорят, что acks_late борется с дублями. Он борется с потерями и ровно поэтому увеличивает число повторных выполнений.