I recently read a pretty good e-book on parallel and concurrent programming in Ruby Working with Ruby Threads by Jesse Storimer. I decided to try out running something with Rails and threading enabled, so I converted this blog to run with Puma and Rubinius on Heroku. Here’s how I did that
Add Puma to the Gemfile and disable Unicorn
gem 'puma' # gem 'unicorn'
Also change the Heroku Procfile to use Puma with a minimum of 5 threads and a maximum of 16 - the defaults are 0 and 16
web: bundle exec puma -t 5:16 -p $PORT
Change to Rubinius in Gemfile
Update the Gemfile to tell Heroku to use Rubinius:
ruby "1.9.3", :engine => "rbx", :engine_version => "2.0.0.rc1"
Configure the Rails Database Pool
Since Heroku overwrites the database.yml file during deploy, you can’t configure the database pool size in that file. Heroku documents how to configure a connection pool
Create a new initializer file called database_connection.rb (config/initializers/database_connection.rb) with the following contents
Rails.application.config.after_initialize do ActiveRecord::Base.connection_pool.disconnect! ActiveSupport.on_load(:active_record) do config = Rails.application.config.database_configuration[Rails.env] config['reaping_frequency'] = ENV['DB_REAP_FREQ'] || 10 # seconds config['pool'] = ENV['DB_POOL'] || 16 ActiveRecord::Base.establish_connection(config) end end
Tell Rails We’re Threadsafe to Avoid Single Threaded Requests
In the application.rb file, add the following configuration line, inside the class Application < Rails::Application block.
Rubinius has much slower startup than MRI 1.9.3 and 2.0.0, but seems to have a very similar runtime performance to MRI 1.9.3. If you can live with this, you may have an application that performs better for your workload.
This slow startup actually causes some problems with Heroku, as requests start coming into the app before it is really ready. This means the first few requests after an application restart are likely to fail and produce errors to your users. If you deploy frequently, this will be a problem for you.
Does This Improve My Application?
The only way to tell if your application performance is improved is to measure the before and after speed of your application. If your application runs slower and gives your users a worse experience, then you want to know this and revert back to your current configuration. Only you can tell if this is worth it for your workload.
Heroku Memory And Load Logging
Heroku has a setting to dump to your log stream every few seconds a set of useful information including memory usage and system load. In your command line, type the following
heroku labs:enable log-runtime-metrics
There’s also a useful application that shows this data, when enabled in your application, in a single page display. Read about log2viz, but it seems a bit flakey for me, often missing data from the application.
Some time ago, multi-threading in Ruby was a dark art, and many common Gems had problems running in a multi-threaded environment. However, we’re now at a point where a lot of the common Ruby software will work fine in a multi-threaded way, but you still need to know how to write your own code in a way that doesn’t break in strange and unhappy ways. Jesse Storimer’s e-book, Working with Ruby Threads, can help you get started on that path.