Now that the stable 6.0 has officially released, let’s see how easy it is to configure the CI for your multi-database apps.

The new multiple database support makes it easy for a single application to connect to multiple databases at the same time! You can either do this because you want to segment certain records into their own databases for scaling or isolation, or because you’re doing read/write splitting with replica databases for performance. Either way, there’s a new, simple API for making that happen without reaching inside the bowels of Active Record. The foundational work for multiple-database support was done by Eileen Uchitelle and Aaron Patterson.

Basic setup

If you haven’t gotten your hands dirty by playing around with this feature, here goes a basic configuration to get things running fast and easy. This configuration is meant to be deployed on Heroku

database.yml

default: &default

adapter: postgresql

encoding: unicode

pool: <%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %> development:

native:

<<: *default

database: my_app_development

extra_db:

<<: *default

database: extra_db_development test:

native:

<<: *default

database: my_app_test

extra_db:

<<: *default

database: extra_db_test production:

native:

<<: *default

url: <%= ENV['MY_APP_DATABASE_URL'] %>

sapaad_main:

<<: *default

url: <%= ENV['EXTRA_DATABASE_URL'] %>

Schema file(s)

As you might be already wondering, your app should have the schema files for 2 of the databases you have. In this case, the naming should be like:

config/native.rb

config/extra_db_schema.rb

When you run rails db:create , rails will create 2 databases for you and these schema files.

You also have access to these commands:

$ rails -T rails db:create # creates both databases rails db:create:native # creates just native database rails db:create:extra_db # creates just extra_db database rails db:drop # drops both databases rails db:drop:native # drop just native database rails db:drop:extra_db # drop just extra_db database rails db:migrate # migrates both databases rails db:migrate:native # migrate just native database rails db:migrate:extra_db # migrate just extra_db database rails db:migrate:status # status of both databases rails db:migrate:status:native # status of just native db rails db:migrate:status:extra_db # status of just extra_db db

Connection Switching API

Once the databases have been set up, you’ll have to configure the “roles” of each database. Each database can be configured to have any of or both the roles:

Reading Writing

The official rails documentation covers all the important features.

So in your application_record.rb , you can do something like:

# models/appication_record.rb connects_to database: { writing: :native, reading: :extra_db }

This example makes very little sense, you would never want to “always” write to a database and read from another. This can happen if :extra_db is a follower of the :native database. This can be also configured in the database.yml file.

You can also configure the connection switching on per model basis like this:

# models/post.rb connects_to database: { writing: :native, reading: :native } # models/car.rb connects_to database: { writing: :extra_db, reading: :extra_db }

Deployment

Before deploying in Heroku, we will need to add 2 databases.

To add these 2 databases to Heroku as add-ons, you can run these commands on the Heroku CLI

heroku addons:create heroku-postgresql:hobby-dev --as MY_APP_DATABASE heroku addons:create heroku-postgresql:hobby-dev --as EXTRA_DATABASE

addons:create will create the mentioned add-on, which is heroku-postgresql:hobby-dev . The --as flag is used to customize the name of the add-on. Naming an add-on makes it easy to specify it in the database.yml file, Heroku ENVs, and in the CI configuration.

This can also be achieved by using an app.json file which configures the development setup of Heroku on the first deployment.

Once this is done, deploy the app as you would normally do. It should work straight away.

git push heroku master (If you have Heroku CLI, git initialized, has set up the Heroku remotes)

Heroku Pipeline CI

Heroku supports pipelines and it can trigger the test suite to run automatically on each deployment.

For this, you have to configure the test environment for this app to run. This will also include adding 2 databases to the test environment. This is done by app.json . More information on app.json can be found here.

So, create a new app.json file in the root of your application.

{

"name": "My App",

"description": "This app does one thing and it does it well",

"env": {

},

"formation": {

"test": {

"quantity": 1,

"size": "standard-1x"

}

},

"addons": [

],

"buildpacks": [

{

"url": "heroku/ruby"

}

],

"environments": {

"test": {

"buildpacks": [

{ "url": "heroku/ruby" }

],

"addons": [

{

"plan": "heroku-postgresql",

"as": "MY_APP_TEST_DATABASE"

},

{

"plan": "heroku-postgresql",

"as": "EXTRA_DB_TEST_DATABASE"

}

],

"scripts":

{

"test-setup": "",

"test": "bundle exec rspec -fd"

}

}

}

}

As you can see under the test environment, we have mentioned 2 Heroku add ons with customized names. This will generate customized URLs to those databases as well.

Database Name Database URL MY_APP_TEST_DATABASE - MY_APP_TEST_DATABASE_URL

EXTRA_DB_TEST_DATABASE - EXTRA_DB_TEST_DATABASE_URL

These database URLs will be set as environment variables and you can access them as:

ENV['MY_APP_TEST_DATABASE_URL'] and

ENV['EXTRA_DB_TEST_DATABASE_URL']

Heroku also sets an env named CI with the value true among other variables, while in the test environment.

This makes it easy to specify these databases in our app, for the test suits. So change the database.yml file to:

default: &default

adapter: postgresql

encoding: unicode

pool: <%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %> development:

native:

<<: *default

database: my_app_development

extra_db:

<<: *default

database: extra_db_development <% if ENV['CI'] == 'true' %>

test:

native:

<<: *default

url: ENV['MY_APP_TEST_DATABASE_URL']

extra_db:

<<: *default

url: ENV['EXTRA_DB_TEST_DATABASE_URL']

<% else %>

test:

native:

<<: *default

database: my_app_test

extra_db:

<<: *default

database: extra_db_test

<% end%> production:

native:

<<: *default

url: <%= ENV['MY_APP_DATABASE_URL'] %>

sapaad_main:

<<: *default

url: <%= ENV['EXTRA_DATABASE_URL'] %>

This will allow the app to chose the database URLs from the ENVs set, when in the Heroku’s CI environment.

Now if you have enabled Heroku Pipelines and automatic test runs, your test suite should work as expected