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.

  1. Webhook-Antwortzeitüberschreitung
  2. Nach der Erstellung des Post-Objekts dauert die Verarbeitung der ManyToMany-Felder categories und tags Zeit.
  3. Infolgedessen wird die Webhook-Antwort verzögert und DRF erkennt dies als Fehler.

  4. Keine Gewährleistung der Datenintegrität

  5. 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.
  6. Dies führt dazu, dass Celery mit unvollständigen Daten arbeitet.

  7. Empfang leerer Daten durch Celery nach on_commit()

  8. Obwohl die Datenintegrität mit on_commit() sichergestellt wurde und der Celery-Task geplant wurde, zeigt die Abfrage innerhalb des Tasks, wie tags.all(), weiterhin eine leere Liste an.
  9. Dies liegt daran, dass Celery die Leseoperationen an die Replica-DB sendete und es Verzögerungen bei der Master-Replica-Synchronisation gab.

Lösungsstrategie

  1. Sofortige Rückgabe der Antwort nach der Post-Erstellung
  2. Nur die Post-Instanz erstellen und sofort eine 202 Accepted-Antwort an DRF zurückgeben, um das Timeout-Problem zu umgehen.

  3. Nachfolgende Arbeiten in einem separaten Thread verarbeiten

  4. Verwendung von threading.Thread(), um die Verarbeitung der Beziehungen und den Celery-Aufruf vom Hauptfluss zu trennen.
  5. Entwicklung so, dass die Webhook-Antwort nicht verzögert wird.

  6. Transaktionsschutz innerhalb von post_process gewährleisten

  7. Die gesamte Verarbeitung der ManyToMany-Felder mit transaction.atomic() in einen Transaktionsblock einfügen.
  8. Nach Abschluss der Arbeiten den Celery-Task mit transaction.on_commit() planen.
  9. So wird gewährleistet, dass Celery erst nach Abschluss der Verarbeitung aktiviert wird.

  10. Innerhalb von Celery die Master-DB direkt für Abfragen festlegen

  11. 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

  1. DRF-Server → Django Webhook-Anfrage
  2. Django:
    • Post-Objekt erstellen und sofort Antwort zurückgeben
    • Nachfolgende Arbeiten werden in einem separaten Thread ausgeführt.
  3. Innerhalb des Threads:
    • atomic() zum Aufräumen der Beziehungen verwenden
    • on_commit() zur Planung des Celery-Tasks verwenden
  4. Celery:
    • Daten aus der Master-DB (default) abfragen

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.