Задача Celery и незакоммиченная транзакция
Вопрос
Во вьюхе внутри transaction.atomic() создаётся заказ и сразу вызывается send_receipt.delay(order.id). Иногда воркер падает с Order.DoesNotExist. Почему и как исправить?
Попробуйте сами
Выберите вариант, и откроется разбор.
Показать ответ и разбор
Коротко
Варианты ответа
- Celery не умеет передавать числа больше 2^31, и id заказа обрезается
- Нужно передавать в задачу весь объект заказа, а не id, чтобы не читать базу
- Воркер берёт задачу до коммита; ставить её через
transaction.on_commitправильный ответ - Нужно увеличить таймаут воркера, чтобы он дождался появления заказа в базе
Разбор
Брокер получает задачу сразу, а транзакция коммитится позже. Быстрый воркер успевает выполнить запрос раньше коммита и заказа не видит, а при откате транзакции письмо вообще уйдёт о несуществующем заказе. transaction.on_commit(lambda: send_receipt.delay(order.id)) отправляет задачу только после успешного коммита (в Celery 5.4+ для этого есть и delay_on_commit). Передавать ORM-объект в задачу плохо: он сериализуется и устаревает.
Ответьте сами, а не прочитайте
В тренажёре вопрос попадёт в повторения и вернётся, когда начнёт забываться. Плюс билеты с зачётом: 20 вопросов, не больше 2 ошибок.
Ещё по теме «Django ORM и N+1»
- Задвоение сумм при нескольких annotate
- Посчитать SQL-запросы при ленивой загрузке
- Как prefetch_related склеивает данные
- exists() и count() вместо загрузки строк
- Атомарное увеличение счётчика
- Prefetch с вложенным select_related
- transaction.atomic
- Когда выполняется QuerySet