Create a gist now

Instantly share code, notes, and snippets.

What would you like to do?
Thread-safe JRuby on Rails HOW-TO with the help of Warbler, Apache Tomcat and JNDI (WIP)

#Thread-safe JRuby on Rails HOW-TO

###With the help of Warbler, Apache Tomcat and JNDI

###Read those posts before anything else otherwise you'll find yourself in a very lonely place :

##Prerequisites A Rails 3.1 app with a database like MySQL, PostgreSQL, Oracle ... forget SQLite.

In this HOW-TO we will use a PostgreSQL database (because I like it ;) ) and an Apache Tomcat Java web server, because it's the most popular.

Add ActiveRecord JDBC PostgreSQL Adapter to your Gemfile, like this :

if defined?(JRUBY_VERSION)
  gem 'activerecord-jdbcpostgresql-adapter'
  gem 'jruby-openssl'
  gem 'pg'

Add Warbler to your Gemfile, like this :

  if defined?(JRUBY_VERSION)
    group :development do
      gem 'warbler'

Run this command in a terminal: bundle install

Then run this one: bundle exec warble config

##Config Rails in thread safe mode

In your RAILS_ROOT, open the file config\environments\production.rb And uncomment this line : # config.threadsafe!

Then add those lines to ensure you can use rake tasks in production env :

  # Allow rake tasks to autoload models in thread safe mode, more info at
  config.dependency_loading = true if $rails_rake_task

Finally in your RAILS_ROOT open the file config\warble.rb And replace these 2 lines :

# config.webxml.jruby.min.runtimes = 2
# config.webxml.jruby.max.runtimes = 4

With these lines :

config.webxml.jruby.min.runtimes = 1
config.webxml.jruby.max.runtimes = 1

Create the JNDI connection and configure Rails

In your Apache Tomcat directory, open the file conf\context.xml Add the Resource tag inside the Context tag, like this :

<!-- ... -->
  <Resource name="jdbc/your_jndi_name" auth="Container" type="javax.sql.DataSource"
    maxActive="100" maxIdle="30" maxWait="10000"
    username="your_username" password="your_password" driverClassName="org.postgresql.Driver"


Copy your JDBC driver into the Apache Tomcat lib folder

For Ubuntu : sudo cp ~/.rvm/gems/jruby/gems/jdbc-postgres-9.0.801/lib/*.jar /usr/share/tomcat6/lib

Then in your RAILS_ROOT open the file config\database.yml And replace the production config with this one :

  adapter: jdbc
  jndi: java:comp/env/jdbc/your_jndi_name
  driver: postgresql
  encoding: utf8
  wait_timeout: 5
  pool: 5

In your RAILS_ROOT open the file config\warble.rb And replace this line : # config.webxml.jndi = 'jdbc/rails'

With this one : config.webxml.jndi = 'jdbc/your_jndi_name'

Finally create a config/initializers/connection_pool_fix.rb file with this content :

# Monkey patch ConnectionPool#checkout to avoid database connection timeouts
# Source :
# For Rails 3.2.0 and upper, You need to check if the pool error still occurs
if Rails.version < "3.2.0"
  class ActiveRecord::ConnectionAdapters::ConnectionPool
    def checkout
      # Checkout an available connection
      @connection_mutex.synchronize do
        loop do
          conn = if @checked_out.size < @connections.size
                 elsif @connections.size < @size
          return conn if conn
          # No connections available; wait for one
          if @queue.wait(@timeout)
            # try looting dead threads
            if @size == @checked_out.size
              raise ConnectionTimeoutError, "could not obtain a database connection#{" within #{@timeout} seconds" if @timeout}.  The max pool size is currently #{@size}; consider increasing it."

Create the War file and restart Apache Tomcat

Run this command in a terminal : bundle exec warble

Stop the Apache Tomcat service : sudo service tomcat6 stop

In the Apache Tomcat directory, copy your my_app.war file inside the webapps folder.

Restart the Apache Tomcat service : sudo service tomcat6 start

Enjoy !

The "driver: postgresql" line should be removed from database.yml. It is not needed. The Ruby side of things does not need to load the JDBC driver, Tomcat will take care of that. Having that line there can trigger this error message:

org.jruby.rack.RackInitializationException: jdbc adapter requires driver class and url

npassaro commented Jul 1, 2014

What about the pool? Shouldn't it be Tomcat's responsibility to control the number of connections?

Is the monkey patch still needed for Rails 4.0+?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment