Allow scheduling from after_commit hooks

This commit is contained in:
Douwe Maan 2017-06-01 16:35:32 -05:00
parent 4edb505bb6
commit a9dbda8605
1 changed files with 27 additions and 9 deletions

View File

@ -1,22 +1,24 @@
module Sidekiq
module Worker
mattr_accessor :inside_after_commit
self.inside_after_commit = false
module ClassMethods
module NoSchedulingFromTransactions
NESTING = ::Rails.env.test? ? 1 : 0
%i(perform_async perform_at perform_in).each do |name|
define_method(name) do |*args|
if ActiveRecord::Base.connection.open_transactions > NESTING
raise <<-MSG.strip_heredoc
`#{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.
return super(*args) if Sidekiq::Worker.inside_after_commit
return super(*args) unless ActiveRecord::Base.connection.open_transactions > NESTING
Schedule the worker from inside a `run_after_commit` block instead.
MSG
end
raise <<-MSG.strip_heredoc
`#{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.
super(*args)
Use an `after_commit` hook, or include `AfterCommitQueue` and use a `run_after_commit` block instead.
MSG
end
end
end
@ -25,3 +27,19 @@ module Sidekiq
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