Ruby 2.1 & Rails 3 – Our Experience

We’ve recently moved FTBpro’s Ruby on Rails servers to the newest Ruby version on earth – Ruby 2.1. It has been running on our production servers for the past two weeks. Our stack includes: MySQL, MongoDB, Rails 3.2, ElasticSearch Memcached and Redis. We wanted to share our experience of making this change.


1. First thing you encounter when you move to Ruby 2.1 is the non-working net/http module. As explained here, Ruby 2.x Net/HTTP library asks for gzipped content by default, but does not decode it by default, which makes some JSON parsing of HTTP requests break. This breaks koala gem, right_aws gem and many other gems which relies on JSON HTTP communication to operate.
The solution to this is a small patch to the net/http library. We have put it in our config/initializers/a_net_http.rb so that Rails loads it upon boot. The patch:

2. All of the mongoDB users on the room pay attention: current stable version 0.12.0 of the mongomapper gem does not support ruby2.x. We upgraded our gem version to 0.13.0beta2, and in combination with the net/http patch in bullet 1 it works like a charm.

3. If you are a fan of the debugger gem you’ll have to say farewell. It does not support ruby2.x in any manner and causes nasty segmentation faults with long outputs. The good news is that there is a very good replacement: byebug gem. Its interface is almost similar to that of the debugger gem so you’ll feel right at home, and it works well with ruby2.x.

4. If you’re using imagesize gem to determine the height/width of images you’ll have to find a replacement. We already had Rmagick in our gemset which includes image dimensions retrieval so we just used it.

5. We had a weird bug with the BigDecimal library in ruby 2.1. Here is the output of the exact same code under ruby1.9.3 and ruby 2.1:

Don’t know how to explain this but we’re lucky to have a test suit for this module because we’d never discover it until it got to production.
UPDATE: Wasn’t aware of it but apparently BigDecimal division is a known bug in Ruby 2.1. You should check out this list for more info.

This concludes the changes we had to make to our code so it runs well under Ruby 2.1. Not much, but is it worth the hassle?

The Effects

We observed three, very prominent, improvements in Ruby 2.1 over 1.9.3:

1. Load times are significantly lower. And by “significantly” I mean about forth of the time. The larger your environment is, the larger the difference of the load time. We were nothing less than amazed by this:
* Deployment time dropped from 14 minutes to approximately 5 minutes. This is due to the many rake tasks we run while deploying. We make about 15 deployments to our QA servers daily. That’s 135 minutes, a little more than two hours saved per day for developers waiting for their version to arrive on the QA server.
* Build time by Jenkins CI was reduced from 14 minutes to about 6 minutes. This has shorten the time from opening a pull request to a successful / failed build notice and made the feedback loop a little more bearable.
* Every run of binary that requires rails environment to be loaded takes now forth of the time. This are the measurements I made on our environment:
Ruby 1.9.3: bundle exec rails runner ‘puts “a”‘ 41.06s user 2.23s system 98% cpu 43.916 total
Ruby 2.1.0: bundle exec rails runner ‘puts “a”‘ 11.07s user 2.04s system 94% cpu 13.823 total
It saves a lot of waiting time for our developers when running rails server / console and various rake tasks.

2. Garbage collection times dropped from 100ms to almost 0ms. This is our New Relic graph for garbage collection. The vertical line marks the deploy which moved us to ruby 2.1:
GC - ruby1.9.3 vs 2.1

3. We had a severe problem on deployments during high traffic hours – we’d just go down from time to time while unicorn workers were restarting. Ruby 2.1 amazingly mitigates this problem since environment load time is 1/4 now and Ruby 2.1 GC is copy-on-write friendly which makes unicorn better handle forking. You should definitely read this article which explains how Ruby 2.x affects Unicorn’s forking mechanics.

Weird Stuff

The only thing we can’t explain yet since moving to Ruby 2.1 is some strange unicorn master process behaviour: When we restart it, it always starts off with a different amount of memory size. As a result, every unicorn restart causes the mean response time to differ in about 100ms. Here are two graphs where the vertical line represents a unicorn restarts:

Unicorn Restart - Bump Up
Unicorn Restart – Bump Up
Unicorn Restart - Bump Down
Unicorn Restart – Bump Down

This is the only thing we feel uncomfortable with moving to Ruby 2.1.

You Should Also

Ruby 2.1 has some killer advantages over Ruby 1.9.3. It will make you daily operation a lot faster than it is today and can even help you overcome or mitigate other infrastructure problems you’re having.
The changes we had to do to move to Ruby 2.1 are really minor comparing to the benefits we got from it.
There is no reason to stay behind – make a step forward to Ruby 2.1.

3 thoughts on “Ruby 2.1 & Rails 3 – Our Experience

    1. I didn’t actually notice any changes in memory consumption but that might be due to our usage of unicorn_worker_killer gem which kills workers when they cross a certain memory threshold. We have no monitoring of how many requests a worker processes before it is being killed so there might have been a decrease in the number after upgrading to ruby 2.1 but we just didn’t noticed it.

Leave a Reply

Fill in your details below or click an icon to log in: Logo

You are commenting using your account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s