В коде есть две проблемы.
Первый - самый большой, вы передаете экземпляр объекта задачам - это явно помечено как неправильный подход в документации на сельдерей, в основном то, что вы делаете, это сериализация вашего объекта в определенном состоянии и передача его сельдерею для работы ; но тем временем этот объект может измениться; как решение, которое вы должны передать идентификатор объекта в качестве параметра, поэтому сельдерей задача может принести его свежим:
build.delay(instance.pk)
...
@task
def build(my_key):
instance = SomeModel.objects.get(pk=my_key)
instance.status = 'Processing'
instance.save()
Второй проблемой является тонкий характер и редко показывает на радаре. Первая часть кода может быть вызвана в транзакции, это означает, что может возникнуть ситуация, когда ваша задача (в сельдерее) будет быстрее транзакции, а затем ваша модель будет сохранена сначала в задаче сельдерея, а затем по транзакции - и здесь у вас есть проблема.
Если вы изменили свой код, как было предложено выше, ситуация, описанная как вторая проблема, может не произойти или будет отображаться другая ошибка.
Чтобы избежать такой проблемы, это хорошо, чтобы назвать задачи сельдерей из transaction.oncommit
обработчика (введен в Django в версии 1.9)
еще один комментарий, что я могу видеть, вы меняете состояние объекта:
instance.status = 'Processing'
скорее всего как что-то информационное, но, возможно, используется как механизм блокировки ... есть очень хороший вариант select_for_update
метод QuerySet
, который позволит вам заблокировать объект на время транзакции. Это особенно хорошо для задач сельдерея, когда вы делаете:
instance = SomeModel.objects.select_for_update().get(pk=my_key)
это остановит вашу задачу ждет другой, чтобы закончить (не забудьте поставить @transaction.atomic
над этой задачей)
если вы пройдете nowait=True
к select_for_update
- он будет генерировать исключение без каких-либо задержек, что позволит вам справиться с ситуацией.
Вы можете передать force_update = True в качестве параметров для метода сохранения. –
О, есть 'поднять DatabaseError (" Принудительное обновление не повлияло ни на какие строки. ")' After force_update – foo