Use Readyset with Ruby on Rails
This guide walks you through how to use Readyset with your Ruby on Rails app. To do this, you'll use the built-in Rails multiple database support (opens in a new tab) to get the performance of caching with the simplicity of a single read replica.
0. Set up Readyset
First, you'll set up Readyset using Docker.
1. Configure Rails in three steps
Ruby On Rails allows you to configure Readyset as a read replica and define a time window after writes for when to switch read traffic over to Readyset. This allows clients to observe recent writes if there is write traffic ongoing while still taking advantage of Readyset's caching for read-heavy workloads.
This will take three steps. You'll need to add Readyset as a read replica, then configure ActiveRecord to route traffic, and finally configure Rails' connection switching middleware with a few quick changes.
a. Update database configuration
Taking advantage of Readyset means defining a read replica of your primary database.
Rails makes this incredibly simple and in the case of Readyset, you'll configure the replica to point
to your Readyset instance. We recommend following Rails established patterns and name your primary
database as primary
and then your read replica Readyset instance as primary_replica
.
For instance, your database.yml
file will look something like this:
production:
primary:
database: my_primary_database
username: root
password: <%= ENV['ROOT_PASSWORD'] %>
adapter: postgresql
primary_replica:
database: my_primary_database
username: root
password: <%= ENV['READYSET_PASSWORD'] %>
host: <%= ENV['READYSET_HOST'] %>
port: 5433
adapter: postgresql
replica: true
database_tasks: false
It's important to note that the primary_replica
definition points to Readyset and that two
flags are set: replica
is true
and database_tasks
is false
. The database_tasks
flag
will disable Rails database migrations running against Readyset. This isn't needed as
Readyset will see any changes to the primary database already.
b. Update ApplicationRecord
to use two connections
In your application's app/models
directory, you'll find a base class for all of your model classes
often called ApplicationRecord
. Open up that class and add the following line:
connects_to database: { writing: :primary, reading: :primary_replica }
If you haven't made any changes to this base class, it should look like this:
class ApplicationRecord < ActiveRecord::Base
primary_abstract_class
connects_to database: { writing: :primary, reading: :primary_replica }
end
Note how connects_to database:
is pointing to the names of both the primary database and Readyset
via the names you defined in your database.yml
configuration.
If you aren't on Rails 7, you'll need to replace primary_abstract_class
with self.abstract_class = true
.
What's more, if for some reason you do not have access to an ApplicationRecord
base class, you'll need to
monkey patch (opens in a new tab) one.
c. Activate connection switching middleware
Finally, you'll need to activate Rails middleware that'll ensure writes go to the primary database and reads through Readyset. First run this command to generate a configuration file:
bin/rails generate active_record:multi_db
This command will create a new file: config/initializers/multi_db.rb
. Open up that file and uncomment
the following lines:
Rails.application.configure do
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
end
It's important to note that a delay of 2 seconds is designed to ensure the read replica (in this case Readyset) is consistent with writes that are going directly to the primary database.
You're off to the races!
At this point, if you start your Rails application, read requests will go directly to Readyset and writes will go to your primary Postgres database. You'll next need to configure which SQL select statements (i.e. reads) you’ll want to cache. Check out our caching guide for more information.