Commit de0dae19 authored by Jan Provaznik's avatar Jan Provaznik

Merge branch '325725-skip-ordering-for-ci-shared-runners-cte-query' into 'master'

Add option to skip ordering when using DISTINCT

See merge request gitlab-org/gitlab!57312
parents 9c0ef184 0b408bec
...@@ -75,7 +75,7 @@ module EE ...@@ -75,7 +75,7 @@ module EE
# rubocop: disable CodeReuse/ActiveRecord # rubocop: disable CodeReuse/ActiveRecord
def all_namespaces def all_namespaces
namespaces = ::Namespace.reorder(nil).where('namespaces.id = projects.namespace_id') namespaces = ::Namespace.reorder(nil).where('namespaces.id = projects.namespace_id')
::Gitlab::ObjectHierarchy.new(namespaces).roots ::Gitlab::ObjectHierarchy.new(namespaces, options: { skip_ordering: true }).roots
end end
# rubocop: enable CodeReuse/ActiveRecord # rubocop: enable CodeReuse/ActiveRecord
......
---
title: Skip ordering when using distinct in shared runners CTE query
merge_request: 57312
author:
type: performance
...@@ -74,9 +74,16 @@ module Gitlab ...@@ -74,9 +74,16 @@ module Gitlab
read_only(model.from(Arel::Nodes::As.new(recursive_query.arel, objects_table)).order(depth: hierarchy_order)) read_only(model.from(Arel::Nodes::As.new(recursive_query.arel, objects_table)).order(depth: hierarchy_order))
else else
recursive_query = base_and_ancestors_cte(upto).apply_to(model.all) recursive_query = base_and_ancestors_cte(upto).apply_to(model.all)
recursive_query = recursive_query.reselect(*recursive_query.arel.projections, 'ROW_NUMBER() OVER () as depth').distinct
recursive_query = model.from(Arel::Nodes::As.new(recursive_query.arel, objects_table)) if skip_ordering?
read_only(remove_depth_and_maintain_order(recursive_query, hierarchy_order: hierarchy_order)) recursive_query = recursive_query.distinct
else
recursive_query = recursive_query.reselect(*recursive_query.arel.projections, 'ROW_NUMBER() OVER () as depth').distinct
recursive_query = model.from(Arel::Nodes::As.new(recursive_query.arel, objects_table))
recursive_query = remove_depth_and_maintain_order(recursive_query, hierarchy_order: hierarchy_order)
end
read_only(recursive_query)
end end
else else
recursive_query = base_and_ancestors_cte(upto, hierarchy_order).apply_to(model.all) recursive_query = base_and_ancestors_cte(upto, hierarchy_order).apply_to(model.all)
...@@ -100,9 +107,16 @@ module Gitlab ...@@ -100,9 +107,16 @@ module Gitlab
read_only(model.from(Arel::Nodes::As.new(base_cte.arel, objects_table)).order(depth: :asc)) read_only(model.from(Arel::Nodes::As.new(base_cte.arel, objects_table)).order(depth: :asc))
else else
base_cte = base_and_descendants_cte.apply_to(model.all) base_cte = base_and_descendants_cte.apply_to(model.all)
base_cte = base_cte.reselect(*base_cte.arel.projections, 'ROW_NUMBER() OVER () as depth').distinct
base_cte = model.from(Arel::Nodes::As.new(base_cte.arel, objects_table)) if skip_ordering?
read_only(remove_depth_and_maintain_order(base_cte, hierarchy_order: :asc)) base_cte = base_cte.distinct
else
base_cte = base_cte.reselect(*base_cte.arel.projections, 'ROW_NUMBER() OVER () as depth').distinct
base_cte = model.from(Arel::Nodes::As.new(base_cte.arel, objects_table))
base_cte = remove_depth_and_maintain_order(base_cte, hierarchy_order: :asc)
end
read_only(base_cte)
end end
else else
read_only(base_and_descendants_cte(with_depth: with_depth).apply_to(model.all)) read_only(base_and_descendants_cte(with_depth: with_depth).apply_to(model.all))
...@@ -173,6 +187,13 @@ module Gitlab ...@@ -173,6 +187,13 @@ module Gitlab
false false
end end
# Skips the extra ordering when using distinct on the namespace queries
def skip_ordering?
return options[:skip_ordering] if options.key?(:skip_ordering)
false
end
# Remove the extra `depth` field using an INNER JOIN to avoid breaking UNION queries # Remove the extra `depth` field using an INNER JOIN to avoid breaking UNION queries
# and ordering the rows based on the `depth` column to maintain the row order. # and ordering the rows based on the `depth` column to maintain the row order.
# #
......
...@@ -3,14 +3,16 @@ ...@@ -3,14 +3,16 @@
require 'spec_helper' require 'spec_helper'
RSpec.describe Gitlab::ObjectHierarchy do RSpec.describe Gitlab::ObjectHierarchy do
let!(:parent) { create(:group) } let_it_be(:parent) { create(:group) }
let!(:child1) { create(:group, parent: parent) } let_it_be(:child1) { create(:group, parent: parent) }
let!(:child2) { create(:group, parent: child1) } let_it_be(:child2) { create(:group, parent: child1) }
let(:options) { {} }
shared_context 'Gitlab::ObjectHierarchy test cases' do shared_context 'Gitlab::ObjectHierarchy test cases' do
describe '#base_and_ancestors' do describe '#base_and_ancestors' do
let(:relation) do let(:relation) do
described_class.new(Group.where(id: child2.id)).base_and_ancestors described_class.new(Group.where(id: child2.id), options: options).base_and_ancestors
end end
it 'includes the base rows' do it 'includes the base rows' do
...@@ -22,13 +24,13 @@ RSpec.describe Gitlab::ObjectHierarchy do ...@@ -22,13 +24,13 @@ RSpec.describe Gitlab::ObjectHierarchy do
end end
it 'can find ancestors upto a certain level' do it 'can find ancestors upto a certain level' do
relation = described_class.new(Group.where(id: child2)).base_and_ancestors(upto: child1) relation = described_class.new(Group.where(id: child2), options: options).base_and_ancestors(upto: child1)
expect(relation).to contain_exactly(child2) expect(relation).to contain_exactly(child2)
end end
it 'uses ancestors_base #initialize argument' do it 'uses ancestors_base #initialize argument' do
relation = described_class.new(Group.where(id: child2.id), Group.none).base_and_ancestors relation = described_class.new(Group.where(id: child2.id), Group.none, options: options).base_and_ancestors
expect(relation).to include(parent, child1, child2) expect(relation).to include(parent, child1, child2)
end end
...@@ -40,7 +42,7 @@ RSpec.describe Gitlab::ObjectHierarchy do ...@@ -40,7 +42,7 @@ RSpec.describe Gitlab::ObjectHierarchy do
describe 'hierarchy_order option' do describe 'hierarchy_order option' do
let(:relation) do let(:relation) do
described_class.new(Group.where(id: child2.id)).base_and_ancestors(hierarchy_order: hierarchy_order) described_class.new(Group.where(id: child2.id), options: options).base_and_ancestors(hierarchy_order: hierarchy_order)
end end
context ':asc' do context ':asc' do
...@@ -63,7 +65,7 @@ RSpec.describe Gitlab::ObjectHierarchy do ...@@ -63,7 +65,7 @@ RSpec.describe Gitlab::ObjectHierarchy do
describe '#base_and_descendants' do describe '#base_and_descendants' do
let(:relation) do let(:relation) do
described_class.new(Group.where(id: parent.id)).base_and_descendants described_class.new(Group.where(id: parent.id), options: options).base_and_descendants
end end
it 'includes the base rows' do it 'includes the base rows' do
...@@ -75,7 +77,7 @@ RSpec.describe Gitlab::ObjectHierarchy do ...@@ -75,7 +77,7 @@ RSpec.describe Gitlab::ObjectHierarchy do
end end
it 'uses descendants_base #initialize argument' do it 'uses descendants_base #initialize argument' do
relation = described_class.new(Group.none, Group.where(id: parent.id)).base_and_descendants relation = described_class.new(Group.none, Group.where(id: parent.id), options: options).base_and_descendants
expect(relation).to include(parent, child1, child2) expect(relation).to include(parent, child1, child2)
end end
...@@ -87,7 +89,7 @@ RSpec.describe Gitlab::ObjectHierarchy do ...@@ -87,7 +89,7 @@ RSpec.describe Gitlab::ObjectHierarchy do
context 'when with_depth is true' do context 'when with_depth is true' do
let(:relation) do let(:relation) do
described_class.new(Group.where(id: parent.id)).base_and_descendants(with_depth: true) described_class.new(Group.where(id: parent.id), options: options).base_and_descendants(with_depth: true)
end end
it 'includes depth in the results' do it 'includes depth in the results' do
...@@ -106,14 +108,14 @@ RSpec.describe Gitlab::ObjectHierarchy do ...@@ -106,14 +108,14 @@ RSpec.describe Gitlab::ObjectHierarchy do
describe '#descendants' do describe '#descendants' do
it 'includes only the descendants' do it 'includes only the descendants' do
relation = described_class.new(Group.where(id: parent)).descendants relation = described_class.new(Group.where(id: parent), options: options).descendants
expect(relation).to contain_exactly(child1, child2) expect(relation).to contain_exactly(child1, child2)
end end
end end
describe '#max_descendants_depth' do describe '#max_descendants_depth' do
subject { described_class.new(base_relation).max_descendants_depth } subject { described_class.new(base_relation, options: options).max_descendants_depth }
context 'when base relation is empty' do context 'when base relation is empty' do
let(:base_relation) { Group.where(id: nil) } let(:base_relation) { Group.where(id: nil) }
...@@ -136,13 +138,13 @@ RSpec.describe Gitlab::ObjectHierarchy do ...@@ -136,13 +138,13 @@ RSpec.describe Gitlab::ObjectHierarchy do
describe '#ancestors' do describe '#ancestors' do
it 'includes only the ancestors' do it 'includes only the ancestors' do
relation = described_class.new(Group.where(id: child2)).ancestors relation = described_class.new(Group.where(id: child2), options: options).ancestors
expect(relation).to contain_exactly(child1, parent) expect(relation).to contain_exactly(child1, parent)
end end
it 'can find ancestors upto a certain level' do it 'can find ancestors upto a certain level' do
relation = described_class.new(Group.where(id: child2)).ancestors(upto: child1) relation = described_class.new(Group.where(id: child2), options: options).ancestors(upto: child1)
expect(relation).to be_empty expect(relation).to be_empty
end end
...@@ -150,7 +152,7 @@ RSpec.describe Gitlab::ObjectHierarchy do ...@@ -150,7 +152,7 @@ RSpec.describe Gitlab::ObjectHierarchy do
describe '#all_objects' do describe '#all_objects' do
let(:relation) do let(:relation) do
described_class.new(Group.where(id: child1.id)).all_objects described_class.new(Group.where(id: child1.id), options: options).all_objects
end end
it 'includes the base rows' do it 'includes the base rows' do
...@@ -166,13 +168,13 @@ RSpec.describe Gitlab::ObjectHierarchy do ...@@ -166,13 +168,13 @@ RSpec.describe Gitlab::ObjectHierarchy do
end end
it 'uses ancestors_base #initialize argument for ancestors' do it 'uses ancestors_base #initialize argument for ancestors' do
relation = described_class.new(Group.where(id: child1.id), Group.where(id: non_existing_record_id)).all_objects relation = described_class.new(Group.where(id: child1.id), Group.where(id: non_existing_record_id), options: options).all_objects
expect(relation).to include(parent) expect(relation).to include(parent)
end end
it 'uses descendants_base #initialize argument for descendants' do it 'uses descendants_base #initialize argument for descendants' do
relation = described_class.new(Group.where(id: non_existing_record_id), Group.where(id: child1.id)).all_objects relation = described_class.new(Group.where(id: non_existing_record_id), Group.where(id: child1.id), options: options).all_objects
expect(relation).to include(child2) expect(relation).to include(child2)
end end
...@@ -210,6 +212,19 @@ RSpec.describe Gitlab::ObjectHierarchy do ...@@ -210,6 +212,19 @@ RSpec.describe Gitlab::ObjectHierarchy do
expect(parent.self_and_descendants.to_sql).to include("DISTINCT") expect(parent.self_and_descendants.to_sql).to include("DISTINCT")
expect(child2.self_and_ancestors.to_sql).to include("DISTINCT") expect(child2.self_and_ancestors.to_sql).to include("DISTINCT")
end end
context 'when the skip_ordering option is set' do
let(:options) { { skip_ordering: true } }
it_behaves_like 'Gitlab::ObjectHierarchy test cases'
it 'does not include ROW_NUMBER()' do
query = described_class.new(Group.where(id: parent.id), options: options).base_and_descendants.to_sql
expect(query).to include("DISTINCT")
expect(query).not_to include("ROW_NUMBER()")
end
end
end end
context 'when the use_distinct_in_object_hierarchy feature flag is disabled' do context 'when the use_distinct_in_object_hierarchy feature flag is disabled' do
......
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