Fork me on GitHub

June 30, 2011

Delayed Job the Java Way

Post moved to

On the Rails side, we all know Delayed::Job the (probably most popular) background job eater. We've learned how to run it rake jobs:work, live with it RAILS_ENV=production script/delayed_job start and sometimes even kill -S KILL it.

The beauty of Delayed::Job comes from it's simplicity, the only nuisance being the need to manage a separate process that handles the jobs, besides just booting your application.

So, let's suppose you've decided to go with JRuby on production. Sooner or later you'll discover that it's not going to be a simple interpreter switch, especially with workers such as Delayed::Job, as gems such as daemons do not play well on the Java side. You may even review all of the precious code, to make sure JRuby benefits from threadsafe! just to find out that you need to stick with the "old" Ruby to spawn a delayed daemon.

Well, good news, it doesn't have to necessary be that way, after all:

"JRuby is like the Matrix, only there's no spoon or fork()."

Funny but actually true, the JVM was designed to abstract away (sometimes too much) from the OS. If you ask a Java developer to built an asynchronously worker that polls and executes new tasks as they arrive, he'll immediately write a sort of new Thread() { ... }.start() code (or he starts configuring JMS :)). The point is that Java is a notoriously Thread oriented platform.

And while using JRuby on Rails you might take advantage of it while running (asynchronous) job workers. First thing you should understand is that there's a Servlet API that is the base of every Java web server you'll find (very similar to what Rack is for all Ruby servers these days). On top of it there's an adapter, most servlet containers use, when serving Rack based applications called jruby-rack. And at last, knowing all the pieces, there's jruby-rack-worker that takes some worker code and runs it in a separate thread along side your application serving requests.

The only downside is that one needs to make sure the worker loop plays by the rules in a multi-thread environment. Luckily, this is fairly easy and already handled for Delayed::Job.

Thus the one thing left is how to set it up, well it depends on your web server choice. You'll need to configure a listener to start the background thread when your application gets deployed, these things are usually configured in a deployment descriptor web.xml. Warbler might spill out one for you even if you do not assemble into an "universal" (.war) package.
Then just add the following XML snippet to the bottom :

You'll find out more in the project's README. Happy Delaying ...

P.S. Just to prove a point, I've adapted Navvy as well.