I have many Appointment
models that start at various times of the day, either at :00, :15, :30, :45. I'd like to trigger code to send a reminder 1 hour before the event starts. What would be the best way to use a background worker to trigger this? I'm using the clockwork gem as well so I can schedule Sidekiq workers.
问题:
回答1:
The clockwork
gem is for fixed schedule jobs (a replacement for cron). You'll want to use ActionMailer.delay_until
that comes with sidekiq
:
class Appointment
after_create :queue_reminder
def queue_reminder
MyMailer.delay_until(event_time - 1.hour).appointment_reminder(id)
end
end
See sidekiq
docs here: https://github.com/mperham/sidekiq/wiki/Delayed-Extensions
As shock_one mentioned, if you update an appointment with a new date, you'll have to requeue a reminder and cancel the old one. You'll also want to cancel a job if an Appointment
is destroyed.
For that, I'd advise you use sidekiq-status
, and a reminder_job_id
column. Your Appointment
model would then look something like:
class Appointment
before_save :queue_reminder, if: :event_time_changed?
after_destroy :cancel_reminder, if: :reminder_job_id?
def queue_reminder
cancel_reminder if reminder_job_id
self.reminder_job_id = MyMailer.delay_until(event_time - 1.hour)
.appointment_reminder(id)
end
def cancel_reminder
Sidekiq::Status.cancel reminder_job_id
end
end
回答2:
First you have to decide who will be responsible for scheduling. You can either have a background process, which runs every fifteen minutes, or you can make the model schedule the event at some point of its life - probably after creation.
The former solution is simpler to understand and maintain, but the background process will have many useless empty loops.
On the other hand, the latter solution is more precise. It will be executed exactly as many times as required. However, it may have difficulties if the model changes: for example, the time has been modified after it was created. You'd need to cancel the previous event somehow, and schedule a new one.