background_migration_worker.rb 1.7 KB
Newer Older
1 2
# frozen_string_literal: true

3
class BackgroundMigrationWorker
4
  include ApplicationWorker
5

6 7 8 9 10 11 12 13
  # The minimum amount of time between processing two jobs of the same migration
  # class.
  #
  # This interval is set to 5 minutes so autovacuuming and other maintenance
  # related tasks have plenty of time to clean up after a migration has been
  # performed.
  MIN_INTERVAL = 5.minutes.to_i

14 15 16
  # Performs the background migration.
  #
  # See Gitlab::BackgroundMigration.perform for more information.
17 18 19
  #
  # class_name - The class name of the background migration to run.
  # arguments - The arguments to pass to the migration class.
20
  def perform(class_name, arguments = [])
21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53
    should_perform, ttl = perform_and_ttl(class_name)

    if should_perform
      Gitlab::BackgroundMigration.perform(class_name, arguments)
    else
      # If the lease could not be obtained this means either another process is
      # running a migration of this class or we ran one recently. In this case
      # we'll reschedule the job in such a way that it is picked up again around
      # the time the lease expires.
      self.class.perform_in(ttl || MIN_INTERVAL, class_name, arguments)
    end
  end

  def perform_and_ttl(class_name)
    if always_perform?
      # In test environments `perform_in` will run right away. This can then
      # lead to stack level errors in the above `#perform`. To work around this
      # we'll just perform the migration right away in the test environment.
      [true, nil]
    else
      lease = lease_for(class_name)

      [lease.try_obtain, lease.ttl]
    end
  end

  def lease_for(class_name)
    Gitlab::ExclusiveLease
      .new("#{self.class.name}:#{class_name}", timeout: MIN_INTERVAL)
  end

  def always_perform?
    Rails.env.test?
54 55
  end
end