summaryrefslogtreecommitdiff
path: root/lib/gitlab/background_migration/remove_duplicate_services.rb
blob: 59fb9143a7217c243c9f47772b78a08b6ecf2754 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
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
54
55
56
57
58
# frozen_string_literal: true

module Gitlab
  module BackgroundMigration
    # Remove duplicated service records with the same project and type.
    # These were created in the past for unknown reasons, and should be blocked
    # now by the uniqueness validation in the Service model.
    class RemoveDuplicateServices
      # See app/models/service
      class Service < ActiveRecord::Base
        include EachBatch

        self.table_name = 'services'
        self.inheritance_column = :_type_disabled

        scope :project_ids_with_duplicates, -> do
          select(:project_id)
            .distinct
            .where.not(project_id: nil)
            .group(:project_id, :type)
            .having('count(*) > 1')
        end

        scope :types_with_duplicates, -> (project_ids) do
          select(:project_id, :type)
            .where(project_id: project_ids)
            .group(:project_id, :type)
            .having('count(*) > 1')
        end
      end

      def perform(*project_ids)
        types_with_duplicates = Service.types_with_duplicates(project_ids).pluck(:project_id, :type)

        types_with_duplicates.each do |project_id, type|
          remove_duplicates(project_id, type)
        end
      end

      private

      def remove_duplicates(project_id, type)
        scope = Service.where(project_id: project_id, type: type)

        # Build a subquery to determine which service record is actually in use,
        # by querying for it without specifying an order.
        #
        # This should match the record returned by `Project#find_service`,
        # and the `has_one` service associations on `Project`.
        correct_service = scope.select(:id).limit(1)

        # Delete all other services with the same `project_id` and `type`
        duplicate_services = scope.where.not(id: correct_service)
        duplicate_services.delete_all
      end
    end
  end
end