gitlab-org--gitlab-foss/lib/gitlab/ci/tags/bulk_insert.rb

103 lines
2.7 KiB
Ruby

# frozen_string_literal: true
module Gitlab
module Ci
module Tags
class BulkInsert
include Gitlab::Utils::StrongMemoize
TAGGINGS_BATCH_SIZE = 1000
TAGS_BATCH_SIZE = 500
def initialize(statuses)
@statuses = statuses
end
def insert!
return false if tag_list_by_status.empty?
persist_build_tags!
end
private
attr_reader :statuses
def tag_list_by_status
strong_memoize(:tag_list_by_status) do
statuses.each.with_object({}) do |status, acc|
tag_list = status.tag_list
next unless tag_list
acc[status] = tag_list
end
end
end
def persist_build_tags!
all_tags = tag_list_by_status.values.flatten.uniq.reject(&:blank?)
tag_records_by_name = create_tags(all_tags).index_by(&:name)
taggings = build_taggings_attributes(tag_records_by_name)
return false if taggings.empty?
taggings.each_slice(TAGGINGS_BATCH_SIZE) do |taggings_slice|
ActsAsTaggableOn::Tagging.insert_all!(taggings)
end
true
end
# rubocop: disable CodeReuse/ActiveRecord
def create_tags(tags)
existing_tag_records = ActsAsTaggableOn::Tag.where(name: tags).to_a
missing_tags = detect_missing_tags(tags, existing_tag_records)
return existing_tag_records if missing_tags.empty?
missing_tags
.map { |tag| { name: tag } }
.each_slice(TAGS_BATCH_SIZE) do |tags_attributes|
ActsAsTaggableOn::Tag.insert_all!(tags_attributes)
end
ActsAsTaggableOn::Tag.where(name: tags).to_a
end
# rubocop: enable CodeReuse/ActiveRecord
def build_taggings_attributes(tag_records_by_name)
taggings = statuses.flat_map do |status|
tag_list = tag_list_by_status[status]
next unless tag_list
tags = tag_records_by_name.values_at(*tag_list)
taggings_for(tags, status)
end
taggings.compact!
taggings
end
def taggings_for(tags, status)
tags.map do |tag|
{
tag_id: tag.id,
taggable_type: CommitStatus.name,
taggable_id: status.id,
created_at: Time.current,
context: 'tags'
}
end
end
def detect_missing_tags(tags, tag_records)
if tags.size != tag_records.size
tags - tag_records.map(&:name)
else
[]
end
end
end
end
end
end