April 16, 2021

Feature Updates in Rails 6: Multiple Databases & State Management

Rails 6.0 recently shared its amazing enhancements although most would consider these as feature upgrades.

In my opinion, both are correct, as the actual state of multiple databases before rails 6.0 was not even considered it a completed feature.

So, let's dive into Rails < 6 state! Rails 6 introduced changes and its current roadmap.

Rails 5 State

ActiveRecord supports multiple databases, but Rails < 6 doesn't provide a way to manage them. As mentioned, you need to handle everything (yes, everything) in a really manual way, having to create your own tasks for [.c-inline-code]seeds[.c-inline-code], [.c-inline-code]db:prepare[.c-inline-code], [.c-inline-code]db:migrate[.c-inline-code], etc.

You also need to create a type of generator to handle your secondary database migrations, following conventions. And finally (as if you hadn't done enough custom work), you need to create an initializer to config your connection to the new database.

So, ActiveRecord does support multiple databases, but it is a complete and real mess you should avoid unless absolutely necessary.

Rails 6.0 State

New Stuff

  • Multiple primary databases and a replica for each.

This is amazing as now you can easily configure it in your [.c-inline-code]database.yml[.c-inline-code] like so:

    database: my_primary_database
    user: root
    adapter: sqlite3
    database: my_primary_database
    user: root_readonly
    adapter: sqlite3
    replica: true
    database: my_animals_database
    user: animals_root
    adapter: sqlite3
    migrations_paths: db/animals_migrate
    database: my_animals_database
    user: animals_readonly
    adapter: sqlite3
    replica: true

See how you can define your migration paths for each database, and how you have the power to use multiple adapters (still working on a POC for this assumption).

An example of migration with this would be: [.c-inline-code]rails g migration CreateCats name:string --database animals[.c-inline-code] with this output

  Running via Spring preloader in process 97210
    invoke  active_record
    create    db/animals_migrate/20200819181625_create_cats.rb

  • Automatic connection switching for the model you're working with.

You just need to activate it in your middleware for automatic switching:

config.active_record.database_selector = { delay: 2.seconds }
config.active_record.database_resolver = ActiveRecord::Middleware::DatabaseSelector::Resolver
config.active_record.database_resolver_context = ActiveRecord::Middleware::DatabaseSelector::Resolver::Session

You can also manually decide your connection context:

  • Automatically swapping between the primary and replica depending on the HTTP verb and recent writes.

As we are used to in Rails, this comes standardized:

“If the application is receiving a POST, PUT, DELETE, or PATCH request the application will automatically write to the primary. For the specified time after the write, the application will read from the primary.

For a GET or HEAD request, the application will read from the replica, unless there was a recent write.

  • Rails tasks for creating, dropping, migrating, and interacting with multiple databases.

First big comment, the usual [.c-inline-code]rails db:create[.c-inline-code] will now create both databases, and so the usual [.c-inline-code]rails db:migrate[.c-inline-code] will migrate both databases as well.

You need to specify which database you want to work with like [.c-inline-code]rails db:create:primary[.c-inline-code] or [.c-inline-code]rails db:migrate:secondary[.c-inline-code], if you want to just modify one.

So, for your custom tasks, you may need to also specify the desired database you are working with.

Pending Ones

  • For Sharding right now there are some gems to accomplish this, so until is integrated onto Rails we should keep using gems (no real deal with it).
  • When joining across clusters, Rails states: “Applications cannot join across databases. Rails 6.1 will support using has_many relationships and creating 2 queries instead of joining, but Rails 6.0 will require you to split the joins into 2 selects manually.“

So, we just need to wait for rails 6.1!

  • Load balancing replicas seem to be an interesting feature, I hope to see upcoming news for this.
  • For dumping schema caches for multiple databases, it states: “If you use a schema cache and multiple databases you'll need to write an initializer that loads the schema cache from your app. This wasn't an issue we could resolve in time for Rails 6.0 but hope to have it in a future version soon“

So, this seems like a trivial issue to solve in upcoming versions.

Rails 6.1 Upcoming State

As you may know, rails 6.1 is almost there, so, I will double-check what was stated to be released for this new version, and what is actually going to be released.

Here are some nice open PRs right now:

So, the summary would be:

  • Sharding is now a WIP thing and we may hope to see it live in Rails 6.1.
  • Still waiting for the Joining across clusters (or I missed it in my review).
  • Still waiting for load balancing.
  • Schema caches are now a thing!

Some Personal Ideas

I'm a huge fan of non-relational databases, so, I would love to see some mixins of MongoDB + Postgres for example. With the introduced changes, I think we can soon find a way to properly work with it.

My doubts are "Should ActiveRecord support non-relational databases?" or "Is something more rails work outside ActiveRecord?"

Would love to hear your thoughts. Feel free to leave a comment or connect with me about this topic!