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 commepost.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.
Add a New Comment