1. Problembeschreibung: Wird on_commit() zu früh ausgeführt?
In einem kürzlich abgeschlossenen Django-Projekt habe ich transaction.on_commit()
verwendet, um
Celery-Tasks nach erfolgreichem Abschluss der Transaktion auszuführen.
Als ich die Logs überprüfte, stellte ich jedoch ein merkwürdiges Phänomen fest.
Obwohl Arbeiten wie
post.categories.add(...)
noch nicht abgeschlossen waren,
wurde der inon_commit()
befindliche Celery-Task bereits ausgeführt.
2. Überprüfung des Codes…
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()
Zu Beginn schien alles in Ordnung zu sein…
Aber genau dieses threading.Thread()
war die Falle.
3. Ursache: Transaktionen sind thread-lokal
Die Transaktionen von Django sind an den aktuellen Thread gebunden.
Das bedeutet, dass transaction.atomic()
und on_commit()
nur für die gerade in diesem Thread geöffneten Transaktionen gelten.
Eine
transaction.atomic()
, die in einem neuen Thread geöffnet wurde,
hat keinerlei Bezug zur Haupttransaktion.
4. Was letztendlich passiert ist
- Der Celery-Task wurde "nach dem Commit der Transaktion" ausgeführt, jedoch
- war diese Transaktion nur eine kleine Transaktion im Sub-Thread
- und
post.categories.add(...)
war noch nicht abgeschlossen.
Das bedeutet, dass Celery zu früh ausgeführt wurde,
was zu einem Verweis auf unvollständige Daten führte.
5. Lösung
So kann es einfach behoben werden:
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(...)
✅ Registrieren Sie on_commit()
einfach im Haupt-Thread.
6. Zusammenfassung
- Djangos Transaktionen sind thread-lokal
on_commit()
wird nur nach dem Commit der Transaktion im aktuellen Thread ausgeführt- Wenn in einem anderen Thread registriert, reagiert es nur auf die Transaktionen dieses Threads
Diese Erfahrung war eine sehr starke Lektion.
Ich habe erkannt, dass ich beim Arbeiten mit Transaktionen + Celery + Threads
wirklich vorsichtig sein muss.
Add a New Comment