Best way to trigger an hour before event starts

2019-08-13 20:44发布

问题:

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.