【问题标题】:Rails 3 ActiveRecord .skip_callback thread safetyRails 3 ActiveRecord .skip_callback 线程安全
【发布时间】:2011-07-17 12:30:22
【问题描述】:

这段代码线程安全吗?

MyModel.skip_callback(:save, :before, :my_callback)
my_model_instance.update_attributes(attributes)
MyModel.set_callback(:save, :before, :my_callback)

我可以安全地使用它来避免递归地重新触发相同的回调吗?

这是一个例子

class Blog < ActiveRecord::Base

  after_save  :update_blog_theme, :if => :active_theme_id_changed?

  # ...

  private

  def update_blog_theme

    # Reuses a previously used BlogTheme or creates a new one
    blog_theme = BlogTheme.find_by_theme_id_and_blog_id(
                      self.active_theme_id, 
                      self.id)

    blog_theme ||= BlogTheme.create!( 
                     :theme_id => active_theme_id, 
                     :blog_id => self.id )

    Blog.skip_callback(:save, :after, :update_blog_theme)
    self.update_attributes!(:active_blog_theme_id => blog_theme.id) 
    Blog.set_callback(:save, :after, :update_blog_theme)

  end

end

【问题讨论】:

  • 看起来很老套,为什么不能使用 before_save 或 before_create?
  • 这里没有要求,我没有使用这段代码,也许我永远不会这样做,但我在互联网上找到了这个解决方案,并问自己在多线程中使用它是否安全
  • 用一个真实的例子编辑了问题正文

标签: ruby-on-rails ruby-on-rails-3 activerecord callback thread-safety


【解决方案1】:

skip_callbackset_callback 不是线程安全的。在尝试在 sidekiq(线程异步作业处理器)中创建一些记录时,我能够确认这一点。一旦我重新启用回调,就会出现导致回调被调用的竞争条件。如果我注释回调重新激活代码,则没有问题。

我找到了许多可能的解决方案,包括两个宝石:

  • “偷偷摸摸”的宝石
  • “skip_activerecord_callbacks”宝石

偷偷摸摸的保存宝石似乎是这里最直接和最能揭示意图的选项。 gem 本质上绕过了 ActiveRecord 持久化方法并直接执行 sql。

它也是唯一一个我可以自信地说是线程安全的。它也是一个非常小且易于理解的宝石。缺点是它不调用验证。因此,您需要自己调用验证。

Anand A. Bait 对数字选项做了一个很好的总结。我怀疑所有五个选项都是线程安全的。上面提到的两个宝石与其他可能的选项一起在 Anand 的帖子中列出:http://www.allerin.com/blog/save-an-object-skipping-callbacks-in-rails-3-application/

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2010-10-13
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-09-06
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多