Commit f6dc8369 authored by Jan Provaznik's avatar Jan Provaznik

Merge branch 'optimize-milestones-page' into 'master'

Use UNION instead of OR for milestone queries

See merge request gitlab-org/gitlab!32953
parents 92864ce5 29648c60
...@@ -12,7 +12,7 @@ class Groups::MilestonesController < Groups::ApplicationController ...@@ -12,7 +12,7 @@ class Groups::MilestonesController < Groups::ApplicationController
def index def index
respond_to do |format| respond_to do |format|
format.html do format.html do
@milestone_states = Milestone.states_count(group_projects_with_access, [group]) @milestone_states = Milestone.states_count(group_projects_with_access.without_order, [group])
@milestones = milestones.page(params[:page]) @milestones = milestones.page(params[:page])
end end
format.json do format.json do
......
...@@ -5,6 +5,10 @@ class ApplicationRecord < ActiveRecord::Base ...@@ -5,6 +5,10 @@ class ApplicationRecord < ActiveRecord::Base
alias_method :reset, :reload alias_method :reset, :reload
def self.without_order
reorder(nil)
end
def self.id_in(ids) def self.id_in(ids)
where(id: ids) where(id: ids)
end end
......
...@@ -9,6 +9,7 @@ module Timebox ...@@ -9,6 +9,7 @@ module Timebox
include IidRoutes include IidRoutes
include Referable include Referable
include StripAttribute include StripAttribute
include FromUnion
TimeboxStruct = Struct.new(:title, :name, :id) do TimeboxStruct = Struct.new(:title, :name, :id) do
# Ensure these models match the interface required for exporting # Ensure these models match the interface required for exporting
...@@ -65,7 +66,11 @@ module Timebox ...@@ -65,7 +66,11 @@ module Timebox
groups = groups.compact if groups.is_a? Array groups = groups.compact if groups.is_a? Array
groups = [] if groups.nil? groups = [] if groups.nil?
where(project_id: projects).or(where(group_id: groups)) if Feature.enabled?(:optimized_timebox_queries)
from_union([where(project_id: projects), where(group_id: groups)], remove_duplicates: false)
else
where(project_id: projects).or(where(group_id: groups))
end
end end
scope :within_timeframe, -> (start_date, end_date) do scope :within_timeframe, -> (start_date, end_date) do
......
---
title: Optimize SQL queries on Milestone index page
merge_request: 32953
author:
type: performance
...@@ -225,70 +225,88 @@ describe Milestone do ...@@ -225,70 +225,88 @@ describe Milestone do
end end
end end
describe '#for_projects_and_groups' do shared_examples '#for_projects_and_groups' do
let(:project) { create(:project) } describe '#for_projects_and_groups' do
let(:project_other) { create(:project) } let_it_be(:project) { create(:project) }
let(:group) { create(:group) } let_it_be(:project_other) { create(:project) }
let(:group_other) { create(:group) } let_it_be(:group) { create(:group) }
let_it_be(:group_other) { create(:group) }
before(:all) do
create(:milestone, project: project)
create(:milestone, project: project_other)
create(:milestone, group: group)
create(:milestone, group: group_other)
end
before do subject { described_class.for_projects_and_groups(projects, groups) }
create(:milestone, project: project)
create(:milestone, project: project_other) shared_examples 'filters by projects and groups' do
create(:milestone, group: group) it 'returns milestones filtered by project' do
create(:milestone, group: group_other) milestones = described_class.for_projects_and_groups(projects, [])
end
expect(milestones.count).to eq(1)
expect(milestones.first.project_id).to eq(project.id)
end
it 'returns milestones filtered by group' do
milestones = described_class.for_projects_and_groups([], groups)
subject { described_class.for_projects_and_groups(projects, groups) } expect(milestones.count).to eq(1)
expect(milestones.first.group_id).to eq(group.id)
end
shared_examples 'filters by projects and groups' do it 'returns milestones filtered by both project and group' do
it 'returns milestones filtered by project' do milestones = described_class.for_projects_and_groups(projects, groups)
milestones = described_class.for_projects_and_groups(projects, [])
expect(milestones.count).to eq(1) expect(milestones.count).to eq(2)
expect(milestones.first.project_id).to eq(project.id) expect(milestones).to contain_exactly(project.milestones.first, group.milestones.first)
end
end end
it 'returns milestones filtered by group' do context 'ids as params' do
milestones = described_class.for_projects_and_groups([], groups) let(:projects) { [project.id] }
let(:groups) { [group.id] }
expect(milestones.count).to eq(1) it_behaves_like 'filters by projects and groups'
expect(milestones.first.group_id).to eq(group.id)
end end
it 'returns milestones filtered by both project and group' do context 'relations as params' do
milestones = described_class.for_projects_and_groups(projects, groups) let(:projects) { Project.where(id: project.id).select(:id) }
let(:groups) { Group.where(id: group.id).select(:id) }
expect(milestones.count).to eq(2) it_behaves_like 'filters by projects and groups'
expect(milestones).to contain_exactly(project.milestones.first, group.milestones.first)
end end
end
context 'ids as params' do context 'objects as params' do
let(:projects) { [project.id] } let(:projects) { [project] }
let(:groups) { [group.id] } let(:groups) { [group] }
it_behaves_like 'filters by projects and groups' it_behaves_like 'filters by projects and groups'
end end
context 'relations as params' do it 'returns no records if projects and groups are nil' do
let(:projects) { Project.where(id: project.id).select(:id) } milestones = described_class.for_projects_and_groups(nil, nil)
let(:groups) { Group.where(id: group.id).select(:id) }
it_behaves_like 'filters by projects and groups' expect(milestones).to be_empty
end
end end
end
context 'objects as params' do context 'when `optimized_timebox_queries` feature flag is enabled' do
let(:projects) { [project] } before do
let(:groups) { [group] } stub_feature_flags(optimized_timebox_queries: true)
it_behaves_like 'filters by projects and groups'
end end
it 'returns no records if projects and groups are nil' do it_behaves_like '#for_projects_and_groups'
milestones = described_class.for_projects_and_groups(nil, nil) end
expect(milestones).to be_empty context 'when `optimized_timebox_queries` feature flag is disabled' do
before do
stub_feature_flags(optimized_timebox_queries: false)
end end
it_behaves_like '#for_projects_and_groups'
end end
describe '.upcoming_ids' do describe '.upcoming_ids' 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