# frozen_string_literal: true

module Gitlab
  module Database
    module Migrations
      module LockRetryMixin
        module ActiveRecordMigrationProxyLockRetries
          def migration_class
            migration.class
          end

          def migration_connection
            migration.connection
          end

          def with_lock_retries_used!
            # regular AR migrations don't have this,
            # only ones inheriting from Gitlab::Database::Migration have
            return unless migration.respond_to?(:with_lock_retries_used!)

            migration.with_lock_retries_used!
          end

          def with_lock_retries_used?
            # regular AR migrations don't have this,
            # only ones inheriting from Gitlab::Database::Migration have
            return false unless migration.respond_to?(:with_lock_retries_used?)

            migration.with_lock_retries_used?
          end
        end

        module ActiveRecordMigratorLockRetries
          # We patch the original method to start a transaction
          # using the WithLockRetries methodology for the whole migration.
          def ddl_transaction(migration, &block)
            if use_transaction?(migration)
              migration.with_lock_retries_used!

              Gitlab::Database::WithLockRetries.new(
                connection: migration.migration_connection,
                klass: migration.migration_class,
                logger: Gitlab::BackgroundMigration::Logger
              ).run(raise_on_exhaustion: true, &block)
            else
              super
            end
          end
        end

        def self.patch!
          ActiveRecord::MigrationProxy.prepend(ActiveRecordMigrationProxyLockRetries)
          ActiveRecord::Migrator.prepend(ActiveRecordMigratorLockRetries)
        end
      end
    end
  end
end
