Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to correctly implement recurring tasks with time/frequency set by user

Users subscribe to emails containing the last videos, but they also set when to get those emails.

Subscription(user_id, frequency, day, time, time_zone)

user_id  |  frequency  |  day    |  time   |  time_zone
1        |  daily      |  null   |  16:00  |  GMT
2        |  weekly     |  friday |  11:00  |  UTC
3        |  weekly     |  monday |  18:00  |  EST

How can we send the emails at the exact time and frequency chosen by users in their time zone without screwing up (like sending double emails or missing time)

The only frequencies are daily and weekly, if daily then the day is null.

I use redis as a database for this, let me know how to do this the right way!

like image 515
CodeOverload Avatar asked Feb 19 '13 18:02

CodeOverload


1 Answers

I'm going to expand on the answer of fmendez using the resque-scheduler gem.

First, let's create the worker that sends the emails

class SubscriptionWorker
  def self.perform(subscription_id)
    subscription = Subscription.find subscription_id

    # ....
    # handle sending emails here
    # ....

    # Make sure that you don't have duplicate workers
    Resque.remove_delayed(SubscriptionWorker, subscription_id)        

    # this actually calls this same worker but sets it up to work on the next
    # sending time of the subscription.  next_sending_time is a method that
    # we implement in the subscription model.
    Resque.enqueue_at(subscription.next_sending_time, SubscriptionWorker, subscription_id)
  end
end

In your subscription model, add a next_sending_time method to calculate the next time an email should be sent.

# subscription.rb
def next_sending_time
  parsed_time = Time.parse("#{time} #{time_zone}") + 1.day

  if frequency == 'daily'
    parsed_time
  else
    # this adds a day until the date matches the day in the subscription
    while parsed_time.strftime("%A").downcase != day.downcase
      parsed_time += 1.day
    end
  end
end
like image 114
jvnill Avatar answered Oct 11 '22 22:10

jvnill