Commit b8c77157 authored by Marius Bobin's avatar Marius Bobin Committed by Furkan Ayhan

Prevent database deadlocks when deleting projects

Changelog: fixed
parent 51d26555
......@@ -25,6 +25,7 @@ module Ci
}.freeze
CONFIG_EXTENSION = '.gitlab-ci.yml'
DEFAULT_CONFIG_PATH = CONFIG_EXTENSION
CANCELABLE_STATUSES = (Ci::HasStatus::CANCELABLE_STATUSES + ['manual']).freeze
BridgeStatusError = Class.new(StandardError)
......
......@@ -13,6 +13,7 @@ module Ci
ORDERED_STATUSES = %w[failed preparing pending running waiting_for_resource manual scheduled canceled success skipped created].freeze
PASSED_WITH_WARNINGS_STATUSES = %w[failed canceled].to_set.freeze
EXCLUDE_IGNORED_STATUSES = %w[manual failed canceled].to_set.freeze
CANCELABLE_STATUSES = %w[running waiting_for_resource preparing pending created scheduled].freeze
STATUSES_ENUM = { created: 0, pending: 1, running: 2, success: 3,
failed: 4, canceled: 5, skipped: 6, manual: 7,
scheduled: 8, preparing: 9, waiting_for_resource: 10 }.freeze
......@@ -85,7 +86,7 @@ module Ci
scope :waiting_for_resource_or_upcoming, -> { with_status(:created, :scheduled, :waiting_for_resource) }
scope :cancelable, -> do
where(status: [:running, :waiting_for_resource, :preparing, :pending, :created, :scheduled])
where(status: klass::CANCELABLE_STATUSES)
end
scope :without_statuses, -> (names) do
......
......@@ -2890,6 +2890,34 @@ RSpec.describe Ci::Pipeline, :mailer, factory_default: :keep do
end
end
describe '.cancelable' do
subject { described_class.cancelable }
shared_examples 'containing the pipeline' do |status|
context "when it's #{status} pipeline" do
let!(:pipeline) { create(:ci_pipeline, status: status) }
it { is_expected.to contain_exactly(pipeline) }
end
end
shared_examples 'not containing the pipeline' do |status|
context "when it's #{status} pipeline" do
let!(:pipeline) { create(:ci_pipeline, status: status) }
it { is_expected.to be_empty }
end
end
%i[running pending waiting_for_resource preparing created scheduled manual].each do |status|
it_behaves_like 'containing the pipeline', status
end
%i[failed success skipped canceled].each do |status|
it_behaves_like 'not containing the pipeline', status
end
end
describe '#retry_failed' do
subject(:latest_status) { pipeline.latest_statuses.pluck(:status) }
......
......@@ -7,24 +7,51 @@ RSpec.describe Ci::AbortPipelinesService do
let_it_be(:project) { create(:project, namespace: user.namespace) }
let_it_be(:cancelable_pipeline, reload: true) { create(:ci_pipeline, :running, project: project, user: user) }
let_it_be(:manual_pipeline, reload: true) { create(:ci_pipeline, status: :manual, project: project, user: user) } # not cancelable
let_it_be(:manual_pipeline, reload: true) { create(:ci_pipeline, status: :manual, project: project, user: user) }
let_it_be(:other_users_pipeline, reload: true) { create(:ci_pipeline, :running, project: project, user: create(:user)) } # not this user's pipeline
let_it_be(:cancelable_build, reload: true) { create(:ci_build, :running, pipeline: cancelable_pipeline) }
let_it_be(:non_cancelable_build, reload: true) { create(:ci_build, :success, pipeline: cancelable_pipeline) }
let_it_be(:cancelable_stage, reload: true) { create(:ci_stage_entity, name: 'stageA', status: :running, pipeline: cancelable_pipeline, project: project) }
let_it_be(:non_cancelable_stage, reload: true) { create(:ci_stage_entity, name: 'stageB', status: :success, pipeline: cancelable_pipeline, project: project) }
let_it_be(:manual_pipeline_cancelable_build, reload: true) { create(:ci_build, :created, pipeline: manual_pipeline) }
let_it_be(:manual_pipeline_non_cancelable_build, reload: true) { create(:ci_build, :manual, pipeline: manual_pipeline) }
let_it_be(:manual_pipeline_cancelable_stage, reload: true) { create(:ci_stage_entity, name: 'stageA', status: :created, pipeline: manual_pipeline, project: project) }
let_it_be(:manual_pipeline_non_cancelable_stage, reload: true) { create(:ci_stage_entity, name: 'stageB', status: :success, pipeline: manual_pipeline, project: project) }
describe '#execute' do
def expect_correct_cancellations
def expect_correct_pipeline_cancellations
expect(cancelable_pipeline.finished_at).not_to be_nil
expect(cancelable_pipeline.status).to eq('failed')
expect((cancelable_pipeline.stages - [non_cancelable_stage]).map(&:status)).to all(eq('failed'))
expect(cancelable_build.status).to eq('failed')
expect(cancelable_pipeline).to be_failed
expect(manual_pipeline.finished_at).not_to be_nil
expect(manual_pipeline).to be_failed
end
def expect_correct_stage_cancellations
expect(cancelable_pipeline.stages - [non_cancelable_stage]).to all(be_failed)
expect(manual_pipeline.stages - [manual_pipeline_non_cancelable_stage]).to all(be_failed)
expect(non_cancelable_stage).not_to be_failed
expect(manual_pipeline_non_cancelable_stage).not_to be_failed
end
def expect_correct_build_cancellations
expect(cancelable_build).to be_failed
expect(cancelable_build.finished_at).not_to be_nil
expect(manual_pipeline.status).not_to eq('failed')
expect(non_cancelable_stage.status).not_to eq('failed')
expect(non_cancelable_build.status).not_to eq('failed')
expect(manual_pipeline_cancelable_build).to be_failed
expect(manual_pipeline_cancelable_build.finished_at).not_to be_nil
expect(non_cancelable_build).not_to be_failed
expect(manual_pipeline_non_cancelable_build).not_to be_failed
end
def expect_correct_cancellations
expect_correct_pipeline_cancellations
expect_correct_stage_cancellations
expect_correct_build_cancellations
end
context 'with project pipelines' do
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment