22

I have a Rails app deployed on Heroku with the Heroku scheduler add-on successfully working for daily jobs.

Now I want a weekly job, but the scheduler add-on does not allow me a weekly option.

Any suggestions on how I could accomplish this:

  • I've tried using rufus-scheduler in the past but it caused me some problems, so that's not an option. See here for detail.
  • I'm thinking of something along the lines of checking for current day within a daily job. Has anyone tried it and have feedback or know of issues with the approach?
  • Other ideas much appreciated.
Community
  • 1
  • 1
T C
  • 1,337
  • 1
  • 12
  • 21

5 Answers5

38

One approach would be the one of your 2nd bullet point:

activate the Heroku cron add-on, and add a cron.rake task in app/lib/tasks

Activate the Heroku scheduler add-on, and add a scheduler.rake task in app/lib/tasks

task :your_weekly_task=> :environment do
  if Time.now.friday? # previous answer: Date.today.wday == 5
    #do something
  end
end

You even have the luxury of defining the day you want your task to run ;o) (5 is for Friday)

EDIT: by the way, Cron is deprecated and Heroku recommends switching to their Scheduler add-on. This doesn't change the approach for a weekly job though.

EDIT2: adjustments to my answer based on feedback from sunkencity.

Pierre
  • 8,368
  • 4
  • 34
  • 50
  • 3
    The cron add-on is deprecated and will be discontinued in favor of the scheduler one. That said, the approach you suggest is sound, but it could be formulated better with activesupport: Time.now.friday? – sunkencity Mar 23 '12 at 06:59
  • I'm cringing at the idea of hard-coding the day, so I'll leave the question open for now for a better alternative. – T C Mar 23 '12 at 09:11
  • 1
    Btw, what I do is that I use a heroku config var for the day of the week to run. Then you have to control the time of day via the UI. It's really *not* great. Heroku team, if you are out there and read this, please improve scheduler. It doesn't have to have all of the flexibility of cron. But, at least make it where you can schedule weekly and monthly jobs. – David S Sep 10 '12 at 16:29
  • One little improvement I came up with is to use some metaprogramming to make tasks for each day, e.g. your_weekly_task_monday, your_weekly_task_tuesday etc. Then you can add the task you want in heroku scheduler depending on which day you want it to run, so you don't put this configuration in the app. – mrbrdo Jan 11 '13 at 19:05
  • 1
    if you want to send a report on the 1st day of the month (or on any day of the month), you can do this: if Time.now.day == 1 – nfriend21 Jan 18 '16 at 14:18
10

An alternate option using only shell code. Setup the Heroku scheduler hourly, and do a comparison against the date command:

# setting the schedular to run hourly at *:30 is equivalent to the 
# crondate: 30 8 * * 1
if [ "$(date +%H)" = 08 ] && [ "$(date +%d)" = 01 ]; then YOUR_COMMAND ; fi 

I've used this code to mimic cron in my local time zone:

nz_hour="$(TZ=NZ date +%H)" ; nz_day="$(TZ=NZ date +%d)" ; if [ "$nz_hour" = 08 ] && [ "$nz_day" = 01 ]; then YOUR_COMMAND ; fi 
Rob Ramsay
  • 121
  • 1
  • 4
9

It's not ideal, but I've taken to adding a RUN_IF environment variable to rake tasks run through heroku:scheduler which lets me weekly and monthly schedules for jobs.

# lib/tasks/scheduler.rake
def run?
  eval ENV.fetch('RUN_IF', 'true')
end

def scheduled
  if run?
    yield
  else
    puts "RUN_IF #{ENV['RUN_IF'].inspect} eval'd to false: aborting job."
  end
end

# lib/tasks/job.rake
task :job do
  scheduled do
    # ...
  end
end

If a rake task is run without a RUN_IF variable it will run. Otherwise, the job will be aborted unless the value of RUN_IF evals to a falsey value.

$ rake job                              # => runs always
$ rake job RUN_IF='Date.today.monday?'  # => only runs on Mondays
$ rake job RUN_IF='Date.today.day == 1' # => only runs on the 1st of the month
$ rake job RUN_IF='false'               # => never runs (not practical, just demonstration)

Similar to other ideas above, but I prefer moving the scheduling details out of the application code.

John Parker
  • 91
  • 1
  • 1
1

As discussed over here, and using the above logic from Rob, here are the bash scripts broken down by a day of the week, once a month, and on a specific date.

Run a task every Monday:

if [ "$(date +%u)" = 1 ]; then MY_COMMAND; fi

Run a task every 1st day in a month:

if [ "$(date +%d)" = 01 ]; then MY_COMMAND; fi

You could also run a job every year on December 24th:

if [ "$(date +%m)" = 12 ] && [ "$(date +%d)" = 24 ]; then MY_COMMAND; fi
Jordan
  • 1,879
  • 3
  • 19
  • 25
0

If you don't want or cannot do this with Rake, another option is to do this with Ruby from bash:

#!/bin/bash

cmd="echo your schedule job here"

# Only run on even days
ruby -e 'if Time.now.utc.day.even?; puts "Not today!"; exit 1; end' && $cmd "$@"

This works even for non-ruby projects (as in my case: Clojure).

Jeroen van Dijk
  • 1,029
  • 10
  • 16