module Gitlab
  module Database
    module MultiThreadedMigration
      MULTI_THREAD_AR_CONNECTION = :thread_local_ar_connection

      # This overwrites the default connection method so that every thread can
      # use a thread-local connection, while still supporting all of Rails'
      # migration methods.
      def connection
        Thread.current[MULTI_THREAD_AR_CONNECTION] ||
          ActiveRecord::Base.connection
      end

      # Starts a thread-pool for N threads, along with N threads each using a
      # single connection. The provided block is yielded from inside each
      # thread.
      #
      # Example:
      #
      #     with_multiple_threads(4) do
      #       execute('SELECT ...')
      #     end
      #
      # thread_count - The number of threads to start.
      #
      # join - When set to true this method will join the threads, blocking the
      #        caller until all threads have finished running.
      #
      # Returns an Array containing the started threads.
      def with_multiple_threads(thread_count, join: true)
        pool = Gitlab::Database.create_connection_pool(thread_count)

        threads = Array.new(thread_count) do
          Thread.new do
            pool.with_connection do |connection|
              begin
                Thread.current[MULTI_THREAD_AR_CONNECTION] = connection
                yield
              ensure
                Thread.current[MULTI_THREAD_AR_CONNECTION] = nil
              end
            end
          end
        end

        threads.each(&:join) if join

        threads
      end
    end
  end
end
