2021-12-11 22:18:48 +05:30
|
|
|
# frozen_string_literal: true
|
|
|
|
|
|
|
|
module Gitlab
|
|
|
|
module BackgroundMigration
|
|
|
|
# Class responsible for executing background migrations based on the given database.
|
|
|
|
#
|
|
|
|
# Chooses the correct worker class when selecting jobs from the queue based on the
|
|
|
|
# convention of how the queues and worker classes are setup for each database.
|
|
|
|
#
|
|
|
|
# Also provides a database connection to the correct tracking database.
|
2022-01-26 12:08:38 +05:30
|
|
|
class JobCoordinator # rubocop:disable Metrics/ClassLength
|
|
|
|
class << self
|
2022-04-04 11:22:00 +05:30
|
|
|
def for_tracking_database(tracking_database)
|
|
|
|
worker_class = worker_for_tracking_database[tracking_database]
|
|
|
|
|
|
|
|
if worker_class.nil?
|
2022-07-16 23:28:13 +05:30
|
|
|
raise ArgumentError, "The '#{tracking_database}' must be one of #{worker_for_tracking_database.keys.to_a}"
|
2022-04-04 11:22:00 +05:30
|
|
|
end
|
|
|
|
|
|
|
|
new(worker_class)
|
|
|
|
end
|
|
|
|
|
|
|
|
private
|
|
|
|
|
2022-01-26 12:08:38 +05:30
|
|
|
def worker_classes
|
|
|
|
@worker_classes ||= [
|
2022-04-04 11:22:00 +05:30
|
|
|
::BackgroundMigrationWorker,
|
|
|
|
::BackgroundMigration::CiDatabaseWorker
|
2022-01-26 12:08:38 +05:30
|
|
|
].freeze
|
|
|
|
end
|
2021-12-11 22:18:48 +05:30
|
|
|
|
2022-01-26 12:08:38 +05:30
|
|
|
def worker_for_tracking_database
|
|
|
|
@worker_for_tracking_database ||= worker_classes
|
2022-04-04 11:22:00 +05:30
|
|
|
.select { |worker_class| Gitlab::Database.has_config?(worker_class.tracking_database) }
|
2022-01-26 12:08:38 +05:30
|
|
|
.index_by(&:tracking_database)
|
|
|
|
.with_indifferent_access
|
|
|
|
.freeze
|
2021-12-11 22:18:48 +05:30
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2022-01-26 12:08:38 +05:30
|
|
|
attr_reader :worker_class
|
2021-12-11 22:18:48 +05:30
|
|
|
|
2022-03-02 08:16:31 +05:30
|
|
|
delegate :minimum_interval, :perform_in, to: :worker_class
|
|
|
|
|
2021-12-11 22:18:48 +05:30
|
|
|
def queue
|
|
|
|
@queue ||= worker_class.sidekiq_options['queue']
|
|
|
|
end
|
|
|
|
|
|
|
|
def with_shared_connection(&block)
|
|
|
|
Gitlab::Database::SharedModel.using_connection(connection, &block)
|
|
|
|
end
|
|
|
|
|
2022-05-07 20:08:51 +05:30
|
|
|
def pending_jobs(include_dead_jobs: false)
|
|
|
|
Enumerator.new do |y|
|
2021-12-11 22:18:48 +05:30
|
|
|
queues = [
|
|
|
|
Sidekiq::ScheduledSet.new,
|
|
|
|
Sidekiq::Queue.new(self.queue)
|
|
|
|
]
|
|
|
|
|
2022-05-07 20:08:51 +05:30
|
|
|
if include_dead_jobs
|
2021-12-11 22:18:48 +05:30
|
|
|
queues << Sidekiq::RetrySet.new
|
|
|
|
queues << Sidekiq::DeadSet.new
|
|
|
|
end
|
|
|
|
|
|
|
|
queues.each do |queue|
|
|
|
|
queue.each do |job|
|
2022-05-07 20:08:51 +05:30
|
|
|
y << job if job.klass == worker_class.name
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
def steal(steal_class, retry_dead_jobs: false)
|
|
|
|
with_shared_connection do
|
|
|
|
pending_jobs(include_dead_jobs: retry_dead_jobs).each do |job|
|
|
|
|
migration_class, migration_args = job.args
|
2021-12-11 22:18:48 +05:30
|
|
|
|
2022-05-07 20:08:51 +05:30
|
|
|
next unless migration_class == steal_class
|
|
|
|
next if block_given? && !(yield job)
|
2021-12-11 22:18:48 +05:30
|
|
|
|
2022-05-07 20:08:51 +05:30
|
|
|
begin
|
|
|
|
perform(migration_class, migration_args) if job.delete
|
|
|
|
rescue Exception # rubocop:disable Lint/RescueException
|
|
|
|
worker_class # enqueue this migration again
|
|
|
|
.perform_async(migration_class, migration_args)
|
2021-12-11 22:18:48 +05:30
|
|
|
|
2022-05-07 20:08:51 +05:30
|
|
|
raise
|
2021-12-11 22:18:48 +05:30
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
def perform(class_name, arguments)
|
|
|
|
with_shared_connection do
|
2022-03-02 08:16:31 +05:30
|
|
|
migration_instance_for(class_name).perform(*arguments)
|
2021-12-11 22:18:48 +05:30
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
def remaining
|
|
|
|
enqueued = Sidekiq::Queue.new(self.queue)
|
|
|
|
scheduled = Sidekiq::ScheduledSet.new
|
|
|
|
|
|
|
|
[enqueued, scheduled].sum do |set|
|
|
|
|
set.count do |job|
|
|
|
|
job.klass == worker_class.name
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
def exists?(migration_class, additional_queues = [])
|
|
|
|
enqueued = Sidekiq::Queue.new(self.queue)
|
|
|
|
scheduled = Sidekiq::ScheduledSet.new
|
|
|
|
|
|
|
|
enqueued_job?([enqueued, scheduled], migration_class)
|
|
|
|
end
|
|
|
|
|
|
|
|
def dead_jobs?(migration_class)
|
|
|
|
dead_set = Sidekiq::DeadSet.new
|
|
|
|
|
|
|
|
enqueued_job?([dead_set], migration_class)
|
|
|
|
end
|
|
|
|
|
|
|
|
def retrying_jobs?(migration_class)
|
|
|
|
retry_set = Sidekiq::RetrySet.new
|
|
|
|
|
|
|
|
enqueued_job?([retry_set], migration_class)
|
|
|
|
end
|
|
|
|
|
2022-03-02 08:16:31 +05:30
|
|
|
def migration_instance_for(class_name)
|
|
|
|
migration_class = migration_class_for(class_name)
|
|
|
|
|
|
|
|
if migration_class < Gitlab::BackgroundMigration::BaseJob
|
|
|
|
migration_class.new(connection: connection)
|
|
|
|
else
|
|
|
|
migration_class.new
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2021-12-11 22:18:48 +05:30
|
|
|
def migration_class_for(class_name)
|
|
|
|
Gitlab::BackgroundMigration.const_get(class_name, false)
|
|
|
|
end
|
|
|
|
|
|
|
|
def enqueued_job?(queues, migration_class)
|
|
|
|
queues.any? do |queue|
|
|
|
|
queue.any? do |job|
|
|
|
|
job.klass == worker_class.name && job.args.first == migration_class
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
private
|
|
|
|
|
2022-01-26 12:08:38 +05:30
|
|
|
def initialize(worker_class)
|
2021-12-11 22:18:48 +05:30
|
|
|
@worker_class = worker_class
|
|
|
|
end
|
|
|
|
|
|
|
|
def connection
|
|
|
|
@connection ||= Gitlab::Database
|
|
|
|
.database_base_models
|
2022-04-04 11:22:00 +05:30
|
|
|
.fetch(worker_class.tracking_database)
|
2021-12-11 22:18:48 +05:30
|
|
|
.connection
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|