如何在运行时禁用Rails关联计数器缓存

6

我正在批量导入记录,不想每次更新计数器。我希望在批量upsert期间跳过counter_cache sql更新,然后在循环结束时调用reset_counters

我已经尝试过:

my_model = MyModel.find_or_initialize_by_slug row[:slug]
my_model.association(:my_association).reflection.options[:counter_cache] = false
my_model.attributes = {:name => "Christopher"}
my_model.save!

但是我可以看到在sql输出中,它仍然在更新计数器缓存。

注意:我不能使用activerecord-import,因为我想执行upserts,并且我正在使用postgresql。


你解决了吗?同样的问题... - Andrea Salicetti
1个回答

5
您有几种不同的选项可以跳过计数器缓存的更新,您选择的选项取决于您想如何构建应用程序。我将讨论您可以避开计数器缓存的不同方法,并提到您可能需要考虑的一些因素。
基本上,您可以有三种不同的方法来跳过计数器缓存的更新:
  1. 猴子补丁(Monkey patch)模型以禁用计数器缓存回调
  2. 使用不触发回调的更新方法
  3. 定义指向相同表的替代模型,该模型没有相同的回调行为,并在批量插入到数据库时使用它
请注意,上面的前两个选项更普遍地与在ActiveRecord中禁用回调有关,这是有道理的,因为计数器缓存是通过回调内部实现的。
当Rails加载具有计数器缓存关联的模型时,它会动态定义回调方法。如果要将其禁用为回调,则必须首先确定回调名称。
有两种主要方法可以找出Rails定义的方法以实现这些回调。您可以阅读Rails源代码来查找它将通过字符串插值生成的名称,或者您可以使用内省来确定您的类响应哪些方法。我将举一个例子,说明如何使用内省查找ActiveRecord定义的回调,以便自动实现计数缓存。
假设您有一个名为SpecialReply的类,它从一个Reply类继承,该类又从ActiveRecord::Base继承(此示例来自Rails测试套件)。它具有如下所定义的计数缓存列:
class SpecialReply < ::Reply
  belongs_to :special_topic, :foreign_key => 'parent_id', :counter_cache => 'replies_count'
end

在控制台中,您可以通过使用.methods查看您的类响应的方法。这将产生很多噪音,因为每个Object实例已经响应了很多方法,因此您可以按以下方式缩小列表:

1.9.3-p194 :001 > sr = SpecialReply.new
1.9.3-p194 :002 > sr.methods - Object.methods

在第二行中,你说:“展示我实例化的SpecialReply对象可以响应的所有方法,减去所有对象都可以响应的方法。这通常有助于内省,通过过滤与所查看的类类型不特定的方法来减少噪声。”
不幸的是,即使经过这种筛选,由于ActiveRecord添加到其所有派生类的方法,仍然存在很多噪音。在这种情况下,使用grep非常有用 - 由于ActiveRecord方便地创建包含字符串counter_cache请参见ActiveRecord用于为belongs_to关联生成计数器缓存方法的元编程)的计数回调方法,因此您可以使用以下内容找出与计数器缓存相关的定义的回调:
1.9.3-p194 :001 > sr = SpecialReply.new
1.9.3-p194 :002 > sr.methods.map(&:to_s).grep(/counter_cache/)

请注意,由于grep操作的是字符串,而methods返回一个符号方法名称的数组,因此我们首先使用to_proc (&:)来将所有符号转换为字符串,然后再筛选出包含counter_cache的方法。这样就留下了以下似乎是由ActiveRecord自动生成的用于实现计数器缓存的回调方法:
belongs_to_counter_cache_after_create_for_special_topic
belongs_to_counter_cache_before_destroy_for_special_topic
belongs_to_counter_cache_after_create_for_topic
belongs_to_counter_cache_before_destroy_for_topic
belongs_to_counter_cache_after_create_for_topic_with_primary_key
belongs_to_counter_cache_before_destroy_for_topic_with_primary_key

你应该能够在程序中遵循类似的过程来确定ActiveRecord添加的方法名称,以便你可以按照现有指令删除回调将它们排除。
以上选项的选择取决于你的程序结构和为提高数据加载效率而考虑的权衡。值得注意的是,前两个选项可能通过从外部修改类的行为(猴子补丁)使你的代码变得不易读,并且可以通过绕过业务规则(更新缓存列)在数据更新时使你的系统不稳定。因此,我建议你考虑是否可以创建另一个类以优化加载数据的方式,同时最大限度地减少对数据的可读性或一致性的影响。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接