debian-mirror-gitlab/app/workers/concerns/application_worker.rb

247 lines
7.4 KiB
Ruby
Raw Normal View History

2018-11-08 19:23:39 +05:30
# frozen_string_literal: true
2019-09-30 21:07:59 +05:30
require 'sidekiq/api'
2018-03-17 18:26:18 +05:30
Sidekiq::Worker.extend ActiveSupport::Concern
module ApplicationWorker
extend ActiveSupport::Concern
include Sidekiq::Worker # rubocop:disable Cop/IncludeSidekiqWorker
2019-12-21 20:55:43 +05:30
include WorkerAttributes
2020-03-13 15:44:24 +05:30
include WorkerContext
2020-10-24 23:57:45 +05:30
include Gitlab::SidekiqVersioning::Worker
2018-03-17 18:26:18 +05:30
2020-05-24 23:13:21 +05:30
LOGGING_EXTRA_KEY = 'extra'
2021-09-04 01:27:46 +05:30
DEFAULT_DELAY_INTERVAL = 1
2021-12-11 22:18:48 +05:30
SAFE_PUSH_BULK_LIMIT = 1000
2020-05-24 23:13:21 +05:30
2018-03-17 18:26:18 +05:30
included do
set_queue
2021-06-08 01:23:25 +05:30
after_set_class_attribute { set_queue }
2020-04-08 14:13:33 +05:30
def structured_payload(payload = {})
2021-04-29 21:17:54 +05:30
context = Gitlab::ApplicationContext.current.merge(
2021-01-29 00:20:46 +05:30
'class' => self.class.name,
2020-04-08 14:13:33 +05:30
'job_status' => 'running',
'queue' => self.class.queue,
'jid' => jid
)
payload.stringify_keys.merge(context)
end
2020-05-24 23:13:21 +05:30
def log_extra_metadata_on_done(key, value)
@done_log_extra_metadata ||= {}
@done_log_extra_metadata[key] = value
end
def logging_extras
return {} unless @done_log_extra_metadata
# Prefix keys with class name to avoid conflicts in Elasticsearch types.
# Also prefix with "extra." so that we know to log these new fields.
@done_log_extra_metadata.transform_keys do |k|
"#{LOGGING_EXTRA_KEY}.#{self.class.name.gsub("::", "_").underscore}.#{k}"
end
end
2018-03-17 18:26:18 +05:30
end
2018-11-20 20:47:30 +05:30
class_methods do
2021-09-30 23:02:18 +05:30
extend ::Gitlab::Utils::Override
2018-03-17 18:26:18 +05:30
def inherited(subclass)
subclass.set_queue
2021-06-08 01:23:25 +05:30
subclass.after_set_class_attribute { subclass.set_queue }
2018-03-17 18:26:18 +05:30
end
2021-12-11 22:18:48 +05:30
def with_status
status_from_class = self.sidekiq_options_hash['status_expiration']
set(status_expiration: status_from_class || Gitlab::SidekiqStatus::DEFAULT_EXPIRATION)
end
2021-10-27 15:23:28 +05:30
def generated_queue_name
Gitlab::SidekiqConfig::WorkerRouter.queue_name_from_worker_name(self)
end
2021-09-30 23:02:18 +05:30
def validate_worker_attributes!
# Since the delayed data_consistency will use sidekiq built in retry mechanism, it is required that this mechanism
# is not disabled.
if retry_disabled? && get_data_consistency == :delayed
raise ArgumentError, "Retry support cannot be disabled if data_consistency is set to :delayed"
end
end
# Checks if sidekiq retry support is disabled
def retry_disabled?
get_sidekiq_options['retry'] == 0 || get_sidekiq_options['retry'] == false
end
override :sidekiq_options
def sidekiq_options(opts = {})
super.tap do
validate_worker_attributes!
end
end
2021-11-11 11:23:49 +05:30
override :data_consistency
def data_consistency(data_consistency, feature_flag: nil)
super
validate_worker_attributes!
end
2021-09-04 01:27:46 +05:30
def perform_async(*args)
2022-01-26 12:08:38 +05:30
return super if Gitlab::Database::LoadBalancing.primary_only?
2021-09-04 01:27:46 +05:30
# Worker execution for workers with data_consistency set to :delayed or :sticky
# will be delayed to give replication enough time to complete
2022-01-26 12:08:38 +05:30
if utilizes_load_balancing_capabilities? && Feature.disabled?(:skip_scheduling_workers_for_replicas, default_enabled: :yaml)
2021-09-04 01:27:46 +05:30
perform_in(delay_interval, *args)
else
super
end
end
2018-03-17 18:26:18 +05:30
def set_queue
2021-06-08 01:23:25 +05:30
queue_name = ::Gitlab::SidekiqConfig::WorkerRouter.global.route(self)
2018-03-17 18:26:18 +05:30
sidekiq_options queue: queue_name # rubocop:disable Cop/SidekiqOptionsQueue
end
def queue_namespace(new_namespace = nil)
if new_namespace
sidekiq_options queue_namespace: new_namespace
set_queue
else
get_sidekiq_options['queue_namespace']&.to_s
end
end
def queue
get_sidekiq_options['queue'].to_s
end
2020-06-23 00:09:42 +05:30
# Set/get which arguments can be logged and sent to Sentry.
#
# Numeric arguments are logged by default, so there is no need to
# list those.
#
# Non-numeric arguments must be listed by position, as Sidekiq
# cannot see argument names.
#
def loggable_arguments(*args)
if args.any?
@loggable_arguments = args
else
@loggable_arguments || []
end
end
2021-12-11 22:18:48 +05:30
def log_bulk_perform_async?
@log_bulk_perform_async
end
def log_bulk_perform_async!
@log_bulk_perform_async = true
end
2019-09-30 21:07:59 +05:30
def queue_size
Sidekiq::Queue.new(queue).size
end
2018-03-17 18:26:18 +05:30
def bulk_perform_async(args_list)
2021-12-11 22:18:48 +05:30
if log_bulk_perform_async?
Sidekiq.logger.info('class' => self.name, 'args_list' => args_list, 'args_list_count' => args_list.length, 'message' => 'Inserting multiple jobs')
end
do_push_bulk(args_list).tap do |job_ids|
if log_bulk_perform_async?
Sidekiq.logger.info('class' => self.name, 'jid_list' => job_ids, 'jid_list_count' => job_ids.length, 'message' => 'Completed JID insertion')
end
end
2018-03-17 18:26:18 +05:30
end
2020-06-23 00:09:42 +05:30
def bulk_perform_in(delay, args_list, batch_size: nil, batch_delay: nil)
2018-03-17 18:26:18 +05:30
now = Time.now.to_i
2021-12-11 22:18:48 +05:30
base_schedule_at = now + delay.to_i
2018-03-17 18:26:18 +05:30
2021-12-11 22:18:48 +05:30
if base_schedule_at <= now
raise ArgumentError, 'The schedule time must be in the future!'
2018-03-17 18:26:18 +05:30
end
2021-12-11 22:18:48 +05:30
schedule_at = base_schedule_at
2020-06-23 00:09:42 +05:30
if batch_size && batch_delay
2021-12-11 22:18:48 +05:30
batch_size = batch_size.to_i
batch_delay = batch_delay.to_i
raise ArgumentError, 'batch_size should be greater than 0' unless batch_size > 0
raise ArgumentError, 'batch_delay should be greater than 0' unless batch_delay > 0
# build an array of schedules corresponding to each item in `args_list`
bulk_schedule_at = Array.new(args_list.size) do |index|
batch_number = index / batch_size
base_schedule_at + (batch_number * batch_delay)
end
schedule_at = bulk_schedule_at
end
if Feature.enabled?(:sidekiq_push_bulk_in_batches)
in_safe_limit_batches(args_list, schedule_at) do |args_batch, schedule_at_for_batch|
Sidekiq::Client.push_bulk('class' => self, 'args' => args_batch, 'at' => schedule_at_for_batch)
2020-06-23 00:09:42 +05:30
end
else
2021-12-11 22:18:48 +05:30
Sidekiq::Client.push_bulk('class' => self, 'args' => args_list, 'at' => schedule_at)
2020-06-23 00:09:42 +05:30
end
2018-03-17 18:26:18 +05:30
end
2021-09-04 01:27:46 +05:30
protected
def delay_interval
DEFAULT_DELAY_INTERVAL.seconds
end
2021-12-11 22:18:48 +05:30
private
def do_push_bulk(args_list)
if Feature.enabled?(:sidekiq_push_bulk_in_batches)
in_safe_limit_batches(args_list) do |args_batch, _|
Sidekiq::Client.push_bulk('class' => self, 'args' => args_batch)
end
else
Sidekiq::Client.push_bulk('class' => self, 'args' => args_list)
end
end
def in_safe_limit_batches(args_list, schedule_at = nil, safe_limit = SAFE_PUSH_BULK_LIMIT)
# `schedule_at` could be one of
# - nil.
# - a single Numeric that represents time, like `30.minutes.from_now.to_i`.
# - an array, where each element is a Numeric that reprsents time.
# - Each element in this array would correspond to the time at which
# - the job in `args_list` at the corresponding index needs to be scheduled.
# In the case where `schedule_at` is an array of Numeric, it needs to be sliced
# in the same manner as the `args_list`, with each slice containing `safe_limit`
# number of elements.
schedule_at = schedule_at.each_slice(safe_limit).to_a if schedule_at.is_a?(Array)
args_list.each_slice(safe_limit).with_index.flat_map do |args_batch, index|
schedule_at_for_batch = process_schedule_at_for_batch(schedule_at, index)
yield(args_batch, schedule_at_for_batch)
end
end
def process_schedule_at_for_batch(schedule_at, index)
return unless schedule_at
return schedule_at[index] if schedule_at.is_a?(Array) && schedule_at.all?(Array)
schedule_at
end
2018-03-17 18:26:18 +05:30
end
end