Commit 08ec6881 authored by Vitali Tatarintev's avatar Vitali Tatarintev

Hide decomposed query behind a feature flag

https://gitlab.com/gitlab-org/gitlab/-/issues/341341
parent 4e95bcd8
...@@ -47,12 +47,48 @@ module Ci ...@@ -47,12 +47,48 @@ module Ci
private private
# rubocop: disable CodeReuse/ActiveRecord
def pipelines_using_cte
sha_relation = merge_request.all_commits.select(:sha).distinct
cte = Gitlab::SQL::CTE.new(:shas, sha_relation)
pipelines_for_merge_requests = triggered_by_merge_request
pipelines_for_branch = filter_by_sha(triggered_for_branch, cte)
Ci::Pipeline.with(cte.to_arel) # rubocop: disable CodeReuse/ActiveRecord
.from_union([pipelines_for_merge_requests, pipelines_for_branch])
end
# rubocop: enable CodeReuse/ActiveRecord
def filter_by_sha(pipelines, cte)
hex = Arel::Nodes::SqlLiteral.new("'hex'")
string_sha = Arel::Nodes::NamedFunction.new('encode', [cte.table[:sha], hex])
join_condition = string_sha.eq(Ci::Pipeline.arel_table[:sha])
filter_by(pipelines, cte, join_condition)
end
def filter_by(pipelines, cte, join_condition)
shas_table =
Ci::Pipeline.arel_table
.join(cte.table, Arel::Nodes::InnerJoin)
.on(join_condition)
.join_sources
pipelines.joins(shas_table) # rubocop: disable CodeReuse/ActiveRecord
end
def all_pipelines_for_merge_request def all_pipelines_for_merge_request
shas = merge_request.all_commit_shas if Feature.enabled?(:decomposed_ci_query_in_pipelines_for_merge_request_finder, source_project, default_enabled: :yaml)
pipelines_for_merge_request = triggered_by_merge_request pipelines_using_cte
pipelines_for_branch = triggered_for_branch.for_sha(shas) else
shas = merge_request.all_commit_shas
pipelines_for_merge_request = triggered_by_merge_request
pipelines_for_branch = triggered_for_branch.for_sha(shas)
Ci::Pipeline.from_union([pipelines_for_merge_request, pipelines_for_branch]) Ci::Pipeline.from_union([pipelines_for_merge_request, pipelines_for_branch])
end
end end
# NOTE: this method returns only parent merge request pipelines. # NOTE: this method returns only parent merge request pipelines.
......
---
name: decomposed_ci_query_in_pipelines_for_merge_request_finder
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/68549
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/341341
milestone: '14.4'
type: development
group: group::pipeline execution
default_enabled: false
...@@ -134,117 +134,129 @@ RSpec.describe Ci::PipelinesForMergeRequestFinder do ...@@ -134,117 +134,129 @@ RSpec.describe Ci::PipelinesForMergeRequestFinder do
end end
end end
context 'when pipelines exist for the branch and merge request' do shared_examples 'returns all pipelines for merge request' do
let(:source_ref) { 'feature' } context 'when pipelines exist for the branch and merge request' do
let(:target_ref) { 'master' } let(:source_ref) { 'feature' }
let(:target_ref) { 'master' }
let!(:branch_pipeline) do let!(:branch_pipeline) do
create(:ci_pipeline, source: :push, project: project,
ref: source_ref, sha: shas.second)
end
let!(:tag_pipeline) do
create(:ci_pipeline, project: project, ref: source_ref, tag: true)
end
let!(:detached_merge_request_pipeline) do
create(:ci_pipeline, source: :merge_request_event, project: project,
ref: source_ref, sha: shas.second, merge_request: merge_request)
end
let(:merge_request) do
create(:merge_request, source_project: project, source_branch: source_ref,
target_project: project, target_branch: target_ref)
end
let(:project) { create(:project, :repository) }
let(:shas) { project.repository.commits(source_ref, limit: 2).map(&:id) }
before do
create(:merge_request_diff_commit,
merge_request_diff: merge_request.merge_request_diff,
sha: shas.second, relative_order: 1)
end
it 'returns merge request pipeline first' do
expect(subject.all).to eq([detached_merge_request_pipeline, branch_pipeline])
end
context 'when there are a branch pipeline and a merge request pipeline' do
let!(:branch_pipeline_2) do
create(:ci_pipeline, source: :push, project: project, create(:ci_pipeline, source: :push, project: project,
ref: source_ref, sha: shas.first) ref: source_ref, sha: shas.second)
end end
let!(:detached_merge_request_pipeline_2) do let!(:tag_pipeline) do
create(:ci_pipeline, source: :merge_request_event, project: project, create(:ci_pipeline, project: project, ref: source_ref, tag: true)
ref: source_ref, sha: shas.first, merge_request: merge_request)
end end
it 'returns merge request pipelines first' do let!(:detached_merge_request_pipeline) do
expect(subject.all) create(:ci_pipeline, source: :merge_request_event, project: project,
.to eq([detached_merge_request_pipeline_2, ref: source_ref, sha: shas.second, merge_request: merge_request)
detached_merge_request_pipeline,
branch_pipeline_2,
branch_pipeline])
end end
end
context 'when there are multiple merge request pipelines from the same branch' do let(:merge_request) do
let!(:branch_pipeline_2) do create(:merge_request, source_project: project, source_branch: source_ref,
create(:ci_pipeline, source: :push, project: project, target_project: project, target_branch: target_ref)
ref: source_ref, sha: shas.first)
end end
let!(:branch_pipeline_with_sha_not_belonging_to_merge_request) do let(:project) { create(:project, :repository) }
create(:ci_pipeline, source: :push, project: project, ref: source_ref) let(:shas) { project.repository.commits(source_ref, limit: 2).map(&:id) }
end
let!(:detached_merge_request_pipeline_2) do before do
create(:ci_pipeline, source: :merge_request_event, project: project, create(:merge_request_diff_commit,
ref: source_ref, sha: shas.first, merge_request: merge_request_2) merge_request_diff: merge_request.merge_request_diff,
sha: shas.second, relative_order: 1)
end end
let(:merge_request_2) do it 'returns merge request pipeline first' do
create(:merge_request, source_project: project, source_branch: source_ref, expect(subject.all).to eq([detached_merge_request_pipeline, branch_pipeline])
target_project: project, target_branch: 'stable')
end end
before do context 'when there are a branch pipeline and a merge request pipeline' do
shas.each.with_index do |sha, index| let!(:branch_pipeline_2) do
create(:merge_request_diff_commit, create(:ci_pipeline, source: :push, project: project,
merge_request_diff: merge_request_2.merge_request_diff, ref: source_ref, sha: shas.first)
sha: sha, relative_order: index)
end end
end
it 'returns only related merge request pipelines' do let!(:detached_merge_request_pipeline_2) do
expect(subject.all) create(:ci_pipeline, source: :merge_request_event, project: project,
.to eq([detached_merge_request_pipeline, ref: source_ref, sha: shas.first, merge_request: merge_request)
branch_pipeline_2, end
branch_pipeline])
expect(described_class.new(merge_request_2, nil).all) it 'returns merge request pipelines first' do
.to eq([detached_merge_request_pipeline_2, expect(subject.all)
branch_pipeline_2, .to eq([detached_merge_request_pipeline_2,
branch_pipeline]) detached_merge_request_pipeline,
branch_pipeline_2,
branch_pipeline])
end
end end
end
context 'when detached merge request pipeline is run on head ref of the merge request' do context 'when there are multiple merge request pipelines from the same branch' do
let!(:detached_merge_request_pipeline) do let!(:branch_pipeline_2) do
create(:ci_pipeline, source: :merge_request_event, project: project, create(:ci_pipeline, source: :push, project: project,
ref: merge_request.ref_path, sha: shas.second, merge_request: merge_request) ref: source_ref, sha: shas.first)
end end
let!(:branch_pipeline_with_sha_not_belonging_to_merge_request) do
create(:ci_pipeline, source: :push, project: project, ref: source_ref)
end
let!(:detached_merge_request_pipeline_2) do
create(:ci_pipeline, source: :merge_request_event, project: project,
ref: source_ref, sha: shas.first, merge_request: merge_request_2)
end
let(:merge_request_2) do
create(:merge_request, source_project: project, source_branch: source_ref,
target_project: project, target_branch: 'stable')
end
before do
shas.each.with_index do |sha, index|
create(:merge_request_diff_commit,
merge_request_diff: merge_request_2.merge_request_diff,
sha: sha, relative_order: index)
end
end
it 'sets the head ref of the merge request to the pipeline ref' do it 'returns only related merge request pipelines' do
expect(detached_merge_request_pipeline.ref).to match(%r{refs/merge-requests/\d+/head}) expect(subject.all)
.to eq([detached_merge_request_pipeline,
branch_pipeline_2,
branch_pipeline])
expect(described_class.new(merge_request_2, nil).all)
.to eq([detached_merge_request_pipeline_2,
branch_pipeline_2,
branch_pipeline])
end
end end
it 'includes the detached merge request pipeline even though the ref is custom path' do context 'when detached merge request pipeline is run on head ref of the merge request' do
expect(merge_request.all_pipelines).to include(detached_merge_request_pipeline) let!(:detached_merge_request_pipeline) do
create(:ci_pipeline, source: :merge_request_event, project: project,
ref: merge_request.ref_path, sha: shas.second, merge_request: merge_request)
end
it 'sets the head ref of the merge request to the pipeline ref' do
expect(detached_merge_request_pipeline.ref).to match(%r{refs/merge-requests/\d+/head})
end
it 'includes the detached merge request pipeline even though the ref is custom path' do
expect(merge_request.all_pipelines).to include(detached_merge_request_pipeline)
end
end end
end end
end end
it_behaves_like 'returns all pipelines for merge request'
context 'when `decomposed_ci_query_in_pipelines_for_merge_request_finder` feature flag disabled' do
before do
stub_feature_flags(decomposed_ci_query_in_pipelines_for_merge_request_finder: false)
end
it_behaves_like 'returns all pipelines for merge request'
end
end end
end end
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