Commit fdf3d7f9 authored by Imre Farkas's avatar Imre Farkas

Linear traversal query for Namespace#root_ancestor

Linear queries have better performance than recursive CTEs.

Changelog: performance
parent 623c043b
......@@ -64,6 +64,18 @@ module Namespaces
traversal_ids.present?
end
def root_ancestor
return super if parent.nil?
return super unless persisted?
return super if traversal_ids.blank?
return super unless Feature.enabled?(:use_traversal_ids_for_root_ancestor, default_enabled: :yaml)
strong_memoize(:root_ancestor) do
Namespace.find_by(id: traversal_ids.first)
end
end
def self_and_descendants
return super unless use_traversal_ids?
......@@ -100,7 +112,8 @@ module Namespaces
# Clear any previously memoized root_ancestor as our ancestors have changed.
clear_memoization(:root_ancestor)
Namespace::TraversalHierarchy.for_namespace(root_ancestor).sync_traversal_ids!
# We cannot rely on Namespaces::Traversal::Linear#root_ancestor because it might be stale
Namespace::TraversalHierarchy.for_namespace(recursive_root_ancestor).sync_traversal_ids!
end
# Lock the root of the hierarchy we just left, and lock the root of the hierarchy
......
......@@ -16,6 +16,7 @@ module Namespaces
parent.root_ancestor
end
end
alias_method :recursive_root_ancestor, :root_ancestor
# Returns all ancestors, self, and descendants of the current namespace.
def self_and_hierarchy
......
......@@ -108,10 +108,13 @@ module Groups
@group.parent = @new_parent_group
@group.clear_memoization(:self_and_ancestors_ids)
@group.clear_memoization(:root_ancestor) if different_root_ancestor?
inherit_group_shared_runners_settings
@group.save!
# #reload is called to make sure traversal_ids are reloaded
@group.reload # rubocop:disable Cop/ActiveRecordAssociationReload
end
# rubocop: disable CodeReuse/ActiveRecord
......
---
name: use_traversal_ids_for_root_ancestor
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/61163
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/331907
milestone: '14.0'
type: development
group: group::access
default_enabled: false
......@@ -17,6 +17,28 @@ RSpec.shared_examples 'namespace traversal' do
end
end
describe '#root_ancestor' do
let_it_be(:group) { create(:group) }
let_it_be(:nested_group) { create(:group, parent: group) }
let_it_be(:deep_nested_group) { create(:group, parent: nested_group) }
it 'returns the correct root ancestor' do
expect(group.root_ancestor).to eq(group)
expect(nested_group.root_ancestor).to eq(group)
expect(deep_nested_group.root_ancestor).to eq(group)
end
describe '#recursive_root_ancestor' do
let(:groups) { [group, nested_group, deep_nested_group] }
it "is equivalent to #recursive_root_ancestor" do
groups.each do |group|
expect(group.root_ancestor).to eq(group.recursive_root_ancestor)
end
end
end
end
describe '#self_and_hierarchy' do
let!(:group) { create(:group, path: 'git_lab') }
let!(:nested_group) { create(:group, parent: group) }
......
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