Djangoは(おそらく)将来的にタスクを実行します


9

モデルがあるとしましょうEvent。イベントの経過後、招待されたすべてのユーザーに通知(電子メール、プッシュなど)を送信したい。以下の線に沿った何か:

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

さて、もちろん、重要な部分はonEventElapsedいつでも呼び出すことtimezone.now() >= event.endです。end現在の日付から数か月先になる可能性があることに注意してください。

私はこれを行う2つの基本的な方法について考えました。

  1. cron過去5分以内にイベントが経過したかどうかを確認してメソッドを実行する定期的なジョブ(たとえば、5分ごとなど)を使用します。

  2. 使用し、将来的に実行するパラメーターを使用celeryしてスケジュールonEventElapsedetaます(モデルsaveメソッド内)。

オプション1を検討すると、考えられる解決策は次のようになりますdjango-celery-beat。ただし、通知を送信するために一定の間隔でタスクを実行するのは少し奇妙に思えます。さらに、(おそらく)それほど洗練されていない解決策となる(潜在的な)問題を思いつきました。

  • 5分ごとに、直前の5分間に経過したイベントを確認しますか?不安定なようですが、いくつかのイベントが見落とされている可能性があります(または他のイベントで通知が2回送信されますか?)潜在的な回避策:True通知が送信されたときに設定されるブール型フィールドをモデルに追加します。

次に、オプション2にも問題があります。

  • イベント開始/終了日時が移動した場合は手動で対処してください。を使用する場合celerytaskID(easy、ofc)を保存し、日付が変更されたらタスクを取り消して、新しいタスクを発行する必要があります。しかし、私は、セロリに将来実行されるタスクを処理するときに(デザイン固有の)問題があることを読みました:githubのOpen Issue。これがどのように起こり、なぜそれが解決するのが簡単ではないのかがわかります。

今、私は潜在的に私の問題を解決できるいくつかのライブラリに出会いました:

  • celery_longterm_scheduler(しかし、これは、Schederyクラスが異なるため、以前のようにセロリを使用できないことを意味しますか?これは、django-celery-beat... の可能な使用法にもつながります... 2つのフレームワークのいずれかを使用すると、ジョブをキューに入れることができます(実行時間は少し長くなりますが、数か月先ではありませんか?)
  • django-apscheduler、を使用しapschedulerます。しかし、遠い将来に実行されるタスクを処理する方法についての情報は見つかりませんでした。

私がこれに取り組む方法に根本的な欠陥はありますか?あなたが持っているかもしれないどんな入力についても嬉しいです。

通知:これは何らかの意見に基づく可能性が高いことを知っていますが、醜いまたはエレガントなものと見なされるものに関係なく、見逃した非常に基本的なことがあるかもしれません。


1
あなたのアプローチは、経過したイベントの直後にエンドユーザーが通知を必要とするかどうかに依存すると思います。私は同様の問題を抱えていましたが、ユーザーが前日に見逃した予定があるため、翌日を知るだけで済みました。したがって、この場合、私は真夜中にcronジョブを実行し、あなたが提案したように、通知が送信されたかどうかをタグ付けするブールフィールドがありました。これは、非常にシンプルで、計算が安価な方法でした。
ヘイデンイーストウッド

1
私の意見では、答えは送信する必要があるイベントの数についてです。毎日何百ものイベントを送信する場合、将来の単一のイベントがどれほど遠いかは関係ありません。最初の解決策(ニーズに基づいて繰り返し時間を調整)を使用して、更新されたデータを読み取るタスクを実行できます。
Dos

@HaydenEastwood担当者がすぐに受け取ることは重要ではありませんが、終了日から2〜5分以内であれば問題ありません。あなたは私の意見1に似たようなことをしたのですか?
ハフネルヌス

1
@Hafnernussはい-メッセージが送信されたかどうかについてデータベース内のフィールドを使用する単純なcron呼び出しは、あなたのケースに適していると思います。
ヘイデンイーストウッド

1
セロリよりも別のaproachタスクをsheduling(作業者の空腹ではないメモリ)を使用Dramatiqとあなたのケースでの作品は、見ることができdramatiq.io/guide.html#scheduling-messagesを。しかし、彼らが言うように-メッセージブローカーはDBではありません-長期的なイベントの計画が必要な場合、最初のソリューションの方が優れています。したがって、両方を組み合わせることができます:イベントをMBで、たとえば1日ごとに入れます。有効期限までに、それらはDBに送られ、cron経由で送信されます。
frost-nzcr4

回答:


2

私は働いている会社でこのようなことをやっていて、解決策は非常に簡単です。

通知を送信する必要があるかどうかを確認するために1時間ごとに実行されるcron /セロリビートを用意します。次に、それらの通知を送信し、完了としてマークします。このようにして、通知時間が数年進んでも、送信されます。ETAを使用することは、非常に長い待機時間を費やす方法ではありません。キャッシュ/ amqpがデータを失う可能性があります。

必要に応じて間隔を短くできますが、重複しないようにしてください。

1時間の時間差が大きすぎる場合は、1時間ごとにスケジューラを実行します。ロジックは次のようなものになります

  1. 次の1時間に送信する必要のあるすべての通知を取得するタスク(このスケジューラータスクを呼び出します)を毎時間実行します(セロリビート経由)-
  2. apply_async(eta)を介してこれらの通知をスケジュールします-これは実際の送信になります

その方法論を使用すると、最高の世界(etaとbeat)の両方が得られます


1
ありがとうございました。それがまさに私がやったことです!
ハフネルヌス
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.