1. Situation problématique : on_commit() s'exécute-t-il trop tôt ?

Récemment, dans un projet Django, j'ai configuré transaction.on_commit() pour exécuter
une tâche Celery après le succès de la transaction.

Cependant, en regardant les journaux, j'ai remarqué un phénomène étrange.

Une tâche Celery à l'intérieur de on_commit() s'est exécutée
alors que des opérations comme post.categories.add(...) n'étaient pas encore terminées.


2. Examinons le code…

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

Au début, il n'y avait pas de problème apparent…
mais ce threading.Thread() était le piège.


3. Cause : la transaction est locale au thread

Les transactions dans Django sont dépendantes du thread actuel.
C'est-à-dire que transaction.atomic() et on_commit() s'appliquent uniquement à la transaction ouverte dans ce thread.

Une transaction.atomic() ouverte dans un nouveau thread
n’a aucun lien avec la transaction principale.


4. Ce qui s'est passé en conséquence

  • La tâche Celery a été exécutée après "la validation de la transaction", mais
  • cette transaction était en fait une petite transaction dans un sous-thread
  • Les opérations telles que post.categories.add(...) étaient encore en cours

En d'autres termes, Celery s'est exécuté trop tôt,
référant à des données incomplètes.


5. Solution

Il suffit de corriger comme ceci :

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(...)
✅ Enregistrer simplement on_commit() dans le thread principal


6. Conclusion

  • Les transactions dans Django sont locales au thread
  • on_commit() s'exécute uniquement après la validation de la transaction dans le thread actuel
  • Si enregistrées dans d'autres threads, elle réagit seulement à la transaction de ce thread

Cette expérience a été une leçon très puissante.
Lorsque l'on utilise des transactions + Celery + threads,
on doit vraiment faire attention.