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
raise <<-MSG.strip_heredoc return super(*args) unless ActiveRecord::Base.connection.open_transactions > NESTING
`#{self}.#{name}` cannot be called inside a transaction as this can lead to race
conditions when the worker runs before the transaction is committed and tries to
access a model that has not been saved yet.
Schedule the worker from inside a `run_after_commit` block instead. raise <<-MSG.strip_heredoc
MSG `#{self}.#{name}` cannot be called inside a transaction as this can lead to
end race conditions when the worker runs before the transaction is committed and
tries to access a model that has not been saved yet.
super(*args) Use an `after_commit` hook, or include `AfterCommitQueue` and use a `run_after_commit` block instead.
MSG
end 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