Возможное состояние гонки между сигналом Django post_save и задачей сельдерея
В приложении django 2.0 у меня есть модель под названием Document, которая загружает и сохраняет изображение в файловой системе. Эта часть работает. Я выполняю некоторое распознавание лиц на изображении, используя https://github.com/ageitgey/face_recognition в задаче сельдерея (v 4.2.1). Я передаю document_id изображения в задачу сельдерея, чтобы задача распознавания лиц могла найти изображение для работы. Это все работает хорошо, если я вызываю задачу face_recognition вручную из действия DocumentAdmin после сохранения изображения.
Я пытался вызвать задачу face_recognition из (models.signals.post_save, sender=Document)
метод в моем models.py, и я получаю сообщение об ошибке из этой строки в задаче celery для face_recognition:
document = Document.objects.get(document_id=document_id)
и ошибка:
[2018-11-26 16:54:28,594: ERROR/ForkPoolWorker-1] Task biometric_identification.tasks.find_faces_task[428ca39b-aefb-4174-9906-ff2146fd6f14] raised unexpected: DoesNotExist('Document matching query does not exist.',)
Traceback (most recent call last):
File "/home/mark/.virtualenvs/memorabilia-JSON/lib/python3.6/site-packages/celery/app/trace.py", line 382, in trace_task
R = retval = fun(*args, **kwargs)
File "/home/mark/.virtualenvs/memorabilia-JSON/lib/python3.6/site-packages/celery/app/trace.py", line 641, in __protected_call__
return self.run(*args, **kwargs)
File "/home/mark/python-projects/memorabilia-JSON/biometric_identification/tasks.py", line 42, in find_faces_task
document = Document.objects.get(document_id=document_id)
File "/home/mark/.virtualenvs/memorabilia-JSON/lib/python3.6/site-packages/django/db/models/manager.py", line 82, in manager_method
return getattr(self.get_queryset(), name)(*args, **kwargs)
File "/home/mark/.virtualenvs/memorabilia-JSON/lib/python3.6/site-packages/django/db/models/query.py", line 403, in get
self.model._meta.object_name
memorabilia.models.DoesNotExist: Document matching query does not exist.
Кроме того, эта ошибка не возникает все время, только изредка. В остальное время процесс работает; т.е. изображение сохранено и лица определены.
Я переопределяю save_model в классе DocumentAdmin, но это просто сохраняет некоторые метаданные для изображения в другой модели. Последняя строка - это вызов super().save_model(request, obj, form, change)
, поэтому я предполагаю, что после этого вызывается сигнал post_save.
Мне кажется, что существует условие гонки между сохранением модели в базе данных и задачей сельдерея, запрашивающей базу данных для вновь созданного document_id. Я думал, что сигнал post_save не был активирован, пока модель не была сохранена?
Нужно ли добавлять искусственную задержку в задачу сельдерея face_recognition, чтобы обойти это возможное состояние гонки, или я что-то упускаю?
Спасибо!
отметка
3 ответа
Проверьте свою функцию где Document
модель сохранена. Он где-то обернут в атомарный блок или ATOMIC_REQUESTS установлен в True
, Так когда post_save
называется, транзакция еще не совершена. Таким образом, ваша модель в настоящий момент не сохраняется в базе данных.
Как упоминалось в ответе @UnholyRaven, проблема связана с тем, что транзакция не была зафиксирована в момент выполнения задачи.
Чтобы решить эту проблему, мы можем запланировать задачу на фиксацию транзакции, используя транзакцию Django.on_commit.
@receiver(post_save, sender=Document):
find_faces(sender, instance, created, **kwargs):
transaction.on_commit(lambda: find_faces_task.apply_async([instance.id]))
Кажется, что иногда сигнал бьет вашу скорость записи в БД! В качестве вредоносного решения вы можете запустить задачу сельдерея чуть позже, всего на несколько секунд.
Вот как это делается:
your_task.apply_async(
[document_id],
countdown=5 # this is the delay in seconds - you can adapt it accordingly
)
Дайте мне знать, если это работает для вашего случая!