Fail Early

  • Posted by Mike Naberezny in Ruby

    I’m pleased to have been able to contribute a recipe to Mike Clark’s new book, Advanced Rails Recipes. The concept presented in my recipe, “Fail Early”, is that you can use initializers to prevent your application from starting up under certain conditions.

    Rails applications typically run under persistent application server processes, like mongrel or thin. When a Rails application starts, it goes through a startup procedure that is performed only once. The startup includes reading the environment configuration files and running any initializers that have been set up. This can also be used as an opportunity to detect potentially dangerous situations and bail out.

    “Fail Early” uses the case of pending migrations to demonstrate. If the application is started while there are pending migrations for the production database, the results can wreck production data. Instead, an initializer detects this condition and exits by calling Ruby’s Kernel.abort.

    Here’s another case where this idea is useful. It’s well-known that the Ruby-based MySQL driver included with Rails isn’t suitable for use in production. In fact, Rails will produce this warning in the log if it is in use:

    WARNING: You’re using the Ruby-based MySQL library that ships with Rails. This library is not suited for production. Please install the C-based MySQL library instead (gem install mysql).

    This can go unnoticed in the log. Instead, we can write a short initializer that detects this condition and aborts the application start if the production server is misconfigured.

    config/initializers/check_mysql_driver.rb:

    if RAILS_ENV == 'production'
      config = ActiveRecord::Base.configurations['production']
      if config['adapter'] == 'mysql'
        ActiveRecord::Base.require_mysql
        if Mysql::VERSION.to_s.include?('-ruby')
          abort "Ruby-based MySQL driver is not suitable for production"
        end
      end
    end
    

    When the initializer above is run in a production environment that has the Ruby-based MySQL driver instead of the C-based one, startup will be aborted.

    $ mongrel_rails start -e production
    ** Starting Mongrel listening at 0.0.0.0:3000
    ** Starting Rails with production environment...
    Ruby-based MySQL driver is not suitable for production
    $
    

    You can run many other safety checks like this at startup. Since they will be run only once and not per-request, your application incurs no performance penalty by doing so.