Where developers come to connect, share, build and be inspired.


1600% faster app requests with Rails on Heroku


Using rainbows! and em-http-request resulted in a 1600% performance increase compared to using unicorn and net_http. (For the use case described below)


  • You have an app that does a lot of calls to a 3rd party API. This is often the case with facebook apps for example.
  • You want to host it on heroku (one dyno preferred, so it's free)


  • You want to call the 3rd party API from your controller and render the response
  • Even if you are using unicorn to forks several worker processes, they will be blocked and waiting for a response from Facebook (or any other potentially slow API) most of the time.

Solution: Use EventMachine and Rainbows!

Here is a quick outline on how to get you up and running with Rainbows! and em-http-requests on heroku.

We will be using the following gems: Gemfile

gem 'rainbows'
gem 'em-http-request'
gem "faraday"


class AsyncController < ApplicationController
  def index
    conn = Faraday.new "http://slowapi.com" do |con|
      con.adapter :em_http
    resp = conn.get '/delay/1'
    resp.on_complete {
      @res = resp.body 
    throw :async


<%= @res %>


worker_processes 3
timeout 30
preload_app true

Rainbows! do
  use :EventMachine

before_fork do |server, worker|
  # Replace with MongoDB or whatever
  if defined?(ActiveRecord::Base)

after_fork do |server, worker|
  if defined?(ActiveRecord::Base)


match 'async_test' =>   'async#index'


web: bundle exec rainbows -p $PORT -c ./config/rainbows.rb

it is also a good idea to set config.threadsafe! in your Rails environment configuration


  • Deploy to heroku
  • Run a benchmark, e.g. the following

    ab -n 600 -c 200 http://mycoolasync.herokuapp.com/async_test

  • Enjoy how fast it is

Some results

I set up a test app on heroku and ran ab -n 600 -c 200 http://mycoolasync.herokuapp.com/async_test


  • Old-school method: 183 second
  • Evented method: 11 seconds

Detailed results for old-school synchronous requests

Using non-evented http request by settting con.adapter :net_http in app/controllers/async_controller.rb

Document Path:          /async_test
Document Length:        2755 bytes

Concurrency Level:      100
Time taken for tests:   183.373 seconds
Complete requests:      600
Write errors:           0
Non-2xx responses:      516
Total transferred:      652495 bytes
HTML transferred:       541903 bytes
Requests per second:    3.27 [#/sec] (mean)
Time per request:       30562.206 [ms] (mean)
Time per request:       305.622 [ms] (mean, across all concurrent requests)
Transfer rate:          3.47 [Kbytes/sec] received

Connection Times (ms)
              min  mean[+/-sd] median   max
Connect:      101  175 260.7    109    1239
Processing:  1270 28308 5571.9  30115   30978
Waiting:     1146 28156 5882.4  30115   30665
Total:       1376 28484 5582.8  30224   32203

Detailed Results for new and fancy evented request method

Ensured by settting con.adapter :em_http in app/controllers/async_controller.rb

Document Path:          /async_test
Document Length:        2758 bytes

Concurrency Level:      100
Time taken for tests:   11.118 seconds
Complete requests:      600
Write errors:           0
Total transferred:      1734330 bytes
HTML transferred:       1655686 bytes
Requests per second:    53.97 [#/sec] (mean)
Time per request:       1853.048 [ms] (mean)
Time per request:       18.530 [ms] (mean, across all concurrent requests)
Transfer rate:          152.33 [Kbytes/sec] received

Connection Times (ms)
              min  mean[+/-sd] median   max
Connect:      101  194 283.0    114    1263
Processing:  1136 1485 416.1   1341    3558
Waiting:     1135 1453 346.2   1339    3159
Total:       1250 1679 472.5   1505    3669

Some final words

I wanted to have a link-baity title once. So here we are with a very unscientific 1600%. You'll be the judge - I think with the right test setting this number could be much much higher. If you replicated this and achieved a better result - let me know. I'll update this post and give you credit.

Cheers, your friend dommmel


  • Matt_aimonetti_portait_2011_sq400

    Using throw :async in a controller action makes me really sad :( But that's not the point of this tip, sorry...I... couldn't resist.

  • User-avatar

    This website looks amazing on an iPad. Well done.

  • E6b625008c816ab3d8d742cc3eddbb00

    @mattetti Thanks for pointing it out. Would love to know how you would go about it.

    From the top of my head: One could use Rack::FiberPool and EM-Synchrony like so


    gem 'rack-fiber_pool', :require => 'rack/fiber_pool'
    gem 'em-synchrony'


    use Rack::FiberPool
    run MyApp::Application


    conn = Faraday::Connection.new(:url => 'http://slowapi.com') do |builder|
       builder.use Faraday::Adapter::EMSynchrony
    resp = conn.get '/delay/1'
    @res = resp.body 
  • Aee029760669bdd096685145a9c48ac6

    Can you explain this line? request.env['async.callback'].call(response)

    Where are request and response defined?

  • E6b625008c816ab3d8d742cc3eddbb00

    @barapa Yeah - that bit is confusing. I am by no way an expert on this kind of stuff - maybe others can chime in and provide more insightful comments than me - anyway here's my answer

    response and request objects

    The two accessor methods response and request exist in every Rails controller. Some details can be found in this section of the rails guides.


    The async.callback is part of a scheme that was first implemented in thin afaik (see this blog post) and has since found its way into some other server software, including Rainsbows!/EventMachine.

  • Bfd07ad84c85d505fc79dc17c6b04c2e

    you are my hero <3

  • E5a4192e5401cf29ca9eca353376b523

    this website is not working....http://mycoolasync.herokuapp.com/async_test

  • Default_profile_6_normal

    Thanks, i would like to say that your blog is loading very fast... can you tell us how you managed it ? www.smartphonesunder10000.com r best phone under 10000 r best smartphone under 15000 r Happy Valentines Day 2016 Poems

Add a comment