summaryrefslogtreecommitdiff
path: root/app/services/ci
diff options
context:
space:
mode:
Diffstat (limited to 'app/services/ci')
-rw-r--r--app/services/ci/after_requeue_job_service.rb2
-rw-r--r--app/services/ci/append_build_trace_service.rb3
-rw-r--r--app/services/ci/build_cancel_service.rb35
-rw-r--r--app/services/ci/build_unschedule_service.rb35
-rw-r--r--app/services/ci/create_downstream_pipeline_service.rb5
-rw-r--r--app/services/ci/create_pipeline_service.rb18
-rw-r--r--app/services/ci/daily_build_group_report_result_service.rb8
-rw-r--r--app/services/ci/delete_unit_tests_service.rb2
-rw-r--r--app/services/ci/destroy_pipeline_service.rb2
-rw-r--r--app/services/ci/drop_pipeline_service.rb13
-rw-r--r--app/services/ci/external_pull_requests/create_pipeline_service.rb11
-rw-r--r--app/services/ci/extract_sections_from_build_trace_service.rb34
-rw-r--r--app/services/ci/pipeline_trigger_service.rb8
-rw-r--r--app/services/ci/pipelines/add_job_service.rb30
-rw-r--r--app/services/ci/queue/builds_table_strategy.rb8
-rw-r--r--app/services/ci/queue/pending_builds_strategy.rb58
-rw-r--r--app/services/ci/resource_groups/assign_resource_from_resource_group_service.rb10
-rw-r--r--app/services/ci/stop_environments_service.rb50
-rw-r--r--app/services/ci/unlock_artifacts_service.rb2
19 files changed, 199 insertions, 135 deletions
diff --git a/app/services/ci/after_requeue_job_service.rb b/app/services/ci/after_requeue_job_service.rb
index b422e57baad..f717dd0862c 100644
--- a/app/services/ci/after_requeue_job_service.rb
+++ b/app/services/ci/after_requeue_job_service.rb
@@ -38,7 +38,7 @@ module Ci
end
def stage_dependent_jobs(processable)
- skipped_jobs(processable).scheduling_type_stage.after_stage(processable.stage_idx)
+ skipped_jobs(processable).after_stage(processable.stage_idx)
end
def needs_dependent_jobs(processable)
diff --git a/app/services/ci/append_build_trace_service.rb b/app/services/ci/append_build_trace_service.rb
index 8200f9790ee..0eef0ff0e61 100644
--- a/app/services/ci/append_build_trace_service.rb
+++ b/app/services/ci/append_build_trace_service.rb
@@ -71,8 +71,7 @@ module Ci
end
def trace_size_exceeded?(size)
- Feature.enabled?(:ci_jobs_trace_size_limit, project, default_enabled: :yaml) &&
- project.actual_limits.exceeded?(:ci_jobs_trace_size_limit, size / 1.megabyte)
+ project.actual_limits.exceeded?(:ci_jobs_trace_size_limit, size / 1.megabyte)
end
end
end
diff --git a/app/services/ci/build_cancel_service.rb b/app/services/ci/build_cancel_service.rb
new file mode 100644
index 00000000000..a23418ed738
--- /dev/null
+++ b/app/services/ci/build_cancel_service.rb
@@ -0,0 +1,35 @@
+# frozen_string_literal: true
+
+module Ci
+ class BuildCancelService
+ def initialize(build, user)
+ @build = build
+ @user = user
+ end
+
+ def execute
+ return forbidden unless allowed?
+ return unprocessable_entity unless build.cancelable?
+
+ build.cancel
+
+ ServiceResponse.success(payload: build)
+ end
+
+ private
+
+ attr_reader :build, :user
+
+ def allowed?
+ user.can?(:update_build, build)
+ end
+
+ def forbidden
+ ServiceResponse.error(message: 'Forbidden', http_status: :forbidden)
+ end
+
+ def unprocessable_entity
+ ServiceResponse.error(message: 'Unprocessable entity', http_status: :unprocessable_entity)
+ end
+ end
+end
diff --git a/app/services/ci/build_unschedule_service.rb b/app/services/ci/build_unschedule_service.rb
new file mode 100644
index 00000000000..3b367ecc4c4
--- /dev/null
+++ b/app/services/ci/build_unschedule_service.rb
@@ -0,0 +1,35 @@
+# frozen_string_literal: true
+
+module Ci
+ class BuildUnscheduleService
+ def initialize(build, user)
+ @build = build
+ @user = user
+ end
+
+ def execute
+ return forbidden unless allowed?
+ return unprocessable_entity unless build.scheduled?
+
+ build.unschedule!
+
+ ServiceResponse.success(payload: build)
+ end
+
+ private
+
+ attr_reader :build, :user
+
+ def allowed?
+ user.can?(:update_build, build)
+ end
+
+ def forbidden
+ ServiceResponse.error(message: 'Forbidden', http_status: :forbidden)
+ end
+
+ def unprocessable_entity
+ ServiceResponse.error(message: 'Unprocessable entity', http_status: :unprocessable_entity)
+ end
+ end
+end
diff --git a/app/services/ci/create_downstream_pipeline_service.rb b/app/services/ci/create_downstream_pipeline_service.rb
index e9ec2338171..a65c22e273c 100644
--- a/app/services/ci/create_downstream_pipeline_service.rb
+++ b/app/services/ci/create_downstream_pipeline_service.rb
@@ -33,8 +33,9 @@ module Ci
current_user,
pipeline_params.fetch(:target_revision))
- downstream_pipeline = service.execute(
- pipeline_params.fetch(:source), **pipeline_params[:execute_params])
+ downstream_pipeline = service
+ .execute(pipeline_params.fetch(:source), **pipeline_params[:execute_params])
+ .payload
downstream_pipeline.tap do |pipeline|
update_bridge_status!(@bridge, pipeline)
diff --git a/app/services/ci/create_pipeline_service.rb b/app/services/ci/create_pipeline_service.rb
index c039f31aafc..ba9665555cc 100644
--- a/app/services/ci/create_pipeline_service.rb
+++ b/app/services/ci/create_pipeline_service.rb
@@ -11,11 +11,11 @@ module Ci
Gitlab::Ci::Pipeline::Chain::Validate::Abilities,
Gitlab::Ci::Pipeline::Chain::Validate::Repository,
Gitlab::Ci::Pipeline::Chain::Validate::SecurityOrchestrationPolicy,
+ Gitlab::Ci::Pipeline::Chain::Skip,
Gitlab::Ci::Pipeline::Chain::Config::Content,
Gitlab::Ci::Pipeline::Chain::Config::Process,
Gitlab::Ci::Pipeline::Chain::Validate::AfterConfig,
Gitlab::Ci::Pipeline::Chain::RemoveUnwantedChatJobs,
- Gitlab::Ci::Pipeline::Chain::Skip,
Gitlab::Ci::Pipeline::Chain::SeedBlock,
Gitlab::Ci::Pipeline::Chain::EvaluateWorkflowRules,
Gitlab::Ci::Pipeline::Chain::Seed,
@@ -87,12 +87,16 @@ module Ci
if pipeline.persisted?
schedule_head_pipeline_update
create_namespace_onboarding_action
+ else
+ # If pipeline is not persisted, try to recover IID
+ pipeline.reset_project_iid
end
- # If pipeline is not persisted, try to recover IID
- pipeline.reset_project_iid unless pipeline.persisted?
-
- pipeline
+ if error_message = pipeline.full_error_messages.presence || pipeline.failure_reason.presence
+ ServiceResponse.error(message: error_message, payload: pipeline)
+ else
+ ServiceResponse.success(payload: pipeline)
+ end
end
# rubocop: enable Metrics/ParameterLists
@@ -100,8 +104,8 @@ module Ci
source = args[0]
params = Hash(args[1])
- execute(source, **params, &block).tap do |pipeline|
- unless pipeline.persisted?
+ execute(source, **params, &block).tap do |response|
+ unless response.payload.persisted?
raise CreateError, pipeline.full_error_messages
end
end
diff --git a/app/services/ci/daily_build_group_report_result_service.rb b/app/services/ci/daily_build_group_report_result_service.rb
index 820e6e08fc5..25c6d57d961 100644
--- a/app/services/ci/daily_build_group_report_result_service.rb
+++ b/app/services/ci/daily_build_group_report_result_service.rb
@@ -3,7 +3,13 @@
module Ci
class DailyBuildGroupReportResultService
def execute(pipeline)
- DailyBuildGroupReportResult.upsert_reports(coverage_reports(pipeline))
+ if DailyBuildGroupReportResult.upsert_reports(coverage_reports(pipeline))
+ Projects::CiFeatureUsage.insert_usage(
+ project_id: pipeline.project_id,
+ feature: :code_coverage,
+ default_branch: pipeline.default_branch?
+ )
+ end
end
private
diff --git a/app/services/ci/delete_unit_tests_service.rb b/app/services/ci/delete_unit_tests_service.rb
index 28f96351175..230661a107d 100644
--- a/app/services/ci/delete_unit_tests_service.rb
+++ b/app/services/ci/delete_unit_tests_service.rb
@@ -23,7 +23,7 @@ module Ci
def delete_batch!(klass)
deleted = 0
- ActiveRecord::Base.transaction do
+ klass.transaction do
ids = klass.deletable.lock('FOR UPDATE SKIP LOCKED').limit(BATCH_SIZE).pluck(:id)
break if ids.empty?
diff --git a/app/services/ci/destroy_pipeline_service.rb b/app/services/ci/destroy_pipeline_service.rb
index 494fcb23a06..dd5c8e0379f 100644
--- a/app/services/ci/destroy_pipeline_service.rb
+++ b/app/services/ci/destroy_pipeline_service.rb
@@ -7,7 +7,7 @@ module Ci
Ci::ExpirePipelineCacheService.new.execute(pipeline, delete: true)
- pipeline.cancel_running if pipeline.cancelable? && ::Feature.enabled?(:cancel_pipelines_prior_to_destroy, default_enabled: :yaml)
+ pipeline.cancel_running if pipeline.cancelable?
pipeline.reset.destroy!
diff --git a/app/services/ci/drop_pipeline_service.rb b/app/services/ci/drop_pipeline_service.rb
index f510943251b..16d3abcbfa0 100644
--- a/app/services/ci/drop_pipeline_service.rb
+++ b/app/services/ci/drop_pipeline_service.rb
@@ -2,6 +2,9 @@
module Ci
class DropPipelineService
+ PRELOADED_COMMIT_STATUS_RELATIONS = [:project, :pipeline].freeze
+ PRELOADED_CI_BUILD_RELATIONS = [:metadata, :deployment, :taggings].freeze
+
# execute service asynchronously for each cancelable pipeline
def execute_async_for_all(pipelines, failure_reason, context_user)
pipelines.cancelable.select(:id).find_in_batches do |pipelines_batch|
@@ -27,11 +30,11 @@ module Ci
private
- def preload_associations_for_drop(builds_batch)
- ActiveRecord::Associations::Preloader.new.preload( # rubocop: disable CodeReuse/ActiveRecord
- builds_batch,
- [:project, :pipeline, :metadata, :deployment, :taggings]
- )
+ # rubocop: disable CodeReuse/ActiveRecord
+ def preload_associations_for_drop(commit_status_batch)
+ ActiveRecord::Associations::Preloader.new.preload(commit_status_batch, PRELOADED_COMMIT_STATUS_RELATIONS)
+ ActiveRecord::Associations::Preloader.new.preload(commit_status_batch.select { |job| job.is_a?(Ci::Build) }, PRELOADED_CI_BUILD_RELATIONS)
end
+ # rubocop: enable CodeReuse/ActiveRecord
end
end
diff --git a/app/services/ci/external_pull_requests/create_pipeline_service.rb b/app/services/ci/external_pull_requests/create_pipeline_service.rb
index 78be94bfb41..83499524a8e 100644
--- a/app/services/ci/external_pull_requests/create_pipeline_service.rb
+++ b/app/services/ci/external_pull_requests/create_pipeline_service.rb
@@ -7,7 +7,8 @@ module Ci
module ExternalPullRequests
class CreatePipelineService < BaseService
def execute(pull_request)
- return unless pull_request.open? && pull_request.actual_branch_head?
+ return pull_request_not_open_error unless pull_request.open?
+ return pull_request_branch_error unless pull_request.actual_branch_head?
create_pipeline_for(pull_request)
end
@@ -26,6 +27,14 @@ module Ci
target_sha: pull_request.target_sha
}
end
+
+ def pull_request_not_open_error
+ ServiceResponse.error(message: 'The pull request is not opened', payload: nil)
+ end
+
+ def pull_request_branch_error
+ ServiceResponse.error(message: 'The source sha is not the head of the source branch', payload: nil)
+ end
end
end
end
diff --git a/app/services/ci/extract_sections_from_build_trace_service.rb b/app/services/ci/extract_sections_from_build_trace_service.rb
deleted file mode 100644
index c756e376901..00000000000
--- a/app/services/ci/extract_sections_from_build_trace_service.rb
+++ /dev/null
@@ -1,34 +0,0 @@
-# frozen_string_literal: true
-
-module Ci
- class ExtractSectionsFromBuildTraceService < BaseService
- def execute(build)
- return false unless build.trace_sections.empty?
-
- Gitlab::Database.bulk_insert(BuildTraceSection.table_name, extract_sections(build)) # rubocop:disable Gitlab/BulkInsert
- true
- end
-
- private
-
- # rubocop: disable CodeReuse/ActiveRecord
- def find_or_create_name(name)
- project.build_trace_section_names.find_or_create_by!(name: name)
- rescue ActiveRecord::RecordInvalid
- project.build_trace_section_names.find_by!(name: name)
- end
- # rubocop: enable CodeReuse/ActiveRecord
-
- def extract_sections(build)
- build.trace.extract_sections.map do |attr|
- name = attr.delete(:name)
- name_record = find_or_create_name(name)
-
- attr.merge(
- build_id: build.id,
- project_id: project.id,
- section_name_id: name_record.id)
- end
- end
- end
-end
diff --git a/app/services/ci/pipeline_trigger_service.rb b/app/services/ci/pipeline_trigger_service.rb
index 62c4d6b4599..7746382b845 100644
--- a/app/services/ci/pipeline_trigger_service.rb
+++ b/app/services/ci/pipeline_trigger_service.rb
@@ -27,13 +27,13 @@ module Ci
# this check is to not leak the presence of the project if user cannot read it
return unless trigger.project == project
- pipeline = Ci::CreatePipelineService
+ response = Ci::CreatePipelineService
.new(project, trigger.owner, ref: params[:ref], variables_attributes: variables)
.execute(:trigger, ignore_skip_ci: true) do |pipeline|
pipeline.trigger_requests.build(trigger: trigger)
end
- pipeline_service_response(pipeline)
+ pipeline_service_response(response.payload)
end
def pipeline_service_response(pipeline)
@@ -57,7 +57,7 @@ module Ci
# this check is to not leak the presence of the project if user cannot read it
return unless can?(job.user, :read_project, project)
- pipeline = Ci::CreatePipelineService
+ response = Ci::CreatePipelineService
.new(project, job.user, ref: params[:ref], variables_attributes: variables)
.execute(:pipeline, ignore_skip_ci: true) do |pipeline|
source = job.sourced_pipelines.build(
@@ -69,7 +69,7 @@ module Ci
pipeline.source_pipeline = source
end
- pipeline_service_response(pipeline)
+ pipeline_service_response(response.payload)
end
def job_from_token
diff --git a/app/services/ci/pipelines/add_job_service.rb b/app/services/ci/pipelines/add_job_service.rb
index 03bdb491200..41f9903e48c 100644
--- a/app/services/ci/pipelines/add_job_service.rb
+++ b/app/services/ci/pipelines/add_job_service.rb
@@ -3,21 +3,33 @@
module Ci
module Pipelines
class AddJobService
+ include ::Gitlab::ExclusiveLeaseHelpers
+
attr_reader :pipeline
def initialize(pipeline)
@pipeline = pipeline
- raise ArgumentError, "Pipeline must be persisted for this service to be used" unless @pipeline.persisted?
+ raise ArgumentError, "Pipeline must be persisted for this service to be used" unless pipeline.persisted?
end
def execute!(job, &block)
assign_pipeline_attributes(job)
- Ci::Pipeline.transaction do
- yield(job)
+ if Feature.enabled?(:ci_pipeline_add_job_with_lock, pipeline.project, default_enabled: :yaml)
+ in_lock("ci:pipelines:#{pipeline.id}:add-job", ttl: LOCK_TIMEOUT, sleep_sec: LOCK_SLEEP, retries: LOCK_RETRIES) do
+ Ci::Pipeline.transaction do
+ yield(job)
+
+ job.update_older_statuses_retried! if Feature.enabled?(:ci_fix_commit_status_retried, pipeline.project, default_enabled: :yaml)
+ end
+ end
+ else
+ Ci::Pipeline.transaction do
+ yield(job)
- job.update_older_statuses_retried! if Feature.enabled?(:ci_fix_commit_status_retried, @pipeline.project, default_enabled: :yaml)
+ job.update_older_statuses_retried! if Feature.enabled?(:ci_fix_commit_status_retried, pipeline.project, default_enabled: :yaml)
+ end
end
ServiceResponse.success(payload: { job: job })
@@ -27,10 +39,14 @@ module Ci
private
+ LOCK_TIMEOUT = 1.minute
+ LOCK_SLEEP = 0.5.seconds
+ LOCK_RETRIES = 20
+
def assign_pipeline_attributes(job)
- job.pipeline = @pipeline
- job.project = @pipeline.project
- job.ref = @pipeline.ref
+ job.pipeline = pipeline
+ job.project = pipeline.project
+ job.ref = pipeline.ref
end
end
end
diff --git a/app/services/ci/queue/builds_table_strategy.rb b/app/services/ci/queue/builds_table_strategy.rb
index c941734ac40..d0a343cb9d4 100644
--- a/app/services/ci/queue/builds_table_strategy.rb
+++ b/app/services/ci/queue/builds_table_strategy.rb
@@ -53,6 +53,14 @@ module Ci
relation.pluck(:id)
end
+ def use_denormalized_shared_runners_data?
+ false
+ end
+
+ def use_denormalized_minutes_data?
+ false
+ end
+
private
def running_builds_for_shared_runners
diff --git a/app/services/ci/queue/pending_builds_strategy.rb b/app/services/ci/queue/pending_builds_strategy.rb
index 55d5cb96a0a..efe3a981d3a 100644
--- a/app/services/ci/queue/pending_builds_strategy.rb
+++ b/app/services/ci/queue/pending_builds_strategy.rb
@@ -11,25 +11,9 @@ module Ci
# rubocop:disable CodeReuse/ActiveRecord
def builds_for_shared_runner
- relation = new_builds
- # don't run projects which have not enabled shared runners and builds
- .joins('INNER JOIN projects ON ci_pending_builds.project_id = projects.id')
- .where(projects: { shared_runners_enabled: true, pending_delete: false })
- .joins('LEFT JOIN project_features ON ci_pending_builds.project_id = project_features.project_id')
- .where('project_features.builds_access_level IS NULL or project_features.builds_access_level > 0')
+ shared_builds = builds_available_for_shared_runners
- if Feature.enabled?(:ci_queueing_disaster_recovery_disable_fair_scheduling, runner, type: :ops, default_enabled: :yaml)
- # if disaster recovery is enabled, we fallback to FIFO scheduling
- relation.order('ci_pending_builds.build_id ASC')
- else
- # Implement fair scheduling
- # this returns builds that are ordered by number of running builds
- # we prefer projects that don't use shared runners at all
- relation
- .with(running_builds_for_shared_runners_cte.to_arel)
- .joins("LEFT JOIN project_builds ON ci_pending_builds.project_id = project_builds.project_id")
- .order(Arel.sql('COALESCE(project_builds.running_builds, 0) ASC'), 'ci_pending_builds.build_id ASC')
- end
+ builds_ordered_for_shared_runners(shared_builds)
end
def builds_matching_tag_ids(relation, ids)
@@ -52,8 +36,44 @@ module Ci
relation.pluck(:build_id)
end
+ def use_denormalized_shared_runners_data?
+ ::Feature.enabled?(:ci_queueing_denormalize_shared_runners_information, runner, type: :development, default_enabled: :yaml)
+ end
+
+ def use_denormalized_minutes_data?
+ ::Feature.enabled?(:ci_queueing_denormalize_ci_minutes_information, runner, type: :development, default_enabled: :yaml)
+ end
+
private
+ def builds_available_for_shared_runners
+ if use_denormalized_shared_runners_data?
+ new_builds.with_instance_runners
+ else
+ new_builds
+ # don't run projects which have not enabled shared runners and builds
+ .joins('INNER JOIN projects ON ci_pending_builds.project_id = projects.id')
+ .where(projects: { shared_runners_enabled: true, pending_delete: false })
+ .joins('LEFT JOIN project_features ON ci_pending_builds.project_id = project_features.project_id')
+ .where('project_features.builds_access_level IS NULL or project_features.builds_access_level > 0')
+ end
+ end
+
+ def builds_ordered_for_shared_runners(relation)
+ if Feature.enabled?(:ci_queueing_disaster_recovery_disable_fair_scheduling, runner, type: :ops, default_enabled: :yaml)
+ # if disaster recovery is enabled, we fallback to FIFO scheduling
+ relation.order('ci_pending_builds.build_id ASC')
+ else
+ # Implement fair scheduling
+ # this returns builds that are ordered by number of running builds
+ # we prefer projects that don't use shared runners at all
+ relation
+ .with(running_builds_for_shared_runners_cte.to_arel)
+ .joins("LEFT JOIN project_builds ON ci_pending_builds.project_id = project_builds.project_id")
+ .order(Arel.sql('COALESCE(project_builds.running_builds, 0) ASC'), 'ci_pending_builds.build_id ASC')
+ end
+ end
+
def running_builds_for_shared_runners_cte
running_builds = ::Ci::RunningBuild
.instance_type
@@ -67,3 +87,5 @@ module Ci
end
end
end
+
+Ci::Queue::PendingBuildsStrategy.prepend_mod_with('Ci::Queue::PendingBuildsStrategy')
diff --git a/app/services/ci/resource_groups/assign_resource_from_resource_group_service.rb b/app/services/ci/resource_groups/assign_resource_from_resource_group_service.rb
index 9e3e6de3928..1d329fe7b53 100644
--- a/app/services/ci/resource_groups/assign_resource_from_resource_group_service.rb
+++ b/app/services/ci/resource_groups/assign_resource_from_resource_group_service.rb
@@ -5,6 +5,8 @@ module Ci
class AssignResourceFromResourceGroupService < ::BaseService
# rubocop: disable CodeReuse/ActiveRecord
def execute(resource_group)
+ release_resource_from_stale_jobs(resource_group)
+
free_resources = resource_group.resources.free.count
resource_group.processables.waiting_for_resource.take(free_resources).each do |processable|
@@ -12,6 +14,14 @@ module Ci
end
end
# rubocop: enable CodeReuse/ActiveRecord
+
+ private
+
+ def release_resource_from_stale_jobs(resource_group)
+ resource_group.resources.stale_processables.find_each do |processable|
+ resource_group.release_resource_from(processable)
+ end
+ end
end
end
end
diff --git a/app/services/ci/stop_environments_service.rb b/app/services/ci/stop_environments_service.rb
deleted file mode 100644
index 7c9fc44e7f4..00000000000
--- a/app/services/ci/stop_environments_service.rb
+++ /dev/null
@@ -1,50 +0,0 @@
-# frozen_string_literal: true
-
-module Ci
- class StopEnvironmentsService < BaseService
- attr_reader :ref
-
- def execute(branch_name)
- @ref = branch_name
-
- return unless @ref.present?
-
- environments.each { |environment| stop(environment) }
- end
-
- def execute_for_merge_request(merge_request)
- merge_request.environments.each { |environment| stop(environment) }
- end
-
- ##
- # This method is for stopping multiple environments in a batch style.
- # The maximum acceptable count of environments is roughly 5000. Please
- # apply acceptable `LIMIT` clause to the `environments` relation.
- def self.execute_in_batch(environments)
- stop_actions = environments.stop_actions.load
-
- environments.update_all(auto_stop_at: nil, state: 'stopped')
-
- stop_actions.each do |stop_action|
- stop_action.play(stop_action.user)
- rescue StandardError => e
- Gitlab::ErrorTracking.track_exception(e, deployable_id: stop_action.id)
- end
- end
-
- private
-
- def environments
- @environments ||= Environments::EnvironmentsByDeploymentsFinder
- .new(project, current_user, ref: @ref, recently_updated: true)
- .execute
- end
-
- def stop(environment)
- return unless environment.stop_action_available?
- return unless can?(current_user, :stop_environment, environment)
-
- environment.stop_with_action!(current_user)
- end
- end
-end
diff --git a/app/services/ci/unlock_artifacts_service.rb b/app/services/ci/unlock_artifacts_service.rb
index 07faf90dd6d..7c169cb8395 100644
--- a/app/services/ci/unlock_artifacts_service.rb
+++ b/app/services/ci/unlock_artifacts_service.rb
@@ -17,7 +17,7 @@ module Ci
SQL
loop do
- break if ActiveRecord::Base.connection.exec_query(query).empty?
+ break if Ci::Pipeline.connection.exec_query(query).empty?
end
end