Rails 3 ActiveRecord .skip_callback线程安全

mca*_*mir 6 activerecord ruby-on-rails callback thread-safety ruby-on-rails-3

这段代码线程安全吗?

MyModel.skip_callback(:save, :before, :my_callback)
my_model_instance.update_attributes(attributes)
MyModel.set_callback(:save, :before, :my_callback)
Run Code Online (Sandbox Code Playgroud)

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

这是一个例子

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
Run Code Online (Sandbox Code Playgroud)

Jef*_*ndt 6

skip_callback并且set_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/