数据库
Rails の `dependent: :destroy` が激重になる話を簡単な例で
Kaziu Dev.to (EN Zone)
25 浏览
何が起きるか
親レコードを消すと、子レコードも一緒に消えて便利な dependent: :destroy(いわゆる cascade 削除)。
でも子がコールバックを持っていると、削除のたびにそれが発火します。
class Author < ApplicationRecord
has_many :books, dependent: :destroy
end
class Book < ApplicationRecord
belongs_to :author, touch: true # 消すと親の更新日時を更新(いわゆる touch)
after_destroy :reindex_search # 1件消すたびに Elasticsearch を再インデックス(同期・重い)
end
author.destroy すると…
Author を1件destroy
└─ books が1000件あれば 1件ずつ destroy(cascade)
└─ そのたびに親を touch し、after_destroy で
Elasticsearch 更新が「直列」で1000回走る 😱
件数が少ないうちは気づかず、本番のデータ量で突然タイムアウトします。
対策:重い子だけ「コールバック無し」で先に消す
delete_all はコールバック(cascade も touch も)を発火させず、SQL 1発で消します。
ActiveRecord::Base.transaction do
Book.where(author_id: author.id).delete_all # ① 重い子を静かに一掃
author.destroy # ② もう子はいないので連鎖しない
end
# Elasticsearch の更新は捨てずに、あとで非同期・一括で1回だけ
ReindexJob.perform_later(author.id) # ③ トランザクションの外で
ポイント3つ
やること
理由
重い子を delete_all で先に消す
コールバック(cascade / touch)を発火させない(destroy_all は発火する)
本体はそのあと消す
連鎖する子が残っていないので重い処理が走らない
重い処理(ES再インデックス)は非同期ジョブで一括
「同期で1件ずつ」→「非同期でまとめて1回」に置き換え
用語メモ
cascade(カスケード) … 親を消すと子も連鎖して消える挙動。dependent: :destroy がこれ
touch … 中身は変えず「更新された印」として関連レコードの更新日時だけ更新すること
destroy = 1件ずつ読み込み、コールバックを呼ぶ(安全だが遅い)
delete = SQLで直接、コールバックを呼ばない(速いが自己責任)
阅读原文:https://dev.to/kaziusan/rails-no-dependent-destroy-gaben-fan-deji-zhong-ninaruhua-wojian-dan-nali-de-301a
← 上一篇
Casbin Gateway 开源:本地 AI 编程 Agent 管理网关,统一 29 个 Agent 的配置、用量与权限
下一篇 →
[分享创造] 做了一个免费的在线视力测试网站 EyeTest Online,想听听大家对体验的建议
相关文章
评论0
还没有评论,来抢沙发吧