A quick dive into query_constraints config in Rails 7.1
source link: https://blog.kiprosh.com/rails-7-adds-column-constraints-for-an-activerecord-base-object/
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
rails_7 Published on 11 January 2023 • Updated on 13 January 2023 • 2 min read
A quick dive into the new query_constraints config introduced in Rails 7.1
ActiveRecord in its current state is designed to work with the primary key column (the default is the id
column). Hence, if you notice the update
, destroy
, and reload
actions on an ActiveRecord::Base object, it fetches the data based on the id
column, and would not have the flexibility to choose any different set of constraints while performing those 3 actions.
Here's an example of reload
action on the Post
model instance(post.reload
).
Post Load (1.3ms) SELECT "posts".* FROM "posts" WHERE "posts"."id" = $1 LIMIT $2 [["id", 1], ["LIMIT", 1]]
If the app we have set up has good composite indexes, before the ActiveRecord query_constraints config (introduced in Rails 7.1), the ActiveRecord could not apply such constraints by default & utilize the composite indexes, especially with the update
, destroy
, and reload
actions.
The new ActiveRecord config query_constraints
introduced in PR changes this behavior and allows us to specify specific columns whenever fetching/performing actions on an ActiveRecord::Base object, we'll learn how.
Let's understand the need of query_constraints
with a relatively simple example of an eCommerce SaaS model of Store & Product
:
# == Schema Information
#
# Table name: products
#
# id :integer(4) not null, primary key
# store_id :integer(4)
# category_id :integer(4)
# title :string(255)
# Indexes
# :id -> Unique, Primary Key
# [:store_id, :category_id] -> Composite Index
class Product < ActiveRecord::Base
belongs_to :store
end
product = Product.first
# => SELECT "products"."*" from "products" LIMIT 1
product.update(title: "Lord of the Rings")
# => UPDATE "products" SET "title" = 'Lord of the Rings' WHERE "products"."id" = 1
In the above example, we see that the id
column was used in the where clause, and we were not able to properly utilize the composite indexes store_id
and category_id
.
Now let's see the behavior after adding the query_constraints
to the same setup:
# == Schema Information
#
# Table name: products
#
# id :integer(4) not null, primary key
# store_id :integer(4)
# category_id :integer(4)
# title :string(255)
# Indexes
# :id -> Unique, Primary Key
# [:store_id, :category_id] -> Composite Index
class Product < ActiveRecord::Base
query_constraints :store_id, :category_id, :id
belongs_to :store
end
product = Product.first
# => SELECT "products"."*" from "products" LIMIT 1
product.update(title: "Lord of the Rings")
# => UPDATE "products" SET "title" = 'Lord of the Rings'\
# WHERE "products"."store_id" = 1 AND "products"."category_id"='11' AND "products"."id" = 1
In this example, with query_constraints
configured, the query automatically and effectively uses our composite indexes store_id
and category_id
.
The query_constraints might be vital for the apps that are designed to work with composite primary keys, for which a prime example would be a Multi-Tenant sharded database design.
What a Multi-Tenant sharded database is?
A database can be designed depending on performance, scaling, and maintenance requirements.
In a multi-tenant sharded database pattern, the table schemas inside each database have a tenant key in the primary key of tables that stores the tenant data. This "tenant key" enables each individual database to have 1 or many tenants.
A tenant's data can be distributed across multiple databases or shards, where all the data for a single tenant is contained in one shard.
Coming back to our above example of the eCommerce SaaS model of Store & Product
, consider if we decided to have Database sharding on the store_id
column, the default constraints become a must-have.
To conclude:
With the changes like Allow specifying columns to use in ActiveRecord::Base object queries, the Rails framework is being prepared to provide generic solutions to the Multi-Tenant Sharded database implementations. With this, the conventional dependency of having anid
column for an ActiveRecord::Base object will go away and provide us the ability to tread the composite columns as the primary key(like in our example it was[:store_id, :category_id]
).
Karan Valecha 👨🏻💻
Lead Software Engineer at Kiprosh
Recommend
About Joyk
Aggregate valuable and interesting links.
Joyk means Joy of geeK