Workers

Workers are your building blocks of Asynchronous Task Processing. An empty auto generated worker looks like this:

class BillingWorker < BackgrounDRb::MetaWorker
  set_worker_name :billing_worker
  def create(args = nil)
    # method gets called, when new instance of worker is created.                      
  end
end 

set_worker_name will set the worker name which can be later used while invoking tasks on the worker. create method gets called when worker is loaded for the first time. If you are starting your worker from rails, you can pass arguments to create method using:

MiddleMan.new_worker(:worker => :billing_worker,\
     :job_key => user_session,:data => current_user.id) 

Using Workers

You can invoke random tasks on workers from rails or you can schedule them using config file. Look into Scheduling section for scheduling and Rails Integration section for invoking worker tasks from rails.

Inbuilt instance methods available in your workers:

Options via class methods :

Following class methods are available for further tuning of workers:

Following snippet demonstrates their usages:

class HelloWorker < BackgrounDRb::MetaWorker
  set_worker_name :hello_worker
  reload_on_schedule true
  pool_size 10
end

When reload_on_schedule is true, worker won’t be loaded while BackgrounDRb starts and hence you don’t need set_no_auto_load option there.

Using Thread Pool

Remember BackgrounDRb follows event model of network programming, but sad truth of life is not all networking libraries follow this model and hence they make use of blocking IO and threads. BackgrounDRb allows you to run all such tasks concurrently in threads which are internally managed by BackgrounDRb thread pool.

Each worker has access to object thread_pool which can be used to run task in a thread concurrently.

thread_pool.defer(wiki_scrap_url) { |wiki_url| scrap_wikipedia(wiki_url) }

So whatever task you specify within scrap_wikipedia is going to run concurrently.

WARNING: You shouldn’t try to use register_status method from within the block supplied to defer. Because, if you do that, you can get corrupted result hashes. However, if you are confident, you should wrap your status_hash ( or whatever data type, you are going to store as a status ) in a mutex and then use register_status . It would make sure that, only one thread resisters status at a time.

Storing result/status objects

All workers can log their results with master using register_status method. This status can be then queried from rails using ask_status. For example:

class ProgressWorker < BackgrounDRb::MetaWorker
  set_worker_name :progress_worker
  def create
    @counter = 0
    add_periodic_timer(2) { increment_counter }
  end
  def increment_counter
    @counter += 1
    register_status(@counter)
  end
end

And using MiddleMan proxy, you can keep querying the status of progress bar :

MiddleMan.worker(:progress_worker).ask_status

Testing Workers

BackgrounDRb comes with a baked in mechanism to write test cases. First make sure that you have bdrb_test_helper.rb in the test directory of your rails app (run rake backgroundrb:setup, if you dont have one).

Just put your worker test cases in test/unit directory of your rails application and require the helper. Now, you should be good to go.

require File.join(File.dirname(__FILE__) + "/../bdrb_test_helper")
require "god_worker" 
 context "When god worker starts" do
  setup do
    god_worker = GodWorker.new
  end
end 

All above helper file does is that it stubs out, relevant worker methods, which really need network IO. There can be methods added, which aren’t stubbed, for all such methods you are encouraged to stub them and send the patch to the backgroundrb mailing list.