debian-mirror-gitlab/config/initializers/forbid_sidekiq_in_transactions.rb

Ignoring revisions in .git-blame-ignore-revs. Click here to bypass and see the normal blame view.

113 lines
3.8 KiB
Ruby
Raw Normal View History

2021-03-11 19:13:27 +05:30
# frozen_string_literal: true
2017-09-10 17:25:29 +05:30
module Sidekiq
module Worker
2018-03-17 18:26:18 +05:30
EnqueueFromTransactionError = Class.new(StandardError)
2017-09-10 17:25:29 +05:30
def self.skipping_transaction_check(&block)
2019-09-30 21:07:59 +05:30
previous_skip_transaction_check = self.skip_transaction_check
Thread.current[:sidekiq_worker_skip_transaction_check] = true
2017-09-10 17:25:29 +05:30
yield
ensure
2019-09-30 21:07:59 +05:30
Thread.current[:sidekiq_worker_skip_transaction_check] = previous_skip_transaction_check
end
def self.skip_transaction_check
Thread.current[:sidekiq_worker_skip_transaction_check]
2017-09-10 17:25:29 +05:30
end
2022-07-23 23:45:48 +05:30
def self.inside_transaction?
::ApplicationRecord.inside_transaction? || ::Ci::ApplicationRecord.inside_transaction?
end
def self.raise_exception_for_being_inside_a_transaction?
!skip_transaction_check && inside_transaction?
end
def self.raise_inside_transaction_exception(cause:)
raise Sidekiq::Worker::EnqueueFromTransactionError, <<~MSG
#{cause} cannot be enqueued 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.
Use an `after_commit` hook, or include `AfterCommitQueue` and use a `run_after_commit` block instead.
MSG
end
2017-09-10 17:25:29 +05:30
module ClassMethods
2018-03-17 18:26:18 +05:30
module NoEnqueueingFromTransactions
2017-09-10 17:25:29 +05:30
%i(perform_async perform_at perform_in).each do |name|
define_method(name) do |*args|
2022-07-23 23:45:48 +05:30
if Sidekiq::Worker.raise_exception_for_being_inside_a_transaction?
2018-03-17 18:26:18 +05:30
begin
2022-07-23 23:45:48 +05:30
Sidekiq::Worker.raise_inside_transaction_exception(cause: "#{self}.#{name}")
2018-03-17 18:26:18 +05:30
rescue Sidekiq::Worker::EnqueueFromTransactionError => e
2020-11-24 15:15:51 +05:30
Gitlab::AppLogger.error(e.message) if ::Rails.env.production?
2020-01-01 13:55:28 +05:30
Gitlab::ErrorTracking.track_and_raise_for_dev_exception(e)
2018-03-17 18:26:18 +05:30
end
end
super(*args)
2017-09-10 17:25:29 +05:30
end
end
2022-07-23 23:45:48 +05:30
end
prepend NoEnqueueingFromTransactions
end
end
end
2022-01-26 12:08:38 +05:30
2022-07-23 23:45:48 +05:30
# We deliver emails using the `deliver_later` method and it uses ActiveJob
# under the hood, which later processes the email via the defined ActiveJob adapter's `enqueue` method.
# For GitLab, the ActiveJob adapter is Sidekiq (in development and production environments).
# We need to set the following up to override the ActiveJob adapater
# so as to ensure that no mailer jobs are enqueued from within a transaction.
module ActiveJob
module QueueAdapters
module NoEnqueueingFromTransactions
%i(enqueue enqueue_at).each do |name|
define_method(name) do |*args|
if Sidekiq::Worker.raise_exception_for_being_inside_a_transaction?
begin
job = args.first
Sidekiq::Worker.raise_inside_transaction_exception(
cause: "The #{job.class} job, enqueued into the queue: #{job.queue_name}"
)
rescue Sidekiq::Worker::EnqueueFromTransactionError => e
Gitlab::AppLogger.error(e.message) if ::Rails.env.production?
Gitlab::ErrorTracking.track_and_raise_for_dev_exception(e)
end
end
2022-01-26 12:08:38 +05:30
2022-07-23 23:45:48 +05:30
super(*args)
2022-01-26 12:08:38 +05:30
end
2017-09-10 17:25:29 +05:30
end
2022-07-23 23:45:48 +05:30
end
# This adapter is used in development & production environments.
class SidekiqAdapter
prepend NoEnqueueingFromTransactions
end
2017-09-10 17:25:29 +05:30
2022-07-23 23:45:48 +05:30
# This adapter is used in test environment.
# If we don't override the test environment adapter,
# we won't be seeing any failing jobs during the CI run,
# even if we enqueue mailers from within a transaction.
class TestAdapter
2018-03-17 18:26:18 +05:30
prepend NoEnqueueingFromTransactions
2017-09-10 17:25:29 +05:30
end
end
end
module ActiveRecord
class Base
module SkipTransactionCheckAfterCommit
2021-01-03 14:25:43 +05:30
def committed!(*args, **kwargs)
2017-09-10 17:25:29 +05:30
Sidekiq::Worker.skipping_transaction_check { super }
end
end
prepend SkipTransactionCheckAfterCommit
end
end