Is this code thread safe?
MyModel.skip_callback(:save, :before, :my_callback)
my_model_instance.update_attributes(attributes)
MyModel.set_callback(:save, :before, :my_callback)
Can i safely use it to avoid retrigger the same callback recursively?
Here is an example
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
skip_callback
and set_callback
are NOT thread safe. I was able to confirm this while trying to create some records within sidekiq (a threaded async job processor). As soon as I re-enable the callbacks, there is a race condition that results in callbacks being called. If I comment the callback re-activation code, there are no issues.
I have found a number of possible solutions to the issue including two gems:
The sneaky-save gem seems to be the most straight-forward and intention-revealing option here. The gem essentially bypasses the ActiveRecord persistence methods and executes straight sql.
It is also the only one that I can confidently say is thread-safe. It is also a very small and understandable gem. The downside is that it does not call validations. Thus you would need to call validations yourself.
Anand A. Bait put together a great rundown on a number options. I am skeptical that all five options are threadsafe. The two gems mentioned above are listed along with other possible options in Anand's post here: http://www.allerin.com/blog/save-an-object-skipping-callbacks-in-rails-3-application/
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With