Rails 6 introduced a new functionality to configure and manage multiple databases in our application.
The best examples for multiple database configuration are:
- Primary replica configuration, where all writes are executed on the primary and all reads on replica database.
- Application has multiple databases to deal with.
For e.g, we store
ordersof user in one database and theirarchivedorders in different database.
To set multiple databases in our Rails application we need to set our
database.yml as below
development:
primary:
<<: *default
database: primary_database
primary_replica:
<<: *default
database: primary_database_replica
replica: true
secondary:
<<: *default
database: secondary_databaseHere, we have created two databases primary and secondary.
We have also added one replica to the primary database.
The reads for primary database will be performed from primary_database_replica
and writes from primary_database.
But, for secondary both reads and writes will be performed on
secondary_database.
There can be cases when we want to block the writes on the database:
- While performing a read operation.
- While executing a code block or transaction.
Examples for this use case can be:
- Converting a database from a single DB to a primary/replica
setup.
As shown in the above example, if we try to split
secondarydatabase intosecondary_database(for write operation) andsecondary_database_replica(for read operation). - Switching between databases i.e., from primary to replica or replica to primary and want to make sure that writes are not performed on replica.
To tackle the above issue, Rails 6 added ability to block writes to a database, even if the database user has permission to write (the database is a primary and not a replica).
ActiveRecord::Base.connected_to(role: :writing) do
User.connection.while_preventing_writes do
# will raise because we're blocking writes
User.create!
end
end
ActiveRecord::Base.connected_to(role: :reading) do
User.connection.while_preventing_writes do
# will not raise because we're not writing
User.first
end
endAs per the above example, database user can perform write operation
(role: :writing).
But, if we call while_preventing_writes and try to create a User using
User.create!, it will raise an exception.
If we are in read role and call while_preventing_writes, no error is raised
while running User.first, since we are not writing.
Rails internally identifies whether a query is a read or write.
So, when SQL queries like INSERT, UPDATE are executed and writes are blocked
the ActiveRecord::StatementInvalid error will be raised.
