Buy @ Amazon

Search This Blog

February 9, 2011

No more cron jobs. Schedule jobs through rufus-scheduler in rails.

In the project that I'm working currently, I was asked to look out for ways to replace the scheduling jobs through crontab in favour of some scheduler that can control/schedule run the tasks at regular interval as long as the application is up and running. Thanks to my comrade in Dev-Ops team (Ranjib Dey) who  directing my attention to rufus-scheduler.

rufus-scheduler - Things can't be simpler! Let us see how simple it is in action with a sample code 

First step to usage is install the gem; so, find below the command for the same. You run it from the root of your application folder and I assume that you use rvm (if not, prefix the command with - sudo )

> gem install rufus-scheduler

Now, when the rails application gets kick-started, it reads all .rb files under config/initializers directory as part of its initialization process. This step in rails start-up, is what we'll take advantage of. So, let us create a new file, say my_tasks_scheduler.rb, in config/initializers directory.

<my_rails_app>

   | -- app

   | -- config

          | -- initializers

                    | -- my_tasks_scheduler.rb

   | -- db

   | -- ...

   | -- ...

   | -- lib

          | -- tasks

                    | -- tempfile.rake

The contents of the file my_tasks_scheduler.rb would be like:


require 'rufus/scheduler'  # Need this to make use of Rufus::Scheduler

require 'rubygems'   # Need this to make use of any Gem, in our case it is rufus-scheduler

require 'rake'     # Need this to make use of Rake::Task

#  'tempfile.rake' is the rake file I had defined under lib/tasks directory in my rails application

load File.join(RAILS_ROOT'lib''tasks''tempfile.rake')
# 'misc.rake' is the rake file defined under railties/lib/tasks directory of the installed rails version that your application makes use of.     

# 'misc.rake' is not required to be loaded if none of your rake tasks that you invoke are dependent on :environment task, directly or indirectly
# If this file is not loaded, you would see an error message like "Don't know how to build task :environment"
load File.join('lib', 'tasks', 'misc.rake')  

# OPTION 1: If you want to run the scheduler as part of your very own rails process then you may adopt this option

temp_files_cleaning_scheduler = Rufus::Scheduler.start_new
# Making use of the syntax used in Crontab

temp_files_cleaning_scheduler.cron '*/1 * * * *' do  

  task = Rake::Task["tempfile:delete_all"

  task.reenable  # If only you do this, will your rake task run the next time you invoke it.

  task.invoke
end


#OPTION 2: If for whatever reason (say, for performance reasons) you want to run your rake tasks as seperate process independent of your rails application, then you may adopt this option


temp_files_cleaning_scheduler = Rufus::Scheduler.start_new  
#Making use of a more intutive approach, instead of Cron syntax

temp_files_cleaning_scheduler.every "1m" do  
    # Programmatically, kick-starting the rake task from the command line

   system "rake tempfile:delete_all RAILS_ENV=#{RAILS.env}"  

end


And that is it, you are all set for action... 


Additional references that are worth reading are mentioned below:



February 1, 2011

Externalizing ALL application specific configurations to a file outside of Rails ROOT

It has been quite a while since I wanted to externalize all application specific configurations to a file outside of Rails ROOT, so that when my application is deployed in production, it picks up the configurations from a file in some specific location outside of Rails environment. The sysadmin makes changes to this file as is required and he alone knows or is eligible to know all these configuration details and certainly not the development team.

The application that I'm having in my mind is an internal rails application that gets deployed in an internal hosting environment instead of hosting it in a external environment like Heroku, etc.

Follow through the self-explanatory code snippets to get an understanding of how I achieve it in simple way. For the noobs, the quick explanation would be that the code snippet in environment.rb file reads the YAML file from system to copy all key value pairs to Rails' ENV hash. This ENV hash is available everywhere while/on/after application load.  

File: config/environment.rb
# Mechanism to load all application related configurations
$CONFIG_FILE = "/var/myapp/config/jsconfig.yml"
require 'yaml'
APP_CONFIG = YAML.load_file($CONFIG_FILE)
APP_CONFIG.each do |key, value|
  ENV[key] = value
end

#3rd Party Server's (that my application is using) Configurations here...
3RD_PARTY_SERVER_URL = ENV['3rd_party_webservice_endpoint_url']
3RD_PARTY_SERVER_CREDENTIALS = {:username => ENV['3rd_party_server_username'], :password=> ENV['3rd_party_server_password']}


File: /var/myapp/config/jsconfig.yml
3rd_party_webservice_endpoint_url: url
3rd_party_server_username: username
3rd_party_server_password: password
myapp_db_url: jdbc:oracle:thin:@localhost:1521:XE
myapp_db_username: kartz
myapp_db_password: rails_savvy


File: /var/myapp/config/database.yml
development:
  adapter: oracle_enhanced
  url: <%= ENV['myapp_db_url'] %>
  username: <%= ENV['myapp_db_username'] %>
  password: <%= ENV['myapp_db_password'] %>
  encoding: utf8

test:
  adapter: oracle_enhanced
  url: <%= ENV['myapp_db_url'] %>
  username: <%= ENV['myapp_db_username'] %>
  password: <%= ENV['myapp_db_password'] %>
  encoding: utf8

production:
  adapter: oracle_enhanced
  url: <%= ENV['myapp_db_url'] %>
  username: <%= ENV['myapp_db_username'] %>
  password: <%= ENV['myapp_db_password'] %>
  encoding: utf8



I'm a wannabe reader for the book,