The Progress

Moving forward ↝

Migrating a Rails Heroku App to Jruby

| Comments

This talk by Charles Nutter recently inspired me to move one of my personal projects on to JRuby. The app is fairly small, running Rails 3.2 using postgresql, activemodel-serializers and oauth to name a few.

Using JRuby on Heroku

In order for Heroku to use JRuby instead of MRI, I’ve added the following to my Gemfile leveraging Bundler’s ruby directive.

1
ruby '1.9.3', engine: 'jruby', engine_version: '1.7.0'

To get JRuby 1.7.0 locally I used rbenv install jruby-1.7.0.

Applying JRuby specific buildpack

For Heroku to get all the plumbing needed for JRuby setup, add this buildpack.

1
heroku config:add BUILDPACK_URL=git://github.com/jruby/heroku-buildpack-jruby.git

JRuby specific gems

Because the pg (postgres ruby gem) includes c-extensions, it will not work with JRuby. An alternative to use with JRuby is jdbc-postgres. Hence, I added the following to my Gemfile replacing the pg gem.

1
2
3
gem 'activerecord-jdbc-adapter'
gem 'activerecord-jdbcpostgresql-adapter'
gem 'jdbc-postgres'

I previously used Thin as the webserver for my app, but given my move to JRuby I wanted a webserver which utilized threads better. I chose Trinidad for this task as it appears to perform quite well according to this benchmark. Make the following changes to use Tinidad.

1
2
3
4
5
# Gemfils
gem 'trinidad', require: nil

# Procfile
web: bin/trinidad -t -r -p $PORT -e $RACK_ENV

Compiling assets

One final change I had to make was ensuring assets would compile properly using JRuby. This require me to replace therubyracer with therubyrhino.

1
2
3
4
5
6
7
8
9
10
# Gemfile
gem 'therubyrhino'

# config/application.rb
config.assets.initialize_on_precompile = false

# config/environments/production.rb
config.serve_static_assets = true
STDOUT.sync = true
config.logger = Logger.new(STDOUT)

For fast asset pre-compilation I’ve also added the Google Closure Compiler.

1
2
3
4
gem 'closure-compiler', group: :assets

# config/environments/production.rb
config.assets.js_compressor = :closure

Going threadsafe!

One final thing I wanted to change in order to leverage the JVM was to enable Rails to use multiple threads for requests by setting config.treadsafe! in config/environments/production.rb'

Issues

As I encounter issues I will update this section with a corresponding solution.

/usr/bin/env: jruby: No such file or directory

Ensure that your PATH includes jruby/bin. To inspect whether it does you can run heroku run export and to set it run heroku config:add PATH="bin:jruby/bin:/usr/bin:/bin".

Observations

Given my project only has two users I’ve yet to see any noticable differences between MRI and JRuby fo rmy site. One downside of the move however is that the slug size has increased quite dramatically from around 20MB to 67MB. This only affects app startup/scaling times and is most likely because the slug now contains JRuby (JVM, etc).

I plan to write a follow up post once I’ve collected some data in NewRelic as to how JRuby performs compared to MRI.

Show Rails Environment in Pry

| Comments

I believe that environment awareness is important. Especially when it comes to your Rails environment. Hence, I added the following lines to my Pry config to have the prompt include the current environment.

1
2
3
4
5
6
7
if defined?(Rails)
  Pry.config.prompt = [proc { env }, "     | "]
end

def env
  Rails.env.production? ? "\e[1;31m#{Rails.env}\e[0m > " : "#{Rails.env} > "
end

Ignore Specific Requests With VCR

| Comments

I recently needed to test the integration between two Rails apps running locally on my machine and wanted to record the HTTP interactions with VCR. VCR enables you to mock HTTP requests by recording interactions and later replay them when you run your tests. This is useful when dealing with third-party APIs and if your application is interaction with other applications you don’t want to have running to make your tests pass.

VCR has an option which disables it from mocking any requests to localhost. As I turn this option off, another problem arose; it wouldn’t allow selenium-webdriver to connect to http://127.0.0.1/_IDENTITY_ (why it needs this I do not know). In searching for an answer I came upon this neat, and seemingly undocumented, feature of VCR.

1
2
3
c.ignore_request do |request|
  URI(request.uri).host == '127.0.0.1'
end

Another use-case could be to ignore all requests to Facebook:

1
2
3
c.ignore_request do |request|
  URI(request.uri).host =~ %r{.*facebook\.com/.*}
end

The feature was enabled in this commit, as a response to this issue. So to enable this feature you need version 2 or greater of VCR.

:wq

Radical Feature Justification

| Comments

Reading Eric Ries’ The lean Startup has recently got me thinking about how best to avoid wasting time developing the wrong things. Spending time developing a feature is always in vain if not one uses it, no matter how sound the idea and implementation might be.

There are many solutions to determining whether a feature should be added to a product or not. Methods like user surveys and questionnaires are proven to work, but adds another level of abstraction to the scenario. Whether a user thinks he/she needs (and would use) a given feature is different to actually using it.

So instead of asking whether a user would use a given feature, why not just add the actionable object (link, button, etc.) to the product and register whenever a user tries to use it?

Given it is clear what the actionable object does, this method of feature justification moves the focus from something users might need to them actually trying to do it.

Whenever a user would try to use the fake feature, it should be registered that a user tried to do so and a helpful message should be displayed. The message could go something like this:

center

Being constantly reminded that a feature is unavailable is annoying, therefore the actionable object should only be visible until the user has interacted with it.

One enough users have tried to use the proposed feature, it is safe to say that it’s development has been justified. When the feature is the added it would be great if users who tried to use it before are shown a message on first use, thanking them for their patience and feedback.

Note: This theory is yet to be testing in the wild, so don’t blame me if you end up pissing off your customers and bankrupting your company.

Import Heroku Databases Locally

| Comments

Short version

I just discovered a way simpler solution. Add the taps gem to your Gemfile, and run heroku db:pull. Voila!

Long (non-rails) version

I have recently started a pet-project which emails me a random piece of text each day, to remind me of something. I am making this as I often read something which gives an insight that I want to be reminded of in the future for it to be properly ‘persisted’ in my brain. Stay posted for the MVP.

Backing up Postgres on Heroku

First you need to add the PG Backups addon to your app:

heroku addons:add pgbackups:auto-month

The auto-month plan is currently free, but in case that changes you can see the other plans at https://addons.heroku.com/pgbackups

With the addon installed you can now run the following script to import your production postgres database into you local postgres database.

#!/bin/bash

heroku pgbackups:capture --expire

curl -o latest.dump `heroku pgbackups:url`

pg_restore --verbose --clean --no-acl --no-owner -h localhost -U <username> -d <database> latest.dump

Remember to replace the username and database parameters.

In case you don’t want to be asked for a password every time, you can create a ~/.pgpass file containing you postgres details using the following format: hostname:port:database:username:password. Give that file chmod 0600 ~/.pgpass permissions and add the parameter -w to the script above.

Metrics Driven Development

| Comments

The statement “If you can’t measure it, you can’t optimize it” really sits well with me. I my short spanned working career I have already spent time on code which did not result in any added business value. One example of doing this was a large refactoring on the persistence layer of an app. Although this cleaned up the code, it did not change anything seen from a business perspective. E.g. not measurably driving the business forward. This talk by Coda Hale made me realize that if a feature/change doesn’t add any business value, then there is absolutely no reason for spending time (and your employers money) on it.

There exists loads of tools that can help measure metrics of an application, and in doing that, you can easily justify or reject a feature or change. A great service for measuring metrics is Instrumental. Basically you pay for not having to fiddle around setting up a metrics solution, and can therefore start measuring your app immediately.

In short: If you can measure the effect of a change/feature, you are guessing on the effect it will have on you business, and most likely shouldn’t be working on it.