Commit a9dbda86 authored by Douwe Maan's avatar Douwe Maan

Allow scheduling from after_commit hooks

parent 4edb505b
module Sidekiq module Sidekiq
module Worker module Worker
mattr_accessor :inside_after_commit
self.inside_after_commit = false
module ClassMethods module ClassMethods
module NoSchedulingFromTransactions module NoSchedulingFromTransactions
NESTING = ::Rails.env.test? ? 1 : 0 NESTING = ::Rails.env.test? ? 1 : 0
%i(perform_async perform_at perform_in).each do |name| %i(perform_async perform_at perform_in).each do |name|
define_method(name) do |*args| define_method(name) do |*args|
if ActiveRecord::Base.connection.open_transactions > NESTING return super(*args) if Sidekiq::Worker.inside_after_commit
return super(*args) unless ActiveRecord::Base.connection.open_transactions > NESTING
raise <<-MSG.strip_heredoc raise <<-MSG.strip_heredoc
`#{self}.#{name}` cannot be called inside a transaction as this can lead to race `#{self}.#{name}` cannot be called inside a transaction as this can lead to
conditions when the worker runs before the transaction is committed and tries to race conditions when the worker runs before the transaction is committed and
access a model that has not been saved yet. tries to access a model that has not been saved yet.
Schedule the worker from inside a `run_after_commit` block instead. Use an `after_commit` hook, or include `AfterCommitQueue` and use a `run_after_commit` block instead.
MSG MSG
end end
super(*args)
end
end end
end end
...@@ -25,3 +27,19 @@ module Sidekiq ...@@ -25,3 +27,19 @@ module Sidekiq
end end
end end
end end
module ActiveRecord
class Base
module InsideAfterCommit
def committed!(*)
inside_after_commit = Sidekiq::Worker.inside_after_commit
Sidekiq::Worker.inside_after_commit = true
super
ensure
Sidekiq::Worker.inside_after_commit = inside_after_commit
end
end
prepend InsideAfterCommit
end
end
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment