Rails 7: バックグラウンドジョブで削除する最大レコード数を指定可能になった(翻訳)
Rails 6.1で追加された:destroy_async
オプションは、foreign_key
制約が無効な場合は常に、関連付けられたレコードをバックグラウンドジョブで削除します。
しかし関連付けられたレコード数があまりに多い場合はどうでしょうか。この場合、デフォルトでは全レコードを1件のバックグラウンドジョブで削除するので時間がかかる可能性があります。
Railsではこの処理を高速化するために、1件のバックグラウンドジョブで削除する最大レコード数を指定する設定がActive Recordに追加され、レコード数がこの上限を超えたらジョブをキューに追加できるようになりました。
例: 以下のProduct
モデルと Review
モデルがあるとします。
# product.rb
class Product < ApplicationRecord
has_many :reviews, dependent: :destroy_async
end
# review.rb
class Review < ApplicationRecord
belongs_to :product
end
ここではreview
レコードをバックグラウンドで削除する:destroy_async
オプションを追加してあります。
改修前
=> Product.find(1).destroy
=> Performing ActiveRecord::DestroyAssociationAsyncJob (Job ID: 880513a7-f6c4-4a35-8d33-6d69a737031e) from Async(default) enqueued at 2022-06-03T11:15:13Z with arguments: {:owner_model_name=>"Product", :owner_id=>1, :association_class=>"Review", :association_ids=>[1, 2, 3], :association_primary_key_column=>:id, :ensuring_owner_was_method=>nil}
Product Load (1.6ms) SELECT "products".* FROM "products" WHERE "products"."id" = $1 LIMIT $2 [["id", 1], ["LIMIT", 1]]
Review Load (1.7ms) SELECT "reviews".* FROM "reviews" WHERE "reviews"."id" IN ($1, $2, $3) ORDER BY "reviews"."id" ASC LIMIT $4 [["id", 1], ["id", 2], ["id", 3], ["LIMIT", 1000]]
TRANSACTION (1.2ms) BEGIN
Review Destroy (1.4ms) DELETE FROM "reviews" WHERE "reviews"."id" = $1 [["id", 1]]
TRANSACTION (1.7ms) COMMIT
TRANSACTION (1.1ms) BEGIN
Review Destroy (1.3ms) DELETE FROM "reviews" WHERE "reviews"."id" = $1 [["id", 2]]
TRANSACTION (1.6ms) COMMIT
TRANSACTION (1.1ms) BEGIN
Review Destroy (1.2ms) DELETE FROM "reviews" WHERE "reviews"."id" = $1 [["id", 3]]
TRANSACTION (1.6ms) COMMIT
Performed ActiveRecord::DestroyAssociationAsyncJob (Job ID: 880513a7-f6c4-4a35-8d33-6d69a737031e) from Async(default) in 51.36ms
ご覧のように、IDが880513a7-f6c4-4a35-8d33-6d69a737031e
の非同期ジョブ1件だけですべてのレビューを削除しています。
改修後
以下のように、1件のバックグラウンドジョブで削除する最大レコード数を指定するdestroy_association_async_batch_size
コンフィグを追加できるようになりました。
# config/environments/development.rb
config.active_record.destroy_association_async_batch_size = 10
これで、レビューが50件あるProductの削除を試みると、これらをすべて削除する5つのDestroyAssociationAsyncJobs
がキューに追加されます。
=> Product.find(2).reviews.count
=> 50
=> Product.find(2).destroy
=> Enqueued ActiveRecord::DestroyAssociationAsyncJob (Job ID: c141dbf1-124a-4659-89cd-15e89ddec6fe) to Async(default) with arguments: {:owner_model_name=>"Product", :owner_id=>2, :association_class=>"Review", :association_ids=>[4, 5, 6, 7, 8, 9, 10, 11, 12, 13], :association_primary_key_column=>:id, :ensuring_owner_was_method=>nil}
Enqueued ActiveRecord::DestroyAssociationAsyncJob (Job ID: 9e3d67ef-aebd-4e56-b6d1-d6bc520d8b74) to Async(default) with arguments: {:owner_model_name=>"Product", :owner_id=>2, :association_class=>"Review", :association_ids=>[14, 15, 16, 17, 18, 19, 20, 21, 22, 23], :association_primary_key_column=>:id, :ensuring_owner_was_method=>nil}
Enqueued ActiveRecord::DestroyAssociationAsyncJob (Job ID: a5910f83-806d-467e-a9cf-0e40701d91aa) to Async(default) with arguments: {:owner_model_name=>"Product", :owner_id=>2, :association_class=>"Review", :association_ids=>[24, 25, 26, 27, 28, 29, 30, 31, 32, 33], :association_primary_key_column=>:id, :ensuring_owner_was_method=>nil}
Enqueued ActiveRecord::DestroyAssociationAsyncJob (Job ID: 1588b961-b531-4ed7-acaa-8cbc71a98103) to Async(default) with arguments: {:owner_model_name=>"Product", :owner_id=>2, :association_class=>"Review", :association_ids=>[34, 35, 36, 37, 38, 39, 40, 41, 42, 43], :association_primary_key_column=>:id, :ensuring_owner_was_method=>nil}
Enqueued ActiveRecord::DestroyAssociationAsyncJob (Job ID: b42848f7-e958-4b47-8670-e0cde6fc0cd9) to Async(default) with arguments: {:owner_model_name=>"Product", :owner_id=>2, :association_class=>"Review", :association_ids=>[44, 45, 46, 47, 48, 49, 50, 51, 52, 53], :association_primary_key_column=>:id, :ensuring_owner_was_method=>nil}
Performed ActiveRecord::DestroyAssociationAsyncJob (Job ID: 1588b961-b531-4ed7-acaa-8cbc71a98103) from Async(default) in 119.36ms
TRANSACTION (2.0ms) COMMIT
Performed ActiveRecord::DestroyAssociationAsyncJob (Job ID: b42848f7-e958-4b47-8670-e0cde6fc0cd9) from Async(default) in 120.38ms
TRANSACTION (2.3ms) COMMIT
Performed ActiveRecord::DestroyAssociationAsyncJob (Job ID: a5910f83-806d-467e-a9cf-0e40701d91aa) from Async(default) in 122.97ms
TRANSACTION (1.6ms) COMMIT
Performed ActiveRecord::DestroyAssociationAsyncJob (Job ID: c141dbf1-124a-4659-89cd-15e89ddec6fe) from Async(default) in 123.75ms
TRANSACTION (1.3ms) COMMIT
Performed ActiveRecord::DestroyAssociationAsyncJob (Job ID: 9e3d67ef-aebd-4e56-b6d1-d6bc520d8b74) from Async(default) in 166.56ms
原注: この拡張は公式のRailsではまだリリースされていません。
詳しくは#44617を参照してください。
概要
元サイトの許諾を得て翻訳・公開いたします。
#44617は、現時点ではRailsのmainブランチにのみ含まれています。
参考: 週刊Railsウォッチ20220523 関連付けの非同期削除でバッチサイズを設定可能になった