Django menjalankan tugas (mungkin) di masa depan yang jauh

9

Misalkan saya punya model Event. Saya ingin mengirim pemberitahuan (email, push, apa pun) ke semua pengguna yang diundang setelah acara berlalu. Sesuatu di sepanjang garis:

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

Sekarang, tentu saja, bagian penting adalah meminta onEventElapsedkapan saja timezone.now() >= event.end. Perlu diingat, endbisa beberapa bulan lagi dari tanggal saat ini.

Saya telah memikirkan dua cara dasar untuk melakukan ini:

  1. Gunakan cronpekerjaan berkala (misalnya, setiap lima menit atau lebih) yang memeriksa apakah ada peristiwa yang berlalu dalam lima menit terakhir dan mengeksekusi metode saya.

  2. Gunakan celerydan jadwalkan onEventElapsedmenggunakan etaparameter yang akan dijalankan di masa depan (dalam savemetode model ).

Mempertimbangkan opsi 1, solusi potensial bisa jadi django-celery-beat. Namun, sepertinya agak aneh untuk menjalankan tugas pada interval tetap untuk mengirim pemberitahuan. Selain itu saya menemukan masalah (potensial) yang (kemungkinan) akan menghasilkan solusi yang tidak terlalu elegan:

  • Periksa setiap lima menit untuk acara yang telah berlalu dalam lima menit sebelumnya? tampaknya goyah, mungkin beberapa acara tidak terjawab (atau yang lain menerima pemberitahuan dua kali?). Workaroung potensial: tambahkan bidang boolean ke model yang disetel Truesetelah pemberitahuan dikirimkan.

Kemudian lagi, opsi 2 juga memiliki masalah:

  • Jaga situasi secara manual saat acara dimulai / berakhir datetime dipindahkan. Saat menggunakan celery, seseorang harus menyimpan taskID(mudah, ofc) dan mencabut tugas begitu tanggal telah berubah dan mengeluarkan tugas baru. Tetapi saya telah membaca, bahwa seledri memiliki masalah (khusus desain) ketika berhadapan dengan tugas-tugas yang dijalankan di masa depan: Masalah Terbuka pada github . Saya menyadari bagaimana ini terjadi dan mengapa itu semua hanya sepele untuk dipecahkan.

Sekarang, saya telah menemukan beberapa perpustakaan yang berpotensi memecahkan masalah saya:

  • celery_longterm_scheduler (Tapi apakah ini berarti saya tidak dapat menggunakan seledri seperti yang akan saya lakukan sebelumnya, karena kelas Penjadwal differend? Ini juga terkait dengan kemungkinan penggunaan django-celery-beat... Menggunakan salah satu dari dua kerangka kerja, apakah masih mungkin untuk mengantre pekerjaan (yang hanya berjalan sedikit lebih lama tetapi tidak beberapa bulan lagi?)
  • django-apscheduler , menggunakan apscheduler. Namun, saya tidak dapat menemukan informasi tentang cara menangani tugas yang dijalankan di masa depan yang jauh.

Apakah ada kelemahan mendasar dengan cara saya mendekati ini? Saya senang atas masukan yang mungkin Anda miliki.

Perhatikan: Saya tahu ini kemungkinan didasarkan pada pendapat tertentu, namun, mungkin ada hal yang sangat mendasar yang saya lewatkan, terlepas dari apa yang dapat dianggap oleh beberapa orang sebagai jelek atau elegan.

Hafnernuss
sumber
1
Saya akan mengatakan pendekatan Anda tergantung pada seberapa cepat setelah peristiwa yang berlalu pengguna akhir perlu pemberitahuan. Saya memiliki masalah serupa di mana pengguna hanya perlu mengetahui hari berikutnya untuk setiap janji yang terlewatkan pada hari sebelumnya. Jadi dalam hal ini saya menjalankan pekerjaan cron di tengah malam, dan, seperti yang Anda sarankan, memiliki bidang boolean untuk menandai apakah pemberitahuan telah dikirim. Itu cara yang sangat sederhana dan murah secara komputasional untuk melakukannya.
Hayden Eastwood
1
Menurut pendapat saya, jawabannya adalah tentang berapa banyak acara yang perlu Anda kirim. Jika Anda memiliki ratusan acara untuk dikirim setiap hari, tidak masalah seberapa jauh di masa depan adalah satu peristiwa: menggunakan solusi pertama (mengadaptasi waktu pengulangan berdasarkan kebutuhan Anda), Anda dapat menjalankan tugas membaca data yang diperbarui.
Dos
@HaydenEastwood Tidaklah penting bagi orang itu untuk segera menerimanya, tetapi dalam 2-5 menit dalam tanggal akhir harus baik-baik saja. Jadi Anda melakukan sesuatu yang mirip dengan opion 1 saya?
Hafnernuss
1
@Hafnernuss Ya - Saya pikir panggilan cron sederhana dengan bidang dalam database untuk apakah pesan dikirim akan cocok untuk kasus Anda.
Hayden Eastwood
1
Dramatiq menggunakan pendekatan lain selain Seledri saat menjadwalkan tugas (bukan ingatan yang lapar pada pekerja) dan dapat bekerja dalam kasus Anda, lihat dramatiq.io/guide.html#scheduling-messages . Tetapi seperti yang mereka katakan - broker pesan bukan DB - ketika Anda membutuhkan perencanaan acara jangka panjang, solusi pertama Anda lebih baik. Jadi, Anda dapat menggabungkan keduanya: letakkan acara dalam MB, katakanlah dalam 1 hari, dan dengan kedaluwarsa mereka akan masuk ke DB dan akan dikirim melalui cron.
frost-nzcr4

Jawaban:

2

Kami melakukan hal seperti ini di perusahaan tempat saya bekerja, dan solusinya cukup sederhana.

Memiliki irama cron / seledri yang berjalan setiap jam untuk memeriksa apakah ada pemberitahuan yang perlu dikirim. Kemudian kirim pemberitahuan itu dan tandai sebagai selesai. Dengan cara ini, bahkan jika waktu pemberitahuan Anda bertahun-tahun ke depan, itu masih akan dikirim. Menggunakan ETA BUKAN cara untuk menunggu waktu yang sangat lama, cache / amqp Anda mungkin kehilangan data.

Anda dapat mengurangi interval Anda tergantung pada kebutuhan Anda, tetapi pastikan mereka tidak tumpang tindih.

Jika satu jam terlalu besar dari perbedaan waktu, maka yang dapat Anda lakukan adalah, jalankan penjadwal setiap jam. Logika akan seperti itu

  1. menjalankan tugas (sebut saja tugas penjadwal ini) setiap jam yang mendapatkan semua pemberitahuan yang perlu dikirim dalam satu jam berikutnya (melalui ketukan seledri) -
  2. Jadwalkan pemberitahuan itu melalui apply_async (eta) - ini akan menjadi pengiriman yang sebenarnya

Menggunakan metodologi itu akan membuat Anda berdua dunia terbaik (eta dan beat)

ibaguio
sumber
1
Terima kasih. Itulah tepatnya yang telah saya lakukan!
Hafnernuss