Exploring Rails Applications with Multiple Databases

Exploring Rails Applications with Multiple Databases

When building a Rails application you will need to think about scaling as you get more and more users and data. One way in which your application may need to scale is at the database level. Rails supports using multiple databases, so you don’t have to store all your data in one place.

Since Rails launched version 6, it has been possible to use its powerful solution for scaling applications through multiple databases. This feature enables the application to distribute the load, optimize performance, and enhance the overall user experience.

A general rule of thumb is to monitor your database server’s performance metrics, such as CPU and memory usage, as well as query execution times. If you notice consistently high usage or increasing response times, it’s a sign that your database is struggling to handle the load.

As an application grows a single database may become a bottleneck. Multiple databases come into play to address this issue, providing a way to distribute data across different servers or clusters. This not only helps in managing increased traffic but also facilitates more efficient use of resources.

Configuring Multiple Databases in Rails:

Rails makes it remarkably easy to work with multiple databases. Using the default config/database.yml file as the starting point for configuring database connections and also defining additional databases.

# config/database.yml

default: &default
  adapter: postgresql
  encoding: unicode
  pool: <%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %>
  username: <%= ENV['DB_USER'] %>
  password: <%= ENV['DB_PASSWORD'] %>
  host: <%= ENV['DB_HOST'] %>

development:
  <<: *default
  database: myapp_development

secondary_db:
  <<: *default
  database: secondary_db_development

In the example above, a secondary database is configured alongside the default database. This additional database could be used for specific functionalities, such as handling user analytics or managing non-relational data.

Model-Level Configuration:

In your models you will have to specify which database a particular model will use. If you don’t specify it, Rails will use the primary database by default. This can be easily changed using the establish_connection config.

# app/models/user.rb
class User < ApplicationRecord
  # This model uses the default database (primary)
end

# app/models/secondary_model.rb
class SecondaryModel < ApplicationRecord
  establish_connection :secondary_db
  # This model uses the secondary database
end

Migrating Multiple Databases:

Rails tasks will also include and require migrations for this secondary database. You can run them by using namespaced tasks:

# Run migration for the default database
rails db:migrate

# Run migration for the secondary database
rails db:migrate:secondary_db

Database Replicas

Replicas is a widely used strategy in software applications to get performance improvements. Adapting an application to this practice will rise in scalability and load distribution, have a better availability and will also reduce load on the primary database.

Rails setup would look like this:

# config/database.yml
production:
  primary:
    database: my_primary_database
    adapter: mysql2
  primary_replica:
    database: my_primary_database
    adapter: mysql2
    replica: true

Making a User model to read from the replica would need to explicitly setup in the model file.

class User < ApplicationRecord
  connects_to database: {
    writing: :primary,
    reading: :primary_replica
  }
end

Horizontal Sharding

Horizontal sharding is like breaking down your database into smaller chunks, distributing the load and keeping the same structure across these slices.

One way to illustrate how horizontal sharding works is to think of an e-commerce website with millions of products. You could partition the product data based on categories. For instance, one shard could handle electronics, another could handle clothing, and so on.

Rails has got your back with an API that makes handling horizontal sharding a breeze.

# config/database.yml
production:
  primary:
    database: my_primary_database
    adapter: mysql2
  primary_shard_one:
    database: my_primary_shard_one
    adapter: mysql2
    migrations_paths: db/migrate_shards

Models can be connected with the connects_to API using the shard’s settings:

class ApplicationRecord < ActiveRecord::Base
  primary_abstract_class

  connects_to database: { writing: :primary, reading: :primary_replica }
end

class ShardRecord < ApplicationRecord
  self.abstract_class = true

  connects_to shards: {
    shard_one: { writing: :primary_shard_one, reading: :primary_shard_one_replica },
  }
end

We’re declaring and reading data from replicas. A replica database is essentially a copy of the primary database that is used for read operations. This helps distribute load and ensures that some heavy operations don’t impact the performance of the primary database.

Check out the following Rails documentation about horizontal sharding opens a new window for more information about this.

Benefits of Multiple Databases:

Scalability: Distributing data across multiple databases allows for better scaling, enabling applications to handle increased traffic and data loads.

Performance Optimization: Different databases can be used for different types of data, optimizing the performance of specific functionalities within the application.

Flexibility: Developers have the flexibility to choose the type of databases that best suit different data storage requirements, whether it’s a relational database for structured data or a NoSQL database for unstructured data.

Conclusion

Rails currently lacks built-in support for automatically balancing the load across replicas. The effectiveness of load balancing heavily relies on your specific infrastructure.

In conclusion, leveraging multiple databases in Rails applications is a powerful strategy for achieving scalability and optimizing performance. By configuring and managing databases at both the application and model levels, developers can ensure a smooth and efficient handling of data.

Need help assessing the right strategy to improve your Rails application’s performance? Talk to us! opens a new window

Get the book