diff --git a/app/models/namespaces/traversal/linear_scopes.rb b/app/models/namespaces/traversal/linear_scopes.rb index 0a4216e043a1e8492dccb3a41f1c987b5be0650f..f5c44171c4293e3c409faf8681ee5044dbb02d12 100644 --- a/app/models/namespaces/traversal/linear_scopes.rb +++ b/app/models/namespaces/traversal/linear_scopes.rb @@ -15,6 +15,13 @@ def as_ids select('namespaces.traversal_ids[array_length(namespaces.traversal_ids, 1)] AS id') end + def roots + return super unless use_traversal_ids_roots? + + root_ids = all.select("#{quoted_table_name}.traversal_ids[1]").distinct + unscoped.where(id: root_ids) + end + def self_and_ancestors(include_self: true, hierarchy_order: nil) return super unless use_traversal_ids_for_ancestor_scopes? @@ -83,6 +90,11 @@ def use_traversal_ids? Feature.enabled?(:use_traversal_ids, default_enabled: :yaml) end + def use_traversal_ids_roots? + Feature.enabled?(:use_traversal_ids_roots, default_enabled: :yaml) && + use_traversal_ids? + end + def use_traversal_ids_for_ancestor_scopes? Feature.enabled?(:use_traversal_ids_for_ancestor_scopes, default_enabled: :yaml) && use_traversal_ids? diff --git a/app/models/namespaces/traversal/recursive_scopes.rb b/app/models/namespaces/traversal/recursive_scopes.rb index 6659cefe095366806c74d6ef0dfb2957ea99bf70..925d9b8bb0c09ea4e4f67ebd6ca2f4c8adf2b0f5 100644 --- a/app/models/namespaces/traversal/recursive_scopes.rb +++ b/app/models/namespaces/traversal/recursive_scopes.rb @@ -10,6 +10,13 @@ def as_ids select('id') end + def roots + Gitlab::ObjectHierarchy + .new(all) + .base_and_ancestors + .where(namespaces: { parent_id: nil }) + end + def self_and_ancestors(include_self: true, hierarchy_order: nil) records = Gitlab::ObjectHierarchy.new(all).base_and_ancestors(hierarchy_order: hierarchy_order) diff --git a/config/feature_flags/development/use_traversal_ids_roots.yml b/config/feature_flags/development/use_traversal_ids_roots.yml new file mode 100644 index 0000000000000000000000000000000000000000..3c0685dc8729490617e5fcb54a24c8bc3c461c43 --- /dev/null +++ b/config/feature_flags/development/use_traversal_ids_roots.yml @@ -0,0 +1,8 @@ +--- +name: use_traversal_ids_roots +introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/74148 +rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/345438 +milestone: '14.5' +type: development +group: group::workspace +default_enabled: false diff --git a/spec/support/shared_examples/namespaces/traversal_scope_examples.rb b/spec/support/shared_examples/namespaces/traversal_scope_examples.rb index f797eea6b6e23958a1f02f298e8c8e0d47cff620..4c09c1c2a3b1390bd6eb900ad19d0e8bbb8fa3ca 100644 --- a/spec/support/shared_examples/namespaces/traversal_scope_examples.rb +++ b/spec/support/shared_examples/namespaces/traversal_scope_examples.rb @@ -49,6 +49,53 @@ it { is_expected.to eq described_class.column_names } end + shared_examples '.roots' do + context 'with only sub-groups' do + subject { described_class.where(id: [deep_nested_group_1, nested_group_1, deep_nested_group_2]).roots } + + it { is_expected.to contain_exactly(group_1, group_2) } + end + + context 'with only root groups' do + subject { described_class.where(id: [group_1, group_2]).roots } + + it { is_expected.to contain_exactly(group_1, group_2) } + end + + context 'with all groups' do + subject { described_class.where(id: groups).roots } + + it { is_expected.to contain_exactly(group_1, group_2) } + end + end + + describe '.roots' do + context "use_traversal_ids_roots feature flag is true" do + before do + stub_feature_flags(use_traversal_ids: true) + stub_feature_flags(use_traversal_ids_roots: true) + end + + it_behaves_like '.roots' + + it 'not make recursive queries' do + expect { described_class.where(id: [nested_group_1]).roots.load }.not_to make_queries_matching(/WITH RECURSIVE/) + end + end + + context "use_traversal_ids_roots feature flag is false" do + before do + stub_feature_flags(use_traversal_ids_roots: false) + end + + it_behaves_like '.roots' + + it 'make recursive queries' do + expect { described_class.where(id: [nested_group_1]).roots.load }.to make_queries_matching(/WITH RECURSIVE/) + end + end + end + shared_examples '.self_and_ancestors' do subject { described_class.where(id: [nested_group_1, nested_group_2]).self_and_ancestors }