Sidekiq::Worker#skipping_transaction_check is not thread-safe
Summary
Any code using Sidekiq::Worker#skipping_transaction_check
in Puma or Sidekiq is vulnerable to races. This could lead to the check being skipped in cases when it shouln't be.
module Sidekiq
module Worker
EnqueueFromTransactionError = Class.new(StandardError)
mattr_accessor :skip_transaction_check
self.skip_transaction_check = false
def self.skipping_transaction_check(&block)
skip_transaction_check = self.skip_transaction_check
self.skip_transaction_check = true
yield
ensure
self.skip_transaction_check = skip_transaction_check
end
module ClassMethods
module NoEnqueueingFromTransactions
%i(perform_async perform_at perform_in).each do |name|
define_method(name) do |*args|
if !Sidekiq::Worker.skip_transaction_check && AfterCommitQueue.inside_transaction?
# ...
The mattr_accessor
is shared mutable state. If thread A sets it to true
by calling skipping_transaction_check
, thread B is affected in perform_async
(or so) even if it hasn't called skipping_transaction_check
.