Django führt Aufgaben (möglicherweise) in ferner Zukunft aus

9

Angenommen, ich habe ein Modell Event. Ich möchte allen eingeladenen Benutzern nach Ablauf des Ereignisses eine Benachrichtigung (E-Mail, Push, was auch immer) senden. Etwas in der Art von:

class Event(models.Model):
    start = models.DateTimeField(...)
    end = models.DateTimeField(...)
    invited = models.ManyToManyField(model=User)

    def onEventElapsed(self):
        for user in self.invited:
           my_notification_backend.sendMessage(target=user, message="Event has elapsed")

Jetzt ist es natürlich entscheidend, onEventElapsedimmer dann aufzurufen timezone.now() >= event.end. Beachten Sie, enddass Monate vom aktuellen Datum entfernt sein können.

Ich habe über zwei grundlegende Möglichkeiten nachgedacht:

  1. Verwenden Sie einen regelmäßigen cronJob (z. B. alle fünf Minuten oder so), der überprüft, ob in den letzten fünf Minuten Ereignisse aufgetreten sind, und meine Methode ausführt.

  2. Verwenden celeryund planen Sie onEventElapsedmit dem etaParameter, der in Zukunft ausgeführt werden soll (innerhalb der Modellmethode save).

In Anbetracht von Option 1 könnte eine mögliche Lösung sein django-celery-beat. Es erscheint jedoch etwas seltsam, eine Aufgabe in einem festgelegten Intervall zum Senden von Benachrichtigungen auszuführen. Außerdem habe ich ein (potenzielles) Problem gefunden, das (wahrscheinlich) zu einer nicht so eleganten Lösung führen würde:

  • Alle fünf Minuten nach Ereignissen suchen, die in den letzten fünf Minuten vergangen sind? scheint wackelig, vielleicht werden einige Ereignisse verpasst (oder andere erhalten ihre Benachrichtigungen zweimal gesendet?). Potenzielle Arbeitsumgebung: Fügen Sie dem Modell ein boolesches Feld hinzu, das nach dem TrueSenden von Benachrichtigungen festgelegt wird.

Andererseits hat Option 2 auch seine Probleme:

  • Kümmern Sie sich manuell um die Situation, in der die Start- / Endzeit eines Ereignisses verschoben wird. Bei der Verwendung celerymüsste man die taskID(easy, ofc) speichern und die Aufgabe widerrufen, sobald sich die Daten geändert haben, und eine neue Aufgabe ausgeben. Aber ich habe gelesen, dass Sellerie ( designspezifische ) Probleme hat, wenn es um Aufgaben geht, die in der Zukunft ausgeführt werden: Open Issue on github . Mir ist klar, wie das passiert und warum es alles andere als trivial zu lösen ist.

Jetzt bin ich auf einige Bibliotheken gestoßen, die möglicherweise mein Problem lösen könnten:

  • celery_longterm_scheduler ( Bedeutet dies jedoch, dass ich Sellerie aufgrund der unterschiedlichen Scheduler-Klasse nicht wie zuvor verwenden kann? Dies django-celery-beathängt auch mit der möglichen Verwendung von ... zusammen. Mit einem der beiden Frameworks ist es weiterhin möglich, Jobs in die Warteschlange zu stellen (das sind nur ein bisschen länger, aber nicht Monate entfernt?)
  • django-apscheduler , verwendet apscheduler. Ich konnte jedoch keine Informationen darüber finden, wie Aufgaben behandelt werden sollen, die in ferner Zukunft ausgeführt werden.

Gibt es einen fundamentalen Fehler in der Art und Weise, wie ich mich dem nähere? Ich freue mich über alle Eingaben, die Sie haben könnten.

Hinweis: Ich weiß, dass dies wahrscheinlich auf einer gewissen Meinung basiert, aber vielleicht gibt es eine sehr grundlegende Sache, die ich übersehen habe, unabhängig davon, was von manchen als hässlich oder elegant angesehen werden könnte.

Hafnernuss
quelle
1
Ich würde sagen, Ihr Ansatz hängt davon ab, wie schnell der Endbenutzer nach dem abgelaufenen Ereignis benachrichtigt werden muss. Ich hatte ein ähnliches Problem, bei dem der Benutzer nur am nächsten Tag wissen musste, ob ein Termin am Vortag verpasst wurde. In diesem Fall führte ich um Mitternacht einen Cron-Job aus und hatte, wie Sie vorgeschlagen haben, ein boolesches Feld, um zu kennzeichnen, ob Benachrichtigungen gesendet wurden. Es war eine sehr einfache und rechnerisch kostengünstige Möglichkeit, dies zu tun.
Hayden Eastwood
1
Meiner Meinung nach geht es bei der Antwort darum, wie viele Ereignisse Sie senden müssen. Wenn Sie täglich Hunderte von Ereignissen senden müssen, spielt es keine Rolle, wie weit in der Zukunft ein einzelnes Ereignis entfernt ist: Mit der ersten Lösung (Anpassen der Wiederholungszeit an Ihre Bedürfnisse) können Sie die Aufgabe ausführen und aktualisierte Daten lesen.
Dos
@ HaydenEastwood Es ist nicht entscheidend, dass die Person es sofort erhält, aber innerhalb von 2-5 Minuten innerhalb des Enddatums sollte alles in Ordnung sein. Also hast du etwas Ähnliches wie meine Meinung 1 gemacht?
Hafnernuss
1
@Hafnernuss Ja - Ich denke, ein einfacher Cron-Aufruf mit einem Feld in der Datenbank, ob eine Nachricht gesendet wurde, passt gut zu Ihrem Fall.
Hayden Eastwood
1
Dramatiq verwendet einen anderen Ansatz als Sellerie, wenn es darum geht, Aufgaben zu erledigen (nicht speicherhungrig für Arbeiter) und könnte in Ihrem Fall funktionieren, siehe dramatiq.io/guide.html#scheduling-messages . Aber wie sie sagen - Message Broker ist nicht DB -, wenn Sie die Planung eines langfristigen Ereignisses benötigen, ist Ihre erste Lösung besser. Sie können also beide kombinieren: Ereignisse in MB einfügen, z. B. bis zu einem Tag, und nach Ablauf werden sie an die DB gesendet und über cron gesendet.
frost-nzcr4

Antworten:

2

Wir machen so etwas in der Firma, für die ich arbeite, und die Lösung ist ganz einfach.

Lassen Sie einen Cronery / Beat-Beat ausführen, der stündlich ausgeführt wird, um zu überprüfen, ob eine Benachrichtigung gesendet werden muss. Senden Sie dann diese Benachrichtigungen und markieren Sie sie als erledigt. Auf diese Weise wird Ihre Benachrichtigungszeit auch dann gesendet, wenn sie Jahre im Voraus liegt. Die Verwendung von ETA ist NICHT der Weg für eine sehr lange Wartezeit. Ihr Cache / Amqp kann die Daten verlieren.

Sie können Ihr Intervall je nach Ihren Anforderungen reduzieren, aber stellen Sie sicher, dass sie sich nicht überlappen.

Wenn eine Stunde zu viel Zeitunterschied ist, können Sie jede Stunde einen Scheduler ausführen. Logik wäre so etwas wie

  1. Führen Sie stündlich eine Aufgabe aus (nennen wir diese Scheduler-Aufgabe), die alle Benachrichtigungen erhält, die in der nächsten Stunde gesendet werden müssen (über Sellerie-Beat).
  2. Planen Sie diese Benachrichtigungen über apply_async (eta) - dies ist das eigentliche Senden

Mit dieser Methode erhalten Sie beide besten Welten (eta und beat).

ibaguio
quelle
1
Vielen Dank. Genau das habe ich getan!
Hafnernuss