the OpenWFEru scheduler

update : The scheduler has moved. It has become the rufus-scheduler gem.

You can install it with :

    sudo gem install rufus-scheduler

Backward compatibility has been preserved. This is still ok :

    require 'rubygems'
    require 'openwfe/util/scheduler'

    scheduler = OpenWFE::Scheduler.new
    scheduler.start

    # ...

Though the new gem will simply require :

    require 'rubygems'
    require 'rufus/scheduler'

    scheduler = Rufus::Scheduler.new
    scheduler.start

    # ...

This ‘move’ is part of a move of some of the OpenWFEru subprojects from OpenWFEru to Rufus


OpenWFEru being a workflow / business process engine needs a scheduler for tasks to execute later (at) and tasks to execute repetitively (cron).

This scheduler isn’t OpenWFE focused so this page details how to use it outside of the OpenWFEru context.

Like the rest of OpenWFEru, it’s licensed under the BSD license.

The OpenWFEru scheduler was presented in Ilya Grigorik’s blog post about Scheduling tasks in Ruby / Rails.

independent (own gem)

You don’t need the full OpenWFEru suite to use the scheduler. Only those 2 files are required :


    lib/openwfe/util/otime.rb
    lib/openwfe/util/scheduler.rb

They are all located in the util subdir of OpenWFEru.

The “openwferu-scheduler” is available in its own gem, just do

    [sudo] gem install openwferu-scheduler

to get it. You can then use it straightaway like in


    require 'rubygems'
    require 'openwfe/util/scheduler'

    include OpenWFE

    scheduler = Scheduler.new
    scheduler.start

    scheduler.schedule_in("1h12m") do
        puts "...taking out the garbage (as requested one hour and twelve minutes ago)"
    end

    # ...

rdoc

The rdocumentation for the scheduler. It is the most up to date source of information about the scheduler.

“at” jobs

“at” jobs are jobs to be executed in the future once (and only once). They are registered via the schedule_in() and the schedule_at() methods of the Scheduler.

The basic idea is :

    scheduler.schedule_in("1h12m") do
        puts "one hour and twelve minutes later..."
    end

    scheduler.schedule_at("Fri Jul 20 09:07:02 +0900 2007") do
        puts "on this beautiful July day..."
    end


    require 'time'
        #
        # so that the Time class now features the parse() methods

    scheduler.schedule_at Time.parse('18 July 2007 14:09:00') do
        puts "yet another beautiful July day in the current time zone..."
    end

Note that jobs meant to be scheduled at past dates will be triggered immediately but not scheduled. There is a :discard_past parameter to prevent the immediate triggering.

    jobid = scheduler.schedule_at yesterday do
        puts "yesterday or today ?"
    end
        #
        # the message will be displayed immediately. 'jobid' will yield nil.

    jobid = scheduler.schedule_at(yesterday, :discard_past => true) do
        puts "yesterday or today ?"
    end
        #
        # the message will not be displayed. 'jobid' will yield nil.

An example of “at” jobs (mostly schedule_in()).

“cron” jobs

Cron jobs have been around since as long as unixes have. The cron aspect of our ruby scheduler follows thus the established practice

    scheduler.schedule("1-60/3 * * * mon-fri") do
        puts "...popping up every 3 minutes on weekdays..."
    end

A small example

You can type “man 5 crontab” on your favourite unix box or look there to learn more about the cron format.

Since OpenWFEru 0.9.16, cron jobs can be specified at the second level.

    scheduler.schedule("7 1-60/3 * * * *") do
        puts "...popping up every 3 minutes, at the seventh second ..."
    end

The first column is dedicated to seconds and optional, it behaves like the other columns, accepting a single value, or list or ranges of values.

    scheduler.schedule("7,27 * * * * *") do
        puts "...popping up at the seventh and the twenty-seventh seconds..."
    end

“every” jobs

For something more relaxed than cron :

    scheduler.schedule_every("12m45s") do
        puts "...popping up every 12 minutes and 45 seconds..."
    end

Since OpenWFEru 0.9.17, ‘every’ jobs can reschedule/unschedule themselves.

Two examples in an “internet electronic mail” context :

    schedule.schedule_every "5h" do |job_id, at, params|
    
        mails = $inbox.fetch_mails
        mails.each { |m| $inbox.mark_as_spam(m) if is_spam(m) }
    
        params[:every] = if mails.size > 100
            "1h" # lots of spam, check every hour
        else
            "5h" # normal schedule, every 5 hours
        end
    end
    schedule.schedule_every "10s" do |job_id, at, params|
        #
        # polls every 10 seconds until a mail arrives
    
        $mail = $inbox.fetch_last_mail
    
        params[:dont_reschedule] = true if $mail
    end

unscheduling jobs

The scheduling methods each return a job identifier that can be used to unschedule the jobs :

    job_id = scheduler.schedule_every("12m45s") do
        puts "...popping up every 12 minutes and 45 seconds..."
    end

    # a bit later...

    scheduler.unschedule(job_id)

stopping the scheduler

To stop the scheduler :

    scheduler.stop

scheduler precision

By default, the scheduler checks for jobs to trigger 4 times per second. This is configurable at instantiation.

    scheduler = OpenWFE::Scheduler.new(:scheduler_precision => 0.500)
    scheduler.start
        #
        # instatiates a scheduler that checks its jobs twice per second
        # (the default is 4 times per second (0.250))

    puts "scheduler precision is #{scheduler.precision}"

tagging jobs

Since OpenWFEru 0.9.16, it’s possible to tag jobs at schedule time.

    scheduler.schedule_in "2h", :tags => "backup" do
        init_backup_sequence()
    end
    scheduler.schedule "0 24 * * *", :tags => [ "old_day", "backup" ] do
        init_backup_sequence()
    end

    scheduler.schedule "0 09 * * *", :tags => "new_day" do
        unlock_building_doors()
    end

    # fetching the jobs with the backup tag

    backup_jobs = scheduler.find_jobs('backup')

    # and cancelling them ...

    backup_jobs.each { |j| scheduler.unschedule(j.job_id) }

questions ?

If you have questions, feel free to ask them on OpenWFEru’s users mailing list.

scheduler and persistence

In the context of OpenWFEru, the persistence responsibility is assumed by the engine, not the scheduler.

(uninstalling openwferu-scheduler)

if you don’t need it :

    $ sudo gem uninstall openwferu-scheduler

(to be continued)