Tech Racho エンジニアの「?」を「!」に。
  • Ruby / Rails関連

Rails 7: バックグラウンドジョブで削除する最大レコード数を指定可能になった(翻訳)

概要

元サイトの許諾を得て翻訳・公開いたします。

#44617は、現時点ではRailsのmainブランチにのみ含まれています。

参考: 週刊Railsウォッチ20220523 関連付けの非同期削除でバッチサイズを設定可能になった

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を参照してください。

関連記事

Rails 6.1: 関連付けをバックグラウンド削除する「dependent: :destroy_async」(翻訳)


CONTACT

TechRachoでは、パートナーシップをご検討いただける方からの
ご連絡をお待ちしております。ぜひお気軽にご意見・ご相談ください。