Commit 794ed77b authored by Fabio Pitino's avatar Fabio Pitino

Merge branch '341561-order-by-stage' into 'master'

Fix the order of subsequent jobs when requeuing a job

See merge request gitlab-org/gitlab!77528
parents ce8704fa d02c2225
...@@ -3,39 +3,50 @@ ...@@ -3,39 +3,50 @@
module Ci module Ci
class AfterRequeueJobService < ::BaseService class AfterRequeueJobService < ::BaseService
def execute(processable) def execute(processable)
process_subsequent_jobs(processable) @processable = processable
reset_source_bridge(processable)
process_subsequent_jobs
reset_source_bridge
end end
private private
def process_subsequent_jobs(processable) def process_subsequent_jobs
(stage_dependent_jobs(processable) | needs_dependent_jobs(processable)) dependent_jobs.each do |job|
.each do |processable| process(job)
process(processable) end
end end
def reset_source_bridge
@processable.pipeline.reset_source_bridge!(current_user)
end end
def reset_source_bridge(processable) def dependent_jobs
processable.pipeline.reset_source_bridge!(current_user) if ::Feature.enabled?(:ci_order_subsequent_jobs_by_stage, @processable.pipeline.project, default_enabled: :yaml)
stage_dependent_jobs
.or(needs_dependent_jobs.except(:preload))
.ordered_by_stage
else
stage_dependent_jobs | needs_dependent_jobs
end
end end
def process(processable) def process(job)
Gitlab::OptimisticLocking.retry_lock(processable, name: 'ci_requeue_job') do |processable| Gitlab::OptimisticLocking.retry_lock(job, name: 'ci_requeue_job') do |job|
processable.process(current_user) job.process(current_user)
end end
end end
def skipped_jobs(processable) def stage_dependent_jobs
processable.pipeline.processables.skipped skipped_jobs.after_stage(@processable.stage_idx)
end end
def stage_dependent_jobs(processable) def needs_dependent_jobs
skipped_jobs(processable).after_stage(processable.stage_idx) skipped_jobs.scheduling_type_dag.with_needs([@processable.name])
end end
def needs_dependent_jobs(processable) def skipped_jobs
skipped_jobs(processable).scheduling_type_dag.with_needs([processable.name]) @skipped_jobs ||= @processable.pipeline.processables.skipped
end end
end end
end end
---
name: ci_order_subsequent_jobs_by_stage
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/77528
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/349977
milestone: '14.7'
type: development
group: group::pipeline authoring
default_enabled: false
...@@ -5,6 +5,7 @@ FactoryBot.define do ...@@ -5,6 +5,7 @@ FactoryBot.define do
name { 'test' } name { 'test' }
add_attribute(:protected) { false } add_attribute(:protected) { false }
created_at { 'Di 29. Okt 09:50:00 CET 2013' } created_at { 'Di 29. Okt 09:50:00 CET 2013' }
scheduling_type { 'stage' }
pending pending
options do options do
...@@ -33,6 +34,8 @@ FactoryBot.define do ...@@ -33,6 +34,8 @@ FactoryBot.define do
end end
trait :dependent do trait :dependent do
scheduling_type { 'dag' }
transient do transient do
sequence(:needed_name) { |n| "dependency #{n}" } sequence(:needed_name) { |n| "dependency #{n}" }
needed { association(:ci_build, name: needed_name, pipeline: pipeline) } needed { association(:ci_build, name: needed_name, pipeline: pipeline) }
......
...@@ -8,14 +8,15 @@ RSpec.describe Ci::AfterRequeueJobService do ...@@ -8,14 +8,15 @@ RSpec.describe Ci::AfterRequeueJobService do
let(:pipeline) { create(:ci_pipeline, project: project) } let(:pipeline) { create(:ci_pipeline, project: project) }
let!(:build) { create(:ci_build, pipeline: pipeline, stage_idx: 0, name: 'build') } let!(:build1) { create(:ci_build, name: 'build1', pipeline: pipeline, stage_idx: 0) }
let!(:test1) { create(:ci_build, :success, pipeline: pipeline, stage_idx: 1) } let!(:test1) { create(:ci_build, :success, name: 'test1', pipeline: pipeline, stage_idx: 1) }
let!(:test2) { create(:ci_build, :skipped, pipeline: pipeline, stage_idx: 1) } let!(:test2) { create(:ci_build, :skipped, name: 'test2', pipeline: pipeline, stage_idx: 1) }
let!(:test3) { create(:ci_build, :skipped, :dependent, pipeline: pipeline, stage_idx: 1, needed: build) } let!(:test3) { create(:ci_build, :skipped, :dependent, name: 'test3', pipeline: pipeline, stage_idx: 1, needed: build1) }
let!(:deploy) { create(:ci_build, :skipped, :dependent, pipeline: pipeline, stage_idx: 2, needed: test3) } let!(:deploy) { create(:ci_build, :skipped, :dependent, name: 'deploy', pipeline: pipeline, stage_idx: 2, needed: test3) }
subject(:execute_service) { described_class.new(project, user).execute(build) } subject(:execute_service) { described_class.new(project, user).execute(build1) }
shared_examples 'processing subsequent skipped jobs' do
it 'marks subsequent skipped jobs as processable' do it 'marks subsequent skipped jobs as processable' do
expect(test1.reload).to be_success expect(test1.reload).to be_success
expect(test2.reload).to be_skipped expect(test2.reload).to be_skipped
...@@ -29,25 +30,34 @@ RSpec.describe Ci::AfterRequeueJobService do ...@@ -29,25 +30,34 @@ RSpec.describe Ci::AfterRequeueJobService do
expect(test3.reload).to be_created expect(test3.reload).to be_created
expect(deploy.reload).to be_created expect(deploy.reload).to be_created
end end
end
it_behaves_like 'processing subsequent skipped jobs'
context 'when there is a job need from the same stage' do context 'when there is a job need from the same stage' do
let!(:test4) do let!(:build2) do
create(:ci_build, create(:ci_build,
:skipped, :skipped,
:dependent, :dependent,
name: 'build2',
pipeline: pipeline, pipeline: pipeline,
stage_idx: 0, stage_idx: 0,
scheduling_type: :dag, scheduling_type: :dag,
needed: build) needed: build1)
end end
shared_examples 'processing the same stage job' do
it 'marks subsequent skipped jobs as processable' do it 'marks subsequent skipped jobs as processable' do
expect { execute_service }.to change { test4.reload.status }.from('skipped').to('created') expect { execute_service }.to change { build2.reload.status }.from('skipped').to('created')
end end
end end
it_behaves_like 'processing subsequent skipped jobs'
it_behaves_like 'processing the same stage job'
end
context 'when the pipeline is a downstream pipeline and the bridge is depended' do context 'when the pipeline is a downstream pipeline and the bridge is depended' do
let!(:trigger_job) { create(:ci_bridge, :strategy_depend, status: 'success') } let!(:trigger_job) { create(:ci_bridge, :strategy_depend, name: 'trigger_job', status: 'success') }
before do before do
create(:ci_sources_pipeline, pipeline: pipeline, source_job: trigger_job) create(:ci_sources_pipeline, pipeline: pipeline, source_job: trigger_job)
......
...@@ -1004,6 +1004,63 @@ RSpec.describe Ci::PipelineProcessing::AtomicProcessingService do ...@@ -1004,6 +1004,63 @@ RSpec.describe Ci::PipelineProcessing::AtomicProcessingService do
end end
end end
context 'when the dependency is stage-independent', :sidekiq_inline do
let(:config) do
<<-EOY
stages: [A, B]
A1:
stage: A
script: exit 0
when: manual
A2:
stage: A
script: exit 0
needs: [A1]
B:
stage: B
needs: [A2]
script: exit 0
EOY
end
let(:pipeline) do
Ci::CreatePipelineService.new(project, user, { ref: 'master' }).execute(:push).payload
end
before do
stub_ci_pipeline_yaml_file(config)
end
it 'processes subsequent jobs in the correct order when playing first job' do
expect(all_builds_names).to eq(%w[A1 A2 B])
expect(all_builds_statuses).to eq(%w[manual skipped skipped])
play_manual_action('A1')
expect(all_builds_names).to eq(%w[A1 A2 B])
expect(all_builds_statuses).to eq(%w[pending created created])
end
context 'when the FF ci_order_subsequent_jobs_by_stage is disabled' do
before do
stub_feature_flags(ci_order_subsequent_jobs_by_stage: false)
end
it 'processes subsequent jobs in an incorrect order when playing first job' do
expect(all_builds_names).to eq(%w[A1 A2 B])
expect(all_builds_statuses).to eq(%w[manual skipped skipped])
play_manual_action('A1')
expect(all_builds_names).to eq(%w[A1 A2 B])
expect(all_builds_statuses).to eq(%w[pending created skipped])
end
end
end
private private
def all_builds def all_builds
......
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