1. Situación: ¿on_commit() se ejecuta demasiado rápido?

Recientemente, en un proyecto de Django, he configurado transaction.on_commit() para que
los trabajos de Celery se ejecuten después de que la transacción haya sido exitosa.

Sin embargo, al revisar los registros, noté un fenómeno extraño.

Aún no se habían completado tareas como post.categories.add(...) pero,
la tarea de Celery dentro de on_commit() se estaba ejecutando.


2. Al revisar el código…

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()

Al principio no parecía haber ningún problema…
pero esta threading.Thread() era la trampa.


3. Causa: Las transacciones son locales del hilo

Las transacciones de Django son dependientes del hilo actual.
Es decir, transaction.atomic() y on_commit() se aplican solo a la transacción abierta en este hilo.

La transaction.atomic() abierta en un hilo nuevo
no tiene relación alguna con la transacción principal.


4. Lo que sucedió como resultado

  • La tarea de Celery se ejecutó "después de la confirmación de la transacción", pero
  • esa transacción era solo una pequeña transacción dentro de un subhilo
  • y tareas como post.categories.add(...) aún estaban en proceso

Es decir, Celery se ejecutó demasiado rápido para lo que se pretendía,
y surgió el problema de acceder a datos incompletos.


5. Solución

Simplemente se puede corregir de esta manera:

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(...)
✅ Solo registrar on_commit() en el hilo principal


6. Resumen

  • Las transacciones de Django son locales del hilo
  • on_commit() se ejecuta solo después de la confirmación de la transacción del hilo actual
  • Si se registra en otro hilo, solo responderá a la transacción de ese hilo

Esta experiencia ha sido una lección poderosa.
Me di cuenta de que cuando se usan transacciones + Celery + hilos,
es crucial tener mucho cuidado.