Rails 7 adds disable_joins for associations

Ashik Salman

Ashik Salman

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.

Stay up to date with our blogs.

Subscribe to receive email notifications for new blog posts.