Search code examples
ruby-on-railsrubyrubygemsrufus-scheduler

how to have a variable that will available to particular scheduled task whenever it runs again after some time?


I am starting many scheduled jobs at the same time using rufus scheduler. I want a variable in that code that will be available to only that scheduled job, when it runs again after some time.

how can i maintain a variable for for each scheduled job?


Solution

  • there multiple ways to do that. I'll detail some of those, going from ugly to somehow elegant.

    Solutions tested against Ruby 1.9.3p392 on Debian GNU/Linux 7 with rufus-scheduler 2.0.23 (https://rubygems.org/gems/rufus-scheduler).

    You could use a global variable:

    require 'rufus-scheduler'
    
    scheduler = Rufus::Scheduler.start_new
    
    scheduler.every '1s' do
      $counter0 ||= 0
      $counter0 += 1
      p [ :c0, $counter0 ]
    end
    scheduler.every '1s' do
      $counter1 ||= 0
      $counter1 += 1
      p [ :c0, $counter1 ]
    end
    
    scheduler.join
    

    Or you could centralize the job variables in a single global variable (note: this isn't thread-safe):

    require 'rufus-scheduler'
    
    scheduler = Rufus::Scheduler.start_new
    
    $job_vars = {}
    
    scheduler.every '1s' do |job|
      ($job_vars[job.object_id] ||= {})['counter'] ||= 0
      $job_vars[job.object_id]['counter'] += 1
      p [ job.job_id, job.object_id, $job_vars[job.object_id]['counter'] ]
    end
    scheduler.every '1.5s' do |job|
      ($job_vars[job.object_id] ||= {})['counter'] ||= 0
      $job_vars[job.object_id]['counter'] += 1
      p [ job.job_id, job.object_id, $job_vars[job.object_id]['counter'] ]
    end
    
    scheduler.join
    

    One step further, just for fun (but still not thread-safe):

    require 'rufus-scheduler'
    
    scheduler = Rufus::Scheduler.start_new
    
    $job_vars = {}
    job = lambda { |job|
      ($job_vars[job.object_id] ||= {})['counter'] ||= 0
      $job_vars[job.object_id]['counter'] += 1
      p [ job.job_id, job.object_id, $job_vars[job.object_id]['counter'] ]
    }
    
    scheduler.every '1s', &job
    scheduler.every '1.5s', &job
    
    scheduler.join
    

    Finally, you could add a #vars to the Job class:

    require 'rufus-scheduler'
    
    class Rufus::Scheduler::Job
      def vars
        @vars ||= {}
      end
    end
    
    scheduler = Rufus::Scheduler.start_new
    
    scheduler.every '1s' do |job|
      job.vars['counter'] = (job.vars['counter'] || 0) + 1
      p [ job.job_id, job.object_id, job.vars['counter'] ]
    end
    scheduler.every '1.5s' do |job|
      job.vars['counter'] = (job.vars['counter'] || 0) + 1
      p [ job.job_id, job.object_id, job.vars['counter'] ]
    end
    
    scheduler.join
    

    This is the solution I prefer. I intend to add a similar bag of variables to Job in rufus-scheduler 3.0 (https://github.com/jmettraux/rufus-scheduler).

    You could also put the variable somewhere else and use the job_id / job.object_id as a key to retrieve it (as the first snips of code do).

    I hope this will help.