#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 !
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