diff options
Diffstat (limited to 'lib/gitlab/background_migration/remove_duplicate_services.rb')
-rw-r--r-- | lib/gitlab/background_migration/remove_duplicate_services.rb | 58 |
1 files changed, 58 insertions, 0 deletions
diff --git a/lib/gitlab/background_migration/remove_duplicate_services.rb b/lib/gitlab/background_migration/remove_duplicate_services.rb new file mode 100644 index 00000000000..59fb9143a72 --- /dev/null +++ b/lib/gitlab/background_migration/remove_duplicate_services.rb @@ -0,0 +1,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 |