Wilcox Development Solutions Blog

Everything you ever wanted to know about debugging backgrounDRb in Rails... before it eats your soul

July 10, 2008

Introduction

BackgrounDRb is both a blessing and a curse. First, it allows you to farm off work to other Ruby processes, so that your main process can get back to the work of serving your client (before a proxy timeout). All kinds of things can be done: re-encoding data, doing long queries, importing data. You can query BackgrounDRb to see what the status of an item is: so giving a simple progress indicator. That’s the promise. The curse is that BackgrounDRb is very hard to debug. Partially due to the fact that it’s like multithreaded programming (technically farmed out to other Ruby processes, presumably via fork)… but multithreaded programming with bad error messages is worse. BackgrounDRb is in this “worse” category. So here are my tips for avoid having your soul eaten by debugging this beast. Or, at least, let you put up a good fight.

Zeroth up - Information

Go, read these articles. I’ll be waiting.

Don’t expect the RubyDocs to give you much information… there’s no actual documentation there. It just barely beats browsing the source files in your editor… but you may prefer this.

First Up - Know your versions

Install from trunk if you can. The December 2007 build of BackgrounDRb has an issue where you can’t pass more than let’s say 4K of data to or from a worker. I’m serious. Yes, that means register_status too. Now you might want to consider files as an interprocess communication method anyway… but I think BackgrounDRb is one of those projects where you should Trust Head.

Second Up - Architecture

Ok, so this stuff is hard. You want to be able to use regular old Ruby to test things out before you add BackgrounDRb to the mix. My suggestion: do all your work in a totally separate, isolated, and unit-tested class, and use your worker only to set that up and run it. Seriously: the more you can debug in unit tests, normally, the more hair you’ll have.

Third Up - The Pain

There are two ways to approach things at this point: the mock way and the interpreter’s way. I don’t care which you pick.

The Mock Way

Instead of inheriting from BackgrounDRb::MetaWorker, inherit from this sucker… class FakeyWorker attr_reader :logger def initialize @logger = logger end def register_status(data) @logger.debug("FROM REGISTER_STATUS") @logger.debug(data) end def self.set_worker_name(something) #ignore end def get_status return @currdata end end Import your worker file into your controller, and call it directly. Call get_status after the call to your worker method, and see what comes back. Since this is (still) happening synchronously, you’ll see exceptions in your browser. Fix errors. Rinse and repeat. Then lose the training wheels and use MiddleMan like you’re supposed to.

The interpreter Way

If that doesn’t appeal to you, (or you’ve done the above but still don’t trust) do the following:

  1. Fire up one console window with script/backgroundrb running
  2. Fire up another console window, and run script/console
  3. Trigger your worker from here. Here’s some example code: >>> MiddleMan.new_worker( :worker => :import_data_worker, :job_key => 41258 ) >>> data = {....} >>> MiddleMan.worker(:import_data_worker, 41258).my_worker_method(data) >>> MiddleMan.worker(:import_data_worker, 41258).ask_status

If ask_status returns nil, you have a problem. If a traceback shows up in the script/backgroundrb window, you have a problem… but don’t bother reading the traceback - it’s probably worthless. If you’re having troubles, break it down: are you 100% sure your worker is not nil? (Answer: No). save the worker to a variable and check it! Also a worker’s worker_info method is nice. If you did get nil from ask_status, I’d ask: is my_worker_method even getting called? Do something as obvious as possible: I like writing to a file in my_worker_method. No way to miss that, or have it go to the wrong log.

Fourth Up - BackgrounDRb STILL wants your soul

BackgrounDRb really wants to eat your soul. Really. Here are some suggestions:

  • Wrap your entire worker method in begin… rescue and register_status $!.backtrace (or, write it to a file)
  • Logging MiddleMan.all_worker_info.to_yaml in your controller action is brilliant. Verify you have the worker you’re looking for!
  • I’m not joking about checking to make sure MiddleMan.worker returns you a worker.
  • Is your worker method not getting called for “no reason at all”? Are you passing a lot of data to it and using the December 2007 version?
  • I’m assuming you’re copying off a worker and action that works already?
  • Some of these things could be full of crap. If so, let me know.

The End

With any luck this article helped your debugging, and you spent way less time than I did debugging your workers. Yes, BackgrounDRb sucks: the documentation isn’t good, the error messages are horrible, it’s a ton of infrastructure, insanely complicated, and sometimes it Just Doesn’t Want To Work. It’s also a fair tool. For all that, I might not be able to submit patches to help out the project, but I can write this article.


Written by Ryan Wilcox Chief Developer, Wilcox Development Solutions... and other things