Django uruchamia zadania (prawdopodobnie) w dalekiej przyszłości

9

Załóżmy, że mam model Event. Chcę wysłać powiadomienie (e-mail, push, cokolwiek) do wszystkich zaproszonych użytkowników po zakończeniu wydarzenia. Coś w stylu:

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")

Teraz oczywiście najważniejszą częścią jest wywoływanie onEventElapsedza każdym razem timezone.now() >= event.end. Pamiętaj, że endmoże upłynąć kilka miesięcy od bieżącej daty.

Pomyślałem o dwóch podstawowych sposobach:

  1. Użyj okresowego cronzadania (powiedzmy co około pięć minut), które sprawdza, czy jakieś zdarzenia minęły w ciągu ostatnich pięciu minut i wykonuje moją metodę.

  2. Użyj celeryi zaplanuj, onEventElapsedużywając etaparametru do uruchomienia w przyszłości (w ramach savemetody modeli ).

Biorąc pod uwagę opcję 1, potencjalnym rozwiązaniem może być django-celery-beat. Jednak wydaje się nieco dziwne uruchamianie zadania w ustalonych odstępach czasu w celu wysyłania powiadomień. Ponadto wpadłem na (potencjalny) problem, który (prawdopodobnie) spowodowałby nie tak eleganckie rozwiązanie:

  • Czy sprawdzać co pięć minut zdarzenia, które upłynęły w ciągu ostatnich pięciu minut? wydaje się chwiejne, może niektóre wydarzenia zostały pominięte (lub inne otrzymały dwa powiadomienia wysyłane dwukrotnie?). Potencjalne obejście: dodaj pole boolowskie do modelu, który jest ustawiony na Truepo wysłaniu powiadomień.

Z drugiej strony, opcja 2 ma również swoje problemy:

  • Ręcznie zajmij się sytuacją, w której przenoszona jest data / godzina rozpoczęcia zdarzenia. Podczas korzystania celerynależy zapisać taskID(łatwe, ofc) i odwołać zadanie po zmianie dat i wydać nowe zadanie. Ale czytałem, że seler ma problemy (specyficzne dla projektu) podczas wykonywania zadań, które będą uruchamiane w przyszłości: Open Issue on github . Zdaję sobie sprawę z tego, jak to się dzieje i dlaczego rozwiązanie tego problemu jest trywialne.

Teraz natknąłem się na biblioteki, które potencjalnie mogłyby rozwiązać mój problem:

  • celery_longterm_scheduler (Ale czy to oznacza, że ​​nie mogę używać selera, tak jak wcześniej, ze względu na inną klasę Scheduler? To także wiąże się z możliwym użyciem django-celery-beat... Przy użyciu dowolnego z dwóch frameworków, czy nadal można kolejkować zadania (że są trochę dłużej, ale nie za kilka miesięcy?)
  • django-apscheduler , używa apscheduler. Nie udało mi się jednak znaleźć żadnych informacji o tym, jak poradzi sobie z zadaniami, które są uruchamiane w dalekiej przyszłości.

Czy jest jakiś poważny błąd w sposobie, w jaki do tego podchodzę? Cieszę się z wszelkich informacji, które możesz mieć.

Uwaga: Wiem, że prawdopodobnie jest to oparte na niektórych opiniach, jednak może brakuje mi bardzo podstawowej rzeczy, niezależnie od tego, co niektórzy mogą uznać za brzydką lub elegancką.

Hafnernuss
źródło
1
Powiedziałbym, że twoje podejście zależy od tego, jak szybko po upływie zdarzenia użytkownik końcowy potrzebuje powiadomienia. Miałem podobny problem, w którym użytkownik musiał wiedzieć tylko następnego dnia o każdym spotkaniu pominiętym poprzedniego dnia. Więc w tym przypadku uruchomiłem zadanie crona o północy i, jak sugerowałeś, miałem pole boolowskie do oznaczenia, czy powiadomienia zostały wysłane. Był to bardzo prosty i niedrogi obliczeniowo sposób.
Hayden Eastwood
1
Moim zdaniem odpowiedź dotyczy tego, ile zdarzeń należy wysłać. Jeśli masz do wysłania setki zdarzeń każdego dnia, nie ma znaczenia, jak daleko w przyszłości będzie jedno wydarzenie: korzystając z pierwszego rozwiązania (dostosowując czas powtarzania w zależności od potrzeb) możesz uruchomić zadanie odczytując zaktualizowane dane.
Dos
@HaydenEastwood Nie jest ważne, aby dana osoba otrzymała ją natychmiast, ale w ciągu 2-5 minut przed datą końcową powinno być dobrze. Więc zrobiłeś coś podobnego do mojej opcji 1?
Hafnernuss
1
@Hafnernuss Tak - Myślę, że zwykłe wywołanie cron z polem w bazie danych określającym, czy wiadomość została wysłana, byłoby dobrym rozwiązaniem dla twojej sprawy.
Hayden Eastwood
1
Dramatiq stosuje inne podejście niż Seler, gdy odrzuca zadania (nie wymaga pamięci od pracownika) i może działać w twoim przypadku, zobacz dramatiq.io/guide.html#scheduling-messages . Ale jak mówią - broker komunikatów nie jest DB - gdy potrzebujesz zaplanować długoterminowe wydarzenie, twoje pierwsze rozwiązanie jest lepsze. Możesz więc połączyć oba: umieść zdarzenia w MB, powiedzmy o 1 dzień, a po wygaśnięciu trafią do DB i zostaną wysłane przez cron.
frost-nzcr4

Odpowiedzi:

2

Robimy coś takiego w firmie, w której pracuję, a rozwiązanie jest dość proste.

Uderzaj w cron / seler, który jest uruchamiany co godzinę, aby sprawdzić, czy należy wysłać jakieś powiadomienie. Następnie wyślij te powiadomienia i oznacz je jako gotowe. W ten sposób, nawet jeśli czas powiadomienia upłynie o wiele lat, nadal będzie wysyłany. Korzystanie z ETA NIE jest sposobem na bardzo długi czas oczekiwania, pamięć podręczna / amqp może utracić dane.

Możesz skrócić interwał w zależności od potrzeb, ale upewnij się, że się nie pokrywają.

Jeśli jedna godzina to zbyt duża różnica czasu, co możesz zrobić, uruchom harmonogram co godzinę. Logika byłaby czymś takim

  1. uruchom zadanie (pozwala nazwać to zadanie harmonogramu) co godzinę, co spowoduje otrzymanie wszystkich powiadomień, które muszą zostać wysłane w ciągu następnej godziny (za pomocą selera)
  2. Zaplanuj te powiadomienia za pomocą Apply_async (eta) - będzie to faktyczne wysyłanie

Dzięki tej metodologii uzyskasz oba najlepsze światy (eta i beat)

ibaguio
źródło
1
Dziękuję Ci. Właśnie to zrobiłem!
Hafnernuss