sebastian.martinez

Scheduling in Ruby 5

Posted by sebastian.martinez
on Monday, August 10

Scheduling tasks is something we all need to know to do, for it’s quite common in applications. Fetching feeds, indexing some data, processing files at a periodical time, that happens a lot. You are probably quite familiar then with the linux cron, if you had to deal with scheduling stuff in the past, but there is something you may not. Let me introduce you the Whenever gem. What is it? A simple gem to schedule tasks writing them in nice ruby syntax…just let the gem work it’s magic and deal with the cron.

In order to install it, you have to add first the github source, only if you never done it :
$ gem sources -a http://gems.github.com
$ sudo gem install javan-whenever
To get started, just place yourself in the app path and type
$ wheneverize . 
This will create an initial config/schedule.rb for you.

There you can nicely write tasks to run.

Some examples are:
every 3.hours do
    runner "MyModel.some_process" 
    rake "my:rake:task" 
    command "/usr/bin/my_great_command" 
  end

  every 1.day, :at => '4:30 am' do
    runner "MyModel.task_to_run_at_four_thirty_in_the_morning" 
  end

  every :hour do # Many shortcuts available: :hour, :day, :month, :year, :reboot
    runner "SomeModel.ladeeda" 
  end

  every :sunday, :at => '12pm' do # Use any day of the week or :weekend, :weekday
    runner "Task.do_something_great" 
  end
Another nice thing to do is integrate it with Capistrano.

 after "deploy:symlink", "deploy:update_crontab" 

  namespace :deploy do
    desc "Update the crontab file" 
    task :update_crontab, :roles => :db do
      run "cd #{release_path} && whenever --update-crontab #{application}" 
    end
  end

The official documentation provides this way to integrate it, but we found some little details, and the solution is here for you.

If you integrate it by the regular way, when making multiple deploys, it will configure several tasks to run in your cron file. Why? Because the path of the current application changed, and the gem uses the path (by adding a comment line) when updating the cron. What should we do then? Simple, change the run line with this:
run "cd #{current_path}; whenever -i #{current_path}/config/schedule.rb" 

What have we done? We used the -i option, which makes an update, but passing the comment we want, and make it not to change in every deploy.

Feel free to try it and leave your comments !!

|

If you have found this material to be useful, you might
consider recommending me on Working With Rails.

Comments

Leave a response

  1. Robby Colvin Robby ColvinAugust 10, 2009 @ 08:53 PM

    Ryan Bates from Railscasts uses this line:

    run “cd #{release_path} && whenever—update-crontab #{application}”

    What is the difference between the two?

  2. Jerod Santo Jerod SantoAugust 10, 2009 @ 09:03 PM

    @Robby

    The difference between running multiple commands separated by ; and && is that the command following && will only run if the first command completed successfully, while the command following a ; will run regardless of the status of the first command.

  3. Josh K Josh KAugust 11, 2009 @ 12:46 AM

    Hi,

    Make sure you include the environment as well, otherwise when you deploy your app on a staging server the wheneverized crontab will be scoped to production.

    run “cd #{current_path} && whenever—update-crontab #{application} -s environment=#{rails_env}”

    Josh

  4. Javan Makhmali Javan MakhmaliAugust 11, 2009 @ 04:49 AM

    If you use ”#{current_path}/config/schedule.rb” as your identifier, it will change with each deploy because current_path is the path to the newest release. This will cause duplicate cron jobs!

    You need to use a non changing string for your identifier to prevent this. That’s why my example uses ”#{application}”, but any string will work.

    Also, there’s no point in running the the whenever command if the cd command fails which is why I prefer to use &&.

  5. Sebastian Martinez Sebastian MartinezAugust 11, 2009 @ 04:28 PM

    We found out that even when using the ‘update-crontab’ option with ”#{application}” it does not override the existing cron job, but writes a new one. This is because we use capistrano, and it creates symbolic links for the new deploys, and the gem considers it as a new application. That’s why we pass the identifier we want, and just override the old one.

Comment