Rails 6 is just around the corner. The release candidate two was recently released. This post is part of the Road to Rails 6 series which will prepare you for Rails 6.

When we create a new brand new Rails 6 app, it is configured to use only one database. To start using multiple databases, we need to switch the database configuration from something called as two-tier configuration to three-tier configuration.

Two tier v/s Three tier database configuration

To understand what it means, let's take a look at sample configuration from database.yml

production: database: cats_and_dogs_production adapter: postgresql

This is an example of two-tier configuration. The two tiers are the environment and the database configuration.

The three-tier configuration includes a middle tier for specifying multiple databases. It looks like this.

production: database_1: database: cats_and_dogs_production adapter: postgresql database_2: database: insights_production adapter: mysql2

When Rails finds this three tier configuration in the database.yml it will treat your Rails application as multi database application.

So to use multiple databases, we need to change the default two-tier configuration to three-tier configuration in the database.yml .

I created a sample Rails app to start with locally which has following database configuration for development environment.

default: &default adapter: sqlite3 pool: <%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %> timeout: 5000 development: <<: *default database: db/development.sqlite3

We will change this two tier configuration to three tier configuration for multiple databases.

development: primary: <<: *default database: db/development.sqlite3

primary indicates the main database the application will use. This is the same database configuration that was present for a single database application that we saw earlier. The only difference is that now it is three tier instead of two.

Rails treats primary key in a special manner. It is your main database.

Now we can add a second database animals to the database.yml .

development: primary: <<: *default database: db/development.sqlite3 animals: <<: *default database: db/animals_development.sqlite3

The primary database must come first and all the other databases must come after that. Though this is not mentioned anywhere I ran into random issues related to animals migrations being run in primary database if the order is switched both locally and on Heroku.

So far so good!

Creating models and migrations

Next step is to create few models. By default, all models in a Rails application inherit from ApplicationRecord and connect to single database. But now we want some of our models to connect to the primary database and some to the animals database. Rails 6 provides a way to connect to a database from a model as follows.

class Lion < ApplicationRecord connects_to database: { writing: :animals } end

The connects_to line simply tell Rails that the Lion model will connect to animals database for both writing and reading purpose. The word animals here matches with the animals key in our updated database.yml .

In real world scenario, you could use a read replica for reading operations to reduce load on the master database.

Ideally, instead of repeating this configuration in every model which needs to connect to animals database, we should create a base class for all animals models.

class AnimalBase < ApplicationRecord connects_to database: { writing: :animals } end class Lion < AnimalBase end

Now our models are configured to connect to the second database. Let's actually create some data to get started. We first need to create the tables in the animals database.

When we create migrations, by default they are created for primary database in the db/migrate directory. If we want to create migrations for animals directory, Rails recommends storing them in a separate directory. We need to add a configuration option migrations_paths in the database.yml for this purpose.

development: primary: <<: *default database: db/development.sqlite3 animals: adapter: sqlite3 database: db/animals_dev.sqlite3 migrations_paths: db/animal_migrate

We can create migrations for animals database by passing the database name to the migration generator command.

rails generate migration AddLions country:string age:integer --db=animals Running via Spring preloader in process 86684 invoke active_record create db/animal_migrate/20190805164716_add_lions.rb

This creates the new migration in animal_migrate directory which we have configured in the database.yml .

If we forget to add the migrations_paths key to the database.yml then Rails does not raise any error as of now but it means it will not create separate directory for the migrations related to animals database.

When we run rails db:migrate it runs migrations for all our databases including animals . We can also run migrations for a particular database.

rails db:migrate:primary rails db:migrate:animals

At this point of time, we are successfully using multiple databases locally. Time to go to production on Heroku.

Deploying multi database app on Heroku

We need to add pg gem for deploying on Heroku and update the production section of the database.yml . The first step is to change to three tier configuration for the primary database. But Heroku relies on the DATABASE_URL environment variable instead of connecting via username and password from datbase.yml .

There was a bug in Rails 6 RC2 related to multiple databases and DATABASE_URL which is fixed in the 6-0-stable branch. So use it instead of RC2 to work with multiple databases on Heroku.

Heroku sets the DATABASE_URL environment variable and it is used by Rails automatically so we don't need to specify it in the configuration.

production: primary: adapter: postgresql

Now to use the animals database, we need to provision it. If you are just playing around, you can add a PostgreSQL hobby addon to your app for this purpose.

Heroku provisions one PostgreSQL addon for your app by default. So you need to provision one more for using multiple databases.

The Heroku PostgreSQL addon generates a environment variable which has the configuration for the new database. In my case it was HEROKU_POSTGRESQL_OLIVE_URL which we will use to connect to the animals database.

production: primary: adapter: postgresql animals: migrations_paths: db/animal_migrate adapter: postgresql url: <%= ENV["HEROKU_POSTGRESQL_OLIVE_URL"] %>

That's all! Now our app is configured to use multiple databases on Heroku. When we deploy and run rails db:migrate everything will be setup nicely and we can start using multiple databases on Heroku with Rails 6.

The code for this article can be found here. In the next article we will learn advanced techniques supported by multi database feature in Rails 6.

Subscribe to my newsletter to be on the Road to Rails 6.