Problemübersicht
Der DRF-Server sendet eine Webhook-POST-Anfrage an den Django-App-Server,
und beim Empfangen dieser Anfrage trat beim Erstellen einer Instanz des Post-Modells folgendes Problem auf.
- Webhook-Antwortzeitüberschreitung
- Nach der Erstellung des Post-Objekts dauert die Verarbeitung der
ManyToMany
-Feldercategories
undtags
Zeit. -
Infolgedessen wird die Webhook-Antwort verzögert und DRF erkennt dies als Fehler.
-
Keine Gewährleistung der Datenintegrität
- Wenn der Celery-Task direkt nach der Verarbeitung aufgerufen wird, wird
tags.add(...)
,categories.add(...)
zu einem Zeitpunkt aufgerufen, an dem die Verarbeitung noch nicht abgeschlossen ist. -
Dies führt dazu, dass Celery mit unvollständigen Daten arbeitet.
-
Empfang leerer Daten durch Celery nach on_commit()
- Obwohl die Datenintegrität mit
on_commit()
sichergestellt wurde und der Celery-Task geplant wurde, zeigt die Abfrage innerhalb des Tasks, wietags.all()
, weiterhin eine leere Liste an. - Dies liegt daran, dass Celery die Leseoperationen an die Replica-DB sendete und es Verzögerungen bei der Master-Replica-Synchronisation gab.
Lösungsstrategie
- Sofortige Rückgabe der Antwort nach der Post-Erstellung
-
Nur die Post-Instanz erstellen und sofort eine
202 Accepted
-Antwort an DRF zurückgeben, um das Timeout-Problem zu umgehen. -
Nachfolgende Arbeiten in einem separaten Thread verarbeiten
- Verwendung von
threading.Thread()
, um die Verarbeitung der Beziehungen und den Celery-Aufruf vom Hauptfluss zu trennen. -
Entwicklung so, dass die Webhook-Antwort nicht verzögert wird.
-
Transaktionsschutz innerhalb von post_process gewährleisten
- Die gesamte Verarbeitung der
ManyToMany
-Felder mittransaction.atomic()
in einen Transaktionsblock einfügen. - Nach Abschluss der Arbeiten den Celery-Task mit
transaction.on_commit()
planen. -
So wird gewährleistet, dass Celery erst nach Abschluss der Verarbeitung aktiviert wird.
-
Innerhalb von Celery die Master-DB direkt für Abfragen festlegen
- Um Integritätsprobleme aufgrund der Verzögerung der Replica zu vermeiden,
using('default')
explizit innerhalb des Celery-Tasks verwenden.
post = Post.objects.using('default').get(id=post_id)
tags = post.tags.using('default').all()
categories = post.categories.using('default').all()
Alternativ kann auch ein DB-Router verwendet werden, um sicherzustellen, dass Celery-Anfragen immer die Master-DB abfragen.
Zusammenfassung der endgültigen Struktur
- DRF-Server → Django Webhook-Anfrage
- Django:
- Post-Objekt erstellen und sofort Antwort zurückgeben
- Nachfolgende Arbeiten werden in einem separaten Thread ausgeführt.
- Innerhalb des Threads:
atomic()
zum Aufräumen der Beziehungen verwendenon_commit()
zur Planung des Celery-Tasks verwenden
- Celery:
- Daten aus der Master-DB (
default
) abfragen
- Daten aus der Master-DB (
Fazit
Obwohl alle Komponenten für sich genommen korrekt sind,
war es notwendig, nicht nur den Code zu optimieren, sondern auch einSystemdesign zu entwickeln, das den Datenfluss und das Timing sowie die Verzögerungen der DB-Replikate berücksichtigt, um die in einer verteilten Architektur auftretenden Latenz- und Datenintegritätsprobleme zu lösen.
Add a New Comment