Fork me on GitHub

August 30, 2011

Session Off Strikes Back

Post moved to

All Rails witnesses should remember the times when one could declaratively control a controller's session. As a developer the reasoning behind the removal of this feature, with the introduction of lazy sessions, is understandable. Although as a framework user, especially if Rails advocates itself as being the easier approach to web development, I have my doubts.

The Ruby language is known to be preferring intuitive programming over various (architectural, performance) compromises and Rails at once seemed to be in a position of delivering the same promise. For me the removal of session :off somehow symbolically represents a starting point of a diversion from this very path. Now, don't take me wrong I highly appreciate Rails and it's community, but I guess it's getting harder to dive into for new comers with every major release since than.

But back to sessions, laziness is great, at the same time I do not think it's a reason for a useful piece of functionality, such as declaring when a session for an action should be available, to go away. How much easier it is for a developer to see an undefined method 'xxx' for nil:NilClass error than track down if a session cookie is being sent for a given action and particular parameters. Not to mention that it may even lead to "false authentication" if a REST API consumer uses a HTTP engine that handles cookies.
Therefore I decided it's worth a monkey patch and brought some love back :

Please remember that session might once again be nil, but only in controllers (request.session works unchanged). Any controller code relying on session never becoming nil, such as flash, might end with an error.

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.

April 9, 2011

Rails.logger is not threadsafe!

Post moved to

At least not completely, one might experience strange logging issues especially in JRuby threadsafe! environments: logs show up fine in the beginning but after a while it seems as if logging stopped working and only errors show up.

Blame ActiveSupport::BufferedLogger for not being (completely) thread-safe, let's take a closer look at it's silencing implementation :

Consider there are 2 threads running logger.silence(&block) concurrently. Thread 1 enters the silence blocks and changed the (temporary) level to ERROR. Then thread 2 comes in and changes the level for the block duration but the old_logger_level is now ERROR, as thread 1 changed the level already (and haven't finished executing the block yet). Now suppose thread 1 is done and restores the correct level, next thread 2 finishes and it also restores the "old" level leaving logger.level different then it was before both threads invoked silence !

Now that we understand what's happening, there seem to be 3 options how to fix this :
  1. No silencing: ActiveSupport::BufferedLogger.silencer = false
  2. Setup a new logger instance for every request (thread)
  3. Use a thread-safe "silencable" logger, such as the following:

This actually does more then promised. Would be great if we haven't had to pollute Thread.current, unfortunately there are places where silence is reinvented instead of being delegated to the logger, thus the "crazy" level= writer. In a perfect world a silence tweak would do the job.

Even if one does not use logger.silence and it's siblings such as ActiveRecord::Base.silence explicitly, it might get called by the framework e.g. when a session is being loaded using ActiveRecord::SessionStore. It's best to make sure logging works correctly in a concurrent environment as tracking down production issues when one can not rely on logs is not that simple.