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 + потоков,
я понял, что нужно быть очень осторожным.