#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 :
- JRuby on Rails and Thread Safety
- Using JDBC Connection Pools with NetBeans 6, JRuby RoR, MySQL and Glassfish
- Q/A: What Thread-safe Rails Means
- Apache Tomcat 6.0 - JNDI Datasource HOW-TO
##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
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
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 !
Is the monkey patch still needed for Rails 4.0+?