Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save ritchiey/1560215 to your computer and use it in GitHub Desktop.
Save ritchiey/1560215 to your computer and use it in GitHub Desktop.
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'
else
  gem 'pg'
end

Add Warbler to your Gemfile, like this :

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

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 http://stackoverflow.com/a/4880253
  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 :

<Context>
<!-- ... -->
  <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"
    url="jdbc:postgresql://your_hostname:5432/your_database_name"/>

</Context>

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 :

production:
  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 : https://github.com/rails/rails/issues/2547
# 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
                   checkout_existing_connection
                 elsif @connections.size < @size
                   checkout_new_connection
                 end
          return conn if conn
  
          # No connections available; wait for one
          if @queue.wait(@timeout)
            next
          else
            # try looting dead threads
            clear_stale_cached_connections!
            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."
            end
          end
          
        end
      end
    end
  end
end

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 !

@ksader-edmunds
Copy link

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