72 lines
2.4 KiB
Ruby
72 lines
2.4 KiB
Ruby
|
# frozen_string_literal: true
|
||
|
|
||
|
class DedupRunnerProjects < Gitlab::Database::Migration[1.0]
|
||
|
TABLE_NAME = :ci_runner_projects
|
||
|
TMP_INDEX_NAME = 'tmp_unique_ci_runner_projects_by_runner_id_and_project_id'
|
||
|
OLD_INDEX_NAME = 'index_ci_runner_projects_on_runner_id_and_project_id'
|
||
|
INDEX_NAME = 'index_unique_ci_runner_projects_on_runner_id_and_project_id'
|
||
|
BATCH_SIZE = 5000
|
||
|
|
||
|
disable_ddl_transaction!
|
||
|
|
||
|
module Ci
|
||
|
class RunnerProject < ActiveRecord::Base
|
||
|
include EachBatch
|
||
|
|
||
|
self.table_name = 'ci_runner_projects'
|
||
|
end
|
||
|
end
|
||
|
|
||
|
def up
|
||
|
last_runner_project_record_id = Ci::RunnerProject.maximum(:id) || 0
|
||
|
|
||
|
# This index will disallow further duplicates while we're deduplicating the data.
|
||
|
add_concurrent_index(TABLE_NAME, [:runner_id, :project_id], where: "id > #{Integer(last_runner_project_record_id)}", unique: true, name: TMP_INDEX_NAME)
|
||
|
|
||
|
Ci::RunnerProject.each_batch(of: BATCH_SIZE) do |relation|
|
||
|
duplicated_runner_projects = Ci::RunnerProject
|
||
|
.select('COUNT(*)', :runner_id, :project_id)
|
||
|
.where('(runner_id, project_id) IN (?)', relation.select(:runner_id, :project_id))
|
||
|
.group(:runner_id, :project_id)
|
||
|
.having('COUNT(*) > 1')
|
||
|
|
||
|
duplicated_runner_projects.each do |runner_project|
|
||
|
deduplicate_item(runner_project)
|
||
|
end
|
||
|
end
|
||
|
|
||
|
add_concurrent_index(TABLE_NAME, [:runner_id, :project_id], unique: true, name: INDEX_NAME)
|
||
|
remove_concurrent_index_by_name(TABLE_NAME, TMP_INDEX_NAME)
|
||
|
remove_concurrent_index_by_name(TABLE_NAME, OLD_INDEX_NAME)
|
||
|
end
|
||
|
|
||
|
def down
|
||
|
add_concurrent_index(TABLE_NAME, [:runner_id, :project_id], name: OLD_INDEX_NAME)
|
||
|
remove_concurrent_index_by_name(TABLE_NAME, TMP_INDEX_NAME)
|
||
|
remove_concurrent_index_by_name(TABLE_NAME, INDEX_NAME)
|
||
|
end
|
||
|
|
||
|
private
|
||
|
|
||
|
def deduplicate_item(runner_project)
|
||
|
runner_projects_records = Ci::RunnerProject
|
||
|
.where(project_id: runner_project.project_id, runner_id: runner_project.runner_id)
|
||
|
.order(updated_at: :asc)
|
||
|
.to_a
|
||
|
|
||
|
attributes = {}
|
||
|
runner_projects_records.each do |runner_projects_record|
|
||
|
params = runner_projects_record.attributes.except('id')
|
||
|
attributes.merge!(params.compact)
|
||
|
end
|
||
|
|
||
|
ApplicationRecord.transaction do
|
||
|
record_to_keep = runner_projects_records.pop
|
||
|
records_to_delete = runner_projects_records
|
||
|
|
||
|
Ci::RunnerProject.where(id: records_to_delete.map(&:id)).delete_all
|
||
|
record_to_keep.update!(attributes)
|
||
|
end
|
||
|
end
|
||
|
end
|