Migrating from Rails 3.1 RC4 to RC5 using Heroku's Cedar Stack (also compass, unicorn, and sendgrid)

Stamped: 08 Aug 2011 | Show comments

A mouthful, indeed

I'm currently developing a super duper secret project which uses Rails 3.1 and is hosted on Heroku. With the release of Rails 3.1 RC5, I've decided to migrate to Heroku's Cedar stack after reading Michael's article about how easily unicorn can be implemented on the new stack.

I've already migrated one app, Wasted on Steam, and the performance enhancements are amazing, as you might expect. Using siege, it was able deal with 100 concurrent requests repeated 10 times in 2.5 seconds per request. Previously, it had taken 10 seconds per request. This is amazing, especially considering Wasted on Steam uses a network blocking request to update data.

For this other app, this super duper secret one, I thought I would detail the process.

The initial setup

Currently, there's no way to migrate the stack automatically, so we have to clone and proceed:

git clone git@heroku.com:<your_app_name>.git cedar_testing

Next, create the app on the new stack:

heroku create --stack cedar
git config heroku.remote heroku

The code changes

Gemfile fixes

Rails 3.1.rc5 separated the asset-building gems into their own group in the Gemfile, called :assets, so we need to make some changes and clean it up so it looks like this:

source 'http://rubygems.org'

gem 'rails', '3.1.0.rc5'

group :assets do
    gem 'sass-rails', "~> 3.1.0.rc"
    gem 'coffee-rails', "~> 3.1.0.rc"
    gem 'uglifier'
    gem 'compass', '0.12.0.alpha.0.91a748a', :git => 'git://github.com/chriseppstein/compass.git', :branch => 'rails31'
end

gem 'pg'
gem "kaminari"
gem "redcarpet"
gem "devise"
gem 'sprockets'
gem "cancan"
gem "mime-types"
gem "treetop"
gem "ancestry", '>= 1.2.2'
gem "nokogiri", '1.4.4'
gem "oa-oauth", :require => "omniauth/oauth"
gem 'oa-openid', :require => 'omniauth/openid'
gem 'omniauth', '>= 0.2.6'
gem 'dalli'

group :production do
    gem 'unicorn'
end


group :development, :test do
    ...
end

Miscellaneous fixes

Please note, we no longer need to include the old version of rake, nor the therubyracer (the Cedar stack now includes node.js). It's very important that you update the path to use the native node.js by typing:

heroku config:add PATH=vendor/bundle/ruby/1.9.1/bin:/usr/local/bin:/usr/bin:/bin:bin

Supposedly, this is supposed to happen on new apps by default, but I had issues. Now, make sure you run bundle update now to update the Gemfile!

Next, we need to update our application.rb; it's changed in RC5. Around line, replace with the following:

Bundler.require *Rails.groups(:assets) if defined?(Bundler)

Now, if you're using Compass like I am, you'll need to create an initializer in config/initializers/sass.rb:

Rails.configuration.sass.tap do |config|
  config.load_paths << "#{Gem.loaded_specs['compass'].full_gem_path}/frameworks/compass/stylesheets"
end

Thanks to Ken Collins for that bit! I imagine, as some point when Rails 3.1 is released this will be done automatically.

Next, in your production.rb, change the config.action_dispatch.x_sendfile_header to:

config.action_dispatch.x_sendfile_header = nil

If you use sendgrid

The Cedar stack is supposed to be less magical and black-boxy then the others, so if you use sendgrid, we have to accommodate for it in our production.rb.

config.action_mailer.raise_delivery_errors = true
config.action_mailer.smtp_settings = {
  :address        => "smtp.sendgrid.net",
  :port           => "25",
  :authentication => :plain,
  :user_name      => ENV['SENDGRID_USERNAME'],
  :password       => ENV['SENDGRID_PASSWORD'],
  :domain         => ENV['SENDGRID_DOMAIN']
}

Using unicorn instead of thin

Our Procfile

This Procfile tells Heroku exactly which process groups need to exist for various reasons. Like a web group to run our unicorn. Our Procfile is quite simple as we only need the main web process, Unicorn.

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

The unicorn config

Place this in your config directory and call it unicorn.rb

worker_processes 4 # amount of unicorn workers to spin up
timeout 30         # restarts workers that hang for 30 seconds

You'll have to play around with the configuration depending on how memory-heavy your app is. Wasted on Steam uses a ton of gems and it can easily run 4 worker processes.

Push the changes to heroku

git add Procfile config/unicorn.rb
git commit -a -m "Updated for Heroku and Unicorn"
git push heroku master

The data

If you have any configuration variables, you'll want to go to the old app and do heroku config -s and migrate them over using heroku config:add <VARIABLE>=<DATA> <HELLO>=<MOREDATA>.

Finally, we have to pull the database from our old app. This will only work if you have the gem taps installed, so gem install taps. Go to your old app folder and type the following:

heroku db:pull sqlite://backup.sql
mv backup.sql ../cedar_testing
cd ../cedar_testing
heroku db:push sqlite://backup.sql

If you have any issues, simply type git remote rm heroku; git remote add heroku git@heroku.com:<new_app_name>.git then proceed with the steps above. This usually fixes everything.

We're done!

Hopefully, everything should work now as planned. Make sure you go to http://<yourapp>.herokuapp.com and not http://<yourapp>.heroku.com, and you should see the site!

Comparing performance

This app is a simple app, but to give you an idea of how it performs, again with 100 concurrent requests repeating 10 times:

Old & Busted

Transactions:                 995 hits
Availability:              99.50 %
Elapsed time:              62.90 secs
Data transferred:           1.35 MB
Response time:              5.26 secs
Transaction rate:          15.82 trans/sec
Throughput:             0.02 MB/sec
Concurrency:               83.19
Successful transactions:         995
Failed transactions:               5
Longest transaction:           15.67
Shortest transaction:           0.12

New hotness

Transactions:                1000 hits
Availability:             100.00 %
Elapsed time:              19.99 secs
Data transferred:           1.36 MB
Response time:              0.61 secs
Transaction rate:          50.03 trans/sec
Throughput:             0.07 MB/sec
Concurrency:               30.33
Successful transactions:        1000
Failed transactions:               0
Longest transaction:           14.04
Shortest transaction:           0.11

Many articles helped get me here

Heroku's articles on migrating helped a lot. Michael's article on using unicorn on Cedar was the inspiration, and finally, Ken's article helped me get Compass integrated. Thanks!

tags: rails, heroku, cedar
recent entries
Rails — A faster way for next and previous links on a post, article, or any model
The awkward things Siri says
Node.js — Getting oAuth Up and Running Using Express.js and Mongoose
Node.js — Getting oAuth Up and Running Using Express.js, Railway.js and Mongoose
Migrating from Rails 3.1 RC4 to RC5 using Heroku's Cedar Stack (also compass, unicorn, and sendgrid)
Random Freeze Fix for GTX 460 in 10.6 (osx86)
Wasted on Steam - an analytic tool for the Steam platform
Rails 3.1 — SQL logging to STDOUT during testing (with rspec, test::unit, or cucumber)
Rails 3.1 — Using ERB/HAML/etc within a Coffeescript JS file
Rails 3.1 — 'load_missing_contant': Expected ... to define ... (LoadError)
View the entire archive of articles