Skip to main content

Benjamin Oakes

Photo of Ben Oakes

Hi, I'm Ben Oakes and this is my geek blog. Currently, I'm a Ruby/JavaScript Developer at Liaison. Previously, I was a Developer at Continuity and Hedgeye, a Research Assistant in the Early Social Cognition Lab at Yale University and a student at the University of Iowa. I also organize, ICRuby, OpenHack Iowa City, and previously organized NewHaven.rb. I have an amazing wife named Danielle Oakes.

Filtering for the month November, 2011. Clear

Blocking migration creation in Rails

by Ben

We have a project that accesses a shared database. To prevent problems (and confusion) we wanted to block the creation of migrations in the “secondary” applications so that there was one authoritative place for migrations to live.

It’s easy to accomplish in a simple way.

If you’ve never made any migrations in the secondary application, just do this:

$ echo "NOTE Please do not make migrations in this project.  They should all live in _primary app_." > db/migrate

That way, when you run rails g migration foo, you’ll get this:

$ rails g migration foo
      invoke  active_record
      create    db/migrate/20111129162804_foo.rb
[...]/ruby-1.9.2-p180/lib/ruby/1.9.1/fileutils.rb:243:in `mkdir': File exists - [...]/db/migrate (Errno::EEXIST)

When you try to cd into that directory, you’ll get this:

$ cd db/migrate
bash: cd: db/migrate: Not a directory

After you get over the initial “wha?” reaction, you’ll look at db/migrate and see the message:

$ cat db/migrate
NOTE Please do not make migrations in this project.  They should all live in _primary app_.

Pretty nice, right?

Using Heroku with an external MySQL database

by Ben

We recently had a need to use Heroku with an external MySQL database. Thankfully, there was already a gem that solved the problem. However, it didn’t have that much by the way of documentation, so I wrote this up and contributed it to the author. Also, I added X.509 support, which we required.

NOTE: After my pull requests are merged, you should look to the main repository’s README which has a copy of the below.

Example: MySQL with a CA Certificate

In this example, we are setting up a connection to an external MySQL server using the default initializer.

What we have:

First, we configure Heroku with the appropriate environment variables:

# You may have to specify the app name or remote name here via --app or --remote, respectively
heroku config:add EXTERNAL_DATABASE_CA='ca-cert.pem'
heroku config:add EXTERNAL_DATABASE_URL='mysql://username:password@server/dbname'

By default, heroku_external_db looks for the CA cert in
config/ca, so we need to commit it:

mkdir -p config/ca
cp path/to/ca-cert.pem config/ca
git add config/ca
git commit -v # Using -v since we want to make sure the contents are what we expect (e.g. not a private key)

Additionally, we need the mysql gem in our Gemfile since we are setting up a MySQL server:

echo "gem 'mysql', '~> 2.8.1'" >> Gemfile
bundle install # Need Gemfile.lock too

Keep in mind that Heroku installs its own database.yml for Rails apps and we have to install pg as well. Unfortunately, shared databases are mandatory (but are free).

$ heroku addons:remove shared-database:5mb
-----> Removing shared-database:5mb from our-app... failed
 !     Shared databases cannot be removed

PostgreSQL may still be useful to you if, for example, you want to have feature toggles in a local database, but the main data kept externally. However in our case, it also means all developers will need MySQL and PostgreSQL running locally, which is unfortunate.

One workaround is only installing pg in production:

# File: Gemfile

# *Only* needed on production.
group :production do
  gem 'pg', '~> 0.11.0' # Regardless of whether you plan to use the database or not, Heroku requires you have 'pg' installed.

Then bundle like so:

bundle install --without production

With our gems updated, commit:

git commit -av # Commit Gemfile and Gemfile.lock

Another option may be overriding database.yml somehow, although it would add complexity. For more info, please see

With our dependencies out of the way, we can move on to testing the connection.

If you are making a new application, you may wish to have a simple MVC for testing that the connection works. E.g., for a blog style application with posts do:

rails generate scaffold post
# NOTE you probably want to change the default "Post.all" to "Post.limit(5)" or something similar
git add .
git commit # ...
# Don't forget to set a default route, etc.

With all these changes committed, we can deploy to our Heroku app:

git push heroku master # Your remote may be different

Now, since we are connecting to an existing database, we don’t need to run any migrations. (Keep in mind that when sharing a database, it is best to have one authoritative source for migrations to live.) If in your situation you’re creating a new database, you may need to do that, run migrations, seed the database, etc at this point.

Open and we should see our data. If you happen to run into a problem, please check the logs first:

heroku logs --tail # Again, you may need to specify an app

If you are having a problem, a good starting point is double checking your passwords, usernames, security settings, etc.

Example: MySQL with X.509

The process is very much the same as the above example, except two extra environment variables and files are required. Below are the extra steps.

What we have:

First, we configure Heroku with the appropriate environment variables:

heroku config:add EXTERNAL_DATABASE_CA='ca-cert.pem'
heroku config:add EXTERNAL_DATABASE_CERT='client-cert.pem'
heroku config:add EXTERNAL_DATABASE_KEY='client-key.pem'

By default, heroku_external_db looks for the files in config/ca, so we need to commit them:

mkdir -p config/ca
cp path/to/ca-cert.pem path/to/client-cert.pem path/to/client-key.pem config/ca
git add config/ca
git commit

The rest of the process is the same as in “MySQL with a CA Certificate”.