Simple Background with Rake
The first thing I tried for this sort of thing was taken directly from one of Ryan Bates excellent "RailsCasts" (Episode 127 to be precise): I just put everything I needed delegated into rake tasks and had a method like this in the application controller:
def call_rake(task,options={})
options[:rails_env] = RAILS_ENV
args = options.map{|n,v| "#{n.to_s.upcase}='#{v}'"}
system "rake #{task} #{args.join(' ')} &"
end
Easy enough, right? Now any controller action can just "call_rake" with the name of the task they want to run and the arguments they want to pass along to it. The ampersand in the system call makes it a forked process, so it works out well.
Now, this worked fine as long as the only things I wanted to run were administrative tasks on my end. Why? Because you can see how if 500 users were to call an action that used "call_rake" at around the same time then I would have 500 background processes spawned. Not good, right?
Better Background with BackgroundJob
Enter some other options: I was pretty familiar with "BackgrounDRb", and I'd heard about "Starling" on another Railscast, so I wrote to our awesome hosting provider (EngineYard) to ask which they preferred dealing with for their clients. The guy I talked to pointed out that they had plenty of experience with both, but that I might want to check out another library called BackgroundJob since that was the one they'd had the least trouble with. I did check it out, and the simplicity is compelling.
To sum it up in brief, a process hangs out on your server and queries this table from time to time. The table ("bj_jobs") is essentially a queue of jobs to be executed. You just submit jobs you want to be run in the background as a string system call, and it takes care of processing the jobs in the table one at a time. Here's the example of the cron job from BackgroundJob's docs:
cmd = bj run --forever \\
--rails_env=development \\
--rails_root=/Users/ahoward/rails_root
*/15 * * * * $cmd
Now commonly BJ is used to run worker code that you write in a new "jobs" directory with "script/runner". That's a good pattern, I think, but for me I already had all these rake tasks I was using for background stuff. So what did I do to my application controller?
def call_rake(task,options={})
options[:rails_env] = RAILS_ENV
args = options.map{|n,v| "#{n.to_s.upcase}='#{v}'"}
Bj.submit "rake #{task} #{args.join(' ')}"
end
Almost nothing. I just changed "system" to "Bj.submit" and, then all the rake tasks I was already using to run background jobs, plus any new ones I wanted to write for users (like generating big reports) can be called the same way they were before. After a week now of having BackgroundJob in production, I'm pretty happy with it, and I'd highly recommend giving it a try to see if it fits your needs before you mess with anything more complex.
