Выбор ответа Senior Django ORM и N+1 спрашивают часто обновлено

Задача Celery и незакоммиченная транзакция

Вопрос

Во вьюхе внутри transaction.atomic() создаётся заказ и сразу вызывается send_receipt.delay(order.id). Иногда воркер падает с Order.DoesNotExist. Почему и как исправить?

Попробуйте сами

Выберите вариант, и откроется разбор.

Показать ответ и разбор

Коротко

Правильный ответ: Воркер берёт задачу до коммита; ставить её через transaction.oncommit. Брокер получает задачу сразу, а транзакция коммитится позже.

Варианты ответа

  1. Celery не умеет передавать числа больше 2^31, и id заказа обрезается
  2. Нужно передавать в задачу весь объект заказа, а не id, чтобы не читать базу
  3. Воркер берёт задачу до коммита; ставить её через transaction.on_commitправильный ответ
  4. Нужно увеличить таймаут воркера, чтобы он дождался появления заказа в базе

Разбор

Брокер получает задачу сразу, а транзакция коммитится позже. Быстрый воркер успевает выполнить запрос раньше коммита и заказа не видит, а при откате транзакции письмо вообще уйдёт о несуществующем заказе. transaction.on_commit(lambda: send_receipt.delay(order.id)) отправляет задачу только после успешного коммита (в Celery 5.4+ для этого есть и delay_on_commit). Передавать ORM-объект в задачу плохо: он сериализуется и устаревает.

Ответьте сами, а не прочитайте

В тренажёре вопрос попадёт в повторения и вернётся, когда начнёт забываться. Плюс билеты с зачётом: 20 вопросов, не больше 2 ошибок.