【问题标题】:Seeking advice on optimizing ActiveRecord::destroy_all寻求关于优化 ActiveRecord::destroy_all 的建议
【发布时间】:2020-03-26 23:54:49
【问题描述】:

Rails 5+

我知道 destroy_all 实例化每个模型并在其上运行 destroy 并且 delete_all 更快,但删除不尊重:

  • before_destroyaround_destroyafter_destroy 回调
  • dependent 关系设置

假设这个列表很全面,我们不应该通过检查模型的这些属性来节省destroy_all 的时间吗?如果没有回调,只需根据需要处理关系吗?

编辑:我正在寻找一种方法来修改默认的destroy_all 行为,以便它更智能并且不会盲目地实例化所有对象并链接到依赖关系的调用。如果我们有 A 与依赖 B (1:1) 的关系,并且 A 很大(1 百万),那么需要实例化和销毁很多对象。是的,特定于应用程序/领域的知识意味着您可以调用delete_all,但如果有人更改模型并添加关系,那么delete_all 就会变得非常危险。如果我们优化destroy_all 做一些思考,我们可以将一个简单的dependent: delete 关系减少为来自关系A 上的单个destroy_all 的两个delete_all 调用(A 和B),其中原始destroy_all 将是2百万对象实例化和数据库命中。

# Pseudocode
# Let the model in question be `User`

ids = self.pluck(:id)
if model.has_destroy_callbacks  # I imagine there's some fancy introspection stuff I can use 
  original_destroy_all
  return
else
  # Check Restrict type
  model.restrict_relationships.each do |rel|
    other_models = some_cute_query
    raise_exception_or_add_error if other_models.any?
  end

  # Add some check here to make sure we didn't miss any unknown dependency type

  # Normal relationships
  model.non_restrict_relationships.each do |rel|
    dep_type = rel.dependent_type
    if dep_type == :destroy
      rel.where(model_id: ids).destroy_all
    elsif dep_type == :delete
      rel.where(model_id: ids).delete_all
    elsif dep_type == :nullify
      rel.where(model_id: ids).update_all(model_name_id: nil)
    end
  end
end
self.delete_all # i.e. the collection that was gonna get destroyed

我正在寻找的是健全性检查,如果我遗漏了一些明显的东西,为什么这不起作用。我也在寻找有关如何将其放入 ActiveRecord 的建议。另外,您能否针对特定模型的集合/关系专门覆盖 destroy_all

【问题讨论】:

    标签: ruby-on-rails activerecord ruby-on-rails-5 rails-activerecord


    【解决方案1】:

    回调链可通过_*_callbacks 方法访问 目的。活动模型回调支持:before:after:around 作为 kind 属性的值。 kind 属性定义 回调运行在链的哪个部分。

    在 before_save 回调链中查找所有回调:

    Topic._save_callbacks.select { |cb| cb.kind.eql?(:before) }
    

    所以在这种情况下它是_destroy_callbacks,但如果您的目标是健全性检查,我会让该方法引发异常并保释而不是调用destroy_all

    raise SomeKindOfError if model._destroy_callbacks.any? 
    

    这在调试和使用方面更有帮助,而不仅仅是隐藏问题。

    获取模型的所有关联可以通过.reflect_on_all_associations 完成,它为您提供 AssocationReflection 对象。从那里你可以得到关联的选项。

    但是……

    这充满了“聪明”的代码。当你到达使用销毁回调是一个性能问题的地步时,会有更大的问题,然后只是在delete_alldestroy_all 之间进行选择,并且自动选择并不能真正解决问题。

    【讨论】:

    • 也许我的问题不清楚。我正在尝试用这个替换destroy_all,这样它是递归的。现在,如果你在一个有很多记录的集合上调用destroy_all,每个对象都会被实例化并且必须销毁关联的对象。假设集合 A 和 B 有一个从属的: :destroy 操作,并且 A 和 B 有 100 万条记录,那是很多 DB 命中,对吧?如果我们检测到我们可以 delete_all 两次,那就是两次 DB 调用 vs 2mil obj 实例化和要删除的查询。
    猜你喜欢
    • 2020-08-30
    • 2011-11-15
    • 1970-01-01
    • 1970-01-01
    • 2019-09-27
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多