Commit 2a53291a authored by Adam Hegyi's avatar Adam Hegyi

Use CTE for looking up projects with shared runners enabled

This change optionally wraps the namespace lookup with a CTE to act as
an optimization fence and prevent bad PG query planning. The change is
behind a feature flag:
use_cte_for_any_project_with_shared_runners_enabled
parent 318cfe28
---
name: use_cte_for_any_project_with_shared_runners_enabled
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/71452
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/342024
milestone: '14.4'
type: development
group: group::optimize
default_enabled: false
...@@ -260,10 +260,10 @@ module EE ...@@ -260,10 +260,10 @@ module EE
def any_project_with_shared_runners_enabled? def any_project_with_shared_runners_enabled?
if ::Feature.enabled?(:cache_shared_runners_enabled, self, default_enabled: :yaml) if ::Feature.enabled?(:cache_shared_runners_enabled, self, default_enabled: :yaml)
Rails.cache.fetch([self, :has_project_with_shared_runners_enabled], expires_in: 5.minutes) do Rails.cache.fetch([self, :has_project_with_shared_runners_enabled], expires_in: 5.minutes) do
all_projects.with_shared_runners.any? any_project_with_shared_runners_enabled_with_cte?
end end
else else
all_projects.with_shared_runners.any? any_project_with_shared_runners_enabled_with_cte?
end end
end end
...@@ -420,6 +420,25 @@ module EE ...@@ -420,6 +420,25 @@ module EE
private private
def any_project_with_shared_runners_enabled_with_cte?
if ::Feature.enabled?(:use_cte_for_any_project_with_shared_runners_enabled, self, default_enabled: :yaml)
projects_query = if user_namespace?
projects
else
cte = ::Gitlab::SQL::CTE.new(:namespace_self_and_descendants_cte, self_and_descendant_ids)
::Project
.with(cte.to_arel)
.from([::Project.table_name, cte.table.name].join(', '))
.where(::Project.arel_table[:namespace_id].eq(cte.table[:id]))
end
projects_query.with_shared_runners.any?
else
all_projects.with_shared_runners.any?
end
end
def fallback_plan def fallback_plan
if ::Gitlab.com? if ::Gitlab.com?
::Plan.free ::Plan.free
......
...@@ -638,32 +638,58 @@ RSpec.describe Namespace do ...@@ -638,32 +638,58 @@ RSpec.describe Namespace do
describe '#any_project_with_shared_runners_enabled?' do describe '#any_project_with_shared_runners_enabled?' do
subject { namespace.any_project_with_shared_runners_enabled? } subject { namespace.any_project_with_shared_runners_enabled? }
context 'without projects' do shared_examples '#any_project_with_shared_runners_enabled? examples' do
it { is_expected.to be_falsey } context 'without projects' do
end it { is_expected.to be_falsey }
end
context 'group with shared runners enabled project' do context 'group with shared runners enabled project' do
let!(:project) { create(:project, namespace: namespace, shared_runners_enabled: true) } let!(:project) { create(:project, namespace: namespace, shared_runners_enabled: true) }
it { is_expected.to be_truthy }
end
context 'subgroup with shared runners enabled project' do
let(:namespace) { create(:group) }
let(:subgroup) { create(:group, parent: namespace) }
let!(:subproject) { create(:project, namespace: subgroup, shared_runners_enabled: true) }
it { is_expected.to be_truthy }
end
context 'with project and disabled shared runners' do
let!(:project) do
create(:project,
namespace: namespace,
shared_runners_enabled: false)
end
it { is_expected.to be_truthy } it { is_expected.to be_falsey }
end
end end
context 'subgroup with shared runners enabled project' do context 'when use_cte_for_any_project_with_shared_runners_enabled is enabled' do
let(:namespace) { create(:group) } before do
let(:subgroup) { create(:group, parent: namespace) } stub_feature_flags(use_cte_for_any_project_with_shared_runners_enabled: true)
let!(:subproject) { create(:project, namespace: subgroup, shared_runners_enabled: true) } end
it { is_expected.to be_truthy } it_behaves_like '#any_project_with_shared_runners_enabled? examples' do
it 'creates a CTE' do
group = create(:group)
expect(Gitlab::SQL::CTE).to receive(:new).and_call_original
group.any_project_with_shared_runners_enabled?
end
end
end end
context 'with project and disabled shared runners' do context 'when use_cte_for_any_project_with_shared_runners_enabled is disabled' do
let!(:project) do before do
create(:project, stub_feature_flags(use_cte_for_any_project_with_shared_runners_enabled: false)
namespace: namespace,
shared_runners_enabled: false)
end end
it { is_expected.to be_falsey } it_behaves_like '#any_project_with_shared_runners_enabled? examples'
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