1. Проблема: on_commit() выполняется слишком рано?
В недавнем проекте Django я использовал transaction.on_commit()
, чтобы
настроить выполнение задач Celery после успешного завершения транзакции.
Однако, изучая логи, я заметил странное явление.
Несмотря на то, что операции, подобные
post.categories.add(...)
, еще не завершены,
задача Celery, находящаяся внутриon_commit()
, уже была выполнена.
2. Изучив код…
def view():
post = Post.objects.create(...)
def post_process():
with transaction.atomic():
post.categories.add(...)
post.tags.add(...)
def run_async():
translate_post.delay(post.id)
transaction.on_commit(run_async)
threading.Thread(target=post_process).start()
Сначала не казалось, что есть какие-либо проблемы…
Но вот это threading.Thread()
оказалось ловушкой.
3. Причина: транзакция является локальной для потока
Транзакции в Django зависят от текущего потока.
То есть, transaction.atomic()
и on_commit()
применяются только к открытой транзакции в этом потоке.
Транзакция
transaction.atomic()
, открытая в новом потоке,
не имеет никакого отношения к основной транзакции.
4. Что в итоге произошло
- Задача Celery была выполнена "после коммита транзакции", но
- эта транзакция была всего лишь маленькой транзакцией в подчиненном потоке,
post.categories.add(...)
и другие операции еще не завершены
То есть, Celery выполнен слишком рано,
что привело к проблеме с доступом к неполным данным.
5. Решение
Просто исправьте это следующим образом:
def view():
post = Post.objects.create(...)
with transaction.atomic():
post.categories.add(...)
post.tags.add(...)
def run_async():
translate_post.delay(post.id)
transaction.on_commit(run_async)
🚫 threading.Thread(...)
✅ Просто зарегистрируйте on_commit()
в основном потоке
6. Итоги
- Транзакции Django локальны для потока
on_commit()
выполняется только после коммита транзакции в текущем потоке- Регистрация в других потоках будет реагировать только на транзакции этого потока
Этот опыт стал для меня мощным уроком.
Когда дело касается транзакций + Celery + потоков,
я понял, что нужно быть очень осторожным.
댓글이 없습니다.