October 26, 2021
This blog is part of our Rails 7 series.
Rails 7 introduces disable_joins
for database associations to avoid join
errors in multi tenant applications where the two tables are located in
different database clusters.
Rails commonly perform lazy loading while fetching records for better efficiency and internally build join queries to fetch the records faster.
But when we deal with different database clusters within the same application, the lazy loading nature of Active Record causes errors when databases can't perform join queries between different clusters.
As a resolution for this, Rails 7 has newly added 'disable_joins' option to tell Rails upfront that the queries have to be performed without joining tables. In this case two or more queries will be generated and used to fetch the results from different database clusters.
The disable_joins
option is available for both has_many :through
and
has_one :through
associations. In some cases, if order or limit is applied, it
will be performed in-memory due to database limitations.
class Employee
has_many :projects
has_many :tasks, through: :projects, disable_joins: true
end
class Project
belongs_to :employee
has_many :tasks
end
class Task
belongs_to :project
end
Before Rails 7, @employee.tasks
without disable_joins
option will raise
error because database clusters can't handle join queries here. If we provide
the disable_joins
option as true (by default the value is set to false) then
Rails will make two or more separate queries to fetch the results from different
database clusters.
SELECT "project"."id" FROM "projects" WHERE "projects"."employee_id" = ? [["employee_id", 1]]
SELECT "tasks".* FROM "tasks" WHERE "tasks"."project_id" IN (?, ?, ?) [["project_id", 1], ["project_id", 2], ["project_id", 3]]
Similarly for has_one through association:
class Publisher
has_one :author
has_one :book, through: :author, disable_joins: true
end
class Author
belongs_to :publisher
has_one :book
end
class Book
belongs_to :author
end
The @publisher.book
will make the following two queries to fetch the results.
SELECT "author"."id" FROM "authors" WHERE "authors"."publisher_id" = ? [["publisher_id", 1]]
SELECT "books".* FROM "books" WHERE "books"."author_id" = ? [["author_id", 1]]
Please be aware that enabling this option without realising the actual need will result in performance implications since two or more queries are performed here. Also queries with order or limit will be done in-memory since the order from one database can't be applied to another database query.
Please checkout the disable_joins
pull requests for
has_many through &
has_one through associations for
more details and discussions.
If this blog was helpful, check out our full blog archive.