diff options
Diffstat (limited to 'app')
-rw-r--r-- | app/controllers/admin/services_controller.rb | 2 | ||||
-rw-r--r-- | app/services/projects/propagate_service_template.rb | 103 | ||||
-rw-r--r-- | app/workers/propagate_service_template_worker.rb | 21 |
3 files changed, 126 insertions, 0 deletions
diff --git a/app/controllers/admin/services_controller.rb b/app/controllers/admin/services_controller.rb index 37a1a23178e..4c3d336b3af 100644 --- a/app/controllers/admin/services_controller.rb +++ b/app/controllers/admin/services_controller.rb @@ -16,6 +16,8 @@ class Admin::ServicesController < Admin::ApplicationController def update if service.update_attributes(service_params[:service]) + PropagateServiceTemplateWorker.perform_async(service.id) if service.active? + redirect_to admin_application_settings_services_path, notice: 'Application settings saved successfully' else diff --git a/app/services/projects/propagate_service_template.rb b/app/services/projects/propagate_service_template.rb new file mode 100644 index 00000000000..a8ef2108492 --- /dev/null +++ b/app/services/projects/propagate_service_template.rb @@ -0,0 +1,103 @@ +module Projects + class PropagateServiceTemplate + BATCH_SIZE = 100 + + def self.propagate(*args) + new(*args).propagate + end + + def initialize(template) + @template = template + end + + def propagate + return unless @template.active? + + Rails.logger.info("Propagating services for template #{@template.id}") + + propagate_projects_with_template + end + + private + + def propagate_projects_with_template + loop do + batch = project_ids_batch + + bulk_create_from_template(batch) unless batch.empty? + + break if batch.size < BATCH_SIZE + end + end + + def bulk_create_from_template(batch) + service_list = batch.map do |project_id| + service_hash.values << project_id + end + + Project.transaction do + bulk_insert_services(service_hash.keys << 'project_id', service_list) + run_callbacks(batch) + end + end + + def project_ids_batch + Project.connection.select_values( + <<-SQL + SELECT id + FROM projects + WHERE NOT EXISTS ( + SELECT true + FROM services + WHERE services.project_id = projects.id + AND services.type = '#{@template.type}' + ) + AND projects.pending_delete = false + AND projects.archived = false + LIMIT #{BATCH_SIZE} + SQL + ) + end + + def bulk_insert_services(columns, values_array) + ActiveRecord::Base.connection.execute( + <<-SQL.strip_heredoc + INSERT INTO services (#{columns.join(', ')}) + VALUES #{values_array.map { |tuple| "(#{tuple.join(', ')})" }.join(', ')} + SQL + ) + end + + def service_hash + @service_hash ||= + begin + template_hash = @template.as_json(methods: :type).except('id', 'template', 'project_id') + + template_hash.each_with_object({}) do |(key, value), service_hash| + value = value.is_a?(Hash) ? value.to_json : value + + service_hash[ActiveRecord::Base.connection.quote_column_name(key)] = + ActiveRecord::Base.sanitize(value) + end + end + end + + def run_callbacks(batch) + if active_external_issue_tracker? + Project.where(id: batch).update_all(has_external_issue_tracker: true) + end + + if active_external_wiki? + Project.where(id: batch).update_all(has_external_wiki: true) + end + end + + def active_external_issue_tracker? + @template.issue_tracker? && !@template.default + end + + def active_external_wiki? + @template.type == 'ExternalWikiService' + end + end +end diff --git a/app/workers/propagate_service_template_worker.rb b/app/workers/propagate_service_template_worker.rb new file mode 100644 index 00000000000..5ce0e0405d0 --- /dev/null +++ b/app/workers/propagate_service_template_worker.rb @@ -0,0 +1,21 @@ +# Worker for updating any project specific caches. +class PropagateServiceTemplateWorker + include Sidekiq::Worker + include DedicatedSidekiqQueue + + LEASE_TIMEOUT = 4.hours.to_i + + def perform(template_id) + return unless try_obtain_lease_for(template_id) + + Projects::PropagateServiceTemplate.propagate(Service.find_by(id: template_id)) + end + + private + + def try_obtain_lease_for(template_id) + Gitlab::ExclusiveLease. + new("propagate_service_template_worker:#{template_id}", timeout: LEASE_TIMEOUT). + try_obtain + end +end |