Tuesday, March 11, 2008

Adventures in BackgroundRb

We've been asked to produce some "nice" reports in PDF for a client, and getting it all done has been quite an adventure. We use Ruport for the reporting, which unfortunately means we also need to learn quite a bit about PDFWriter to get everything looking spiffy, and the report rendering does take a while, but we had it all working in a controller yesterday and things were looking good. Then we deployed the application to EngineYard.

The first thing that happened was that we got timeout errors that looked like this (courtesy of ExceptionNotifier):

A Mongrel::TimeoutError occurred in reports#cost_detail_pdf:
Mongrel timed out this thread: shutdown
/usr/lib64/ruby/gems/1.8/gems/mongrel-1.1.3/lib/mongrel.rb:221:in `within'

The support folks at EngineYard suggested that we shouldn't be running PDF generation from within our controller, and that we should use something like background job, but since we wanted to report status as the job proceeded and we only had a small number of jobs, we went with BackgroundRb instead. (If you care about the difference, look for 'Backgroundrb and Bj serve different purposes' in this thread).

BackgroundRb works great once you get it going, but there were examples in the documentation that simply didn't work for me. Here's what worked for me in the end:

In my controller:

[sourcecode language='ruby']
MiddleMan.new_worker(:worker => :cost_detail_pdf_worker, :job_key => current_user.id)
MiddleMan.ask_work(:worker => :cost_detail_pdf_worker, :job_key => current_user.id,
:worker_method => :build_report,
:data => [session[:production_id], file_name])

and my worker:

[sourcecode language='ruby']
class CostDetailPdfWorker calculated_report, :template => :default)
register_status("Finished Rendering")
File.open(file_path, "w") do |file|
file << pdf
register_status("File available")


The next part of the adventure was getting this running on our Slicehost slice. Unfortunately, BackgroundRb requires Ruby 1.8.5, and we only had 1.8.4 (on Ubuntu 6.06 LTS). Apt-get for Ubuntu 6.06 doesn't include Ruby 1.8.5, so I needed to install Ruby 1.8.5 from source using these excellent instructions.

Unfortunately, I also needed to reinstall all our gems to go with the new version of Ruby. This was mostly stragithforward, except for Postgres. The postgres gem wouldn't build the native extensions, failing to find pg_config. These instructions were close, but before that would work I also needed to do "apt-get install libpq-dev" (you might also need libpgsql-ruby and/or libpgsql-ruby11.8, but I already had those installed).

Ok, looks pretty good now. I started the backgroundrb server manually as a test, and could produce my pdf files, so the next step was to move to our production environment at Engineyard. This was the first time I tried to stop and start the BackgroundRb using Capistrano - unfortunately you can't simply repeat the command lines in your Capistrano tasks. Fortunately, there's also a description of some tasks that do work (make sure you use the start task in the comments at the end of post).

All in all, a nice outcome but with much more pain than I would have liked.