Commit 49139f00 authored by Yorick Peterse's avatar Yorick Peterse

Merge branch 'fix/slow-todos' into 'master'

Speed up todos queries by limiting the projects set we join with

See merge request !5791
parents a632ed69 be870760
...@@ -110,6 +110,7 @@ v 8.11.0 (unreleased) ...@@ -110,6 +110,7 @@ v 8.11.0 (unreleased)
- Each `File::exists?` replaced to `File::exist?` because of deprecate since ruby version 2.2.0 - Each `File::exists?` replaced to `File::exist?` because of deprecate since ruby version 2.2.0
- Add auto-completition in pipeline (Katarzyna Kobierska Ula Budziszewska) - Add auto-completition in pipeline (Katarzyna Kobierska Ula Budziszewska)
- Fix a memory leak caused by Banzai::Filter::SanitizationFilter - Fix a memory leak caused by Banzai::Filter::SanitizationFilter
- Speed up todos queries by limiting the projects set we join with
v 8.10.5 v 8.10.5
- Add a data migration to fix some missing timestamps in the members table. !5670 - Add a data migration to fix some missing timestamps in the members table. !5670
......
class ProjectsFinder < UnionFinder class ProjectsFinder < UnionFinder
def execute(current_user = nil, options = {}) def execute(current_user = nil, project_ids_relation = nil)
segments = all_projects(current_user) segments = all_projects(current_user)
segments.map! { |s| s.where(id: project_ids_relation) } if project_ids_relation
find_union(segments, Project) find_union(segments, Project)
end end
......
...@@ -27,9 +27,11 @@ class TodosFinder ...@@ -27,9 +27,11 @@ class TodosFinder
items = by_action_id(items) items = by_action_id(items)
items = by_action(items) items = by_action(items)
items = by_author(items) items = by_author(items)
items = by_project(items)
items = by_state(items) items = by_state(items)
items = by_type(items) items = by_type(items)
# Filtering by project HAS TO be the last because we use
# the project IDs yielded by the todos query thus far
items = by_project(items)
items.reorder(id: :desc) items.reorder(id: :desc)
end end
...@@ -91,14 +93,9 @@ class TodosFinder ...@@ -91,14 +93,9 @@ class TodosFinder
@project @project
end end
def projects def projects(items)
return @projects if defined?(@projects) item_project_ids = items.reorder(nil).select(:project_id)
ProjectsFinder.new.execute(current_user, item_project_ids)
if project?
@projects = project
else
@projects = ProjectsFinder.new.execute(current_user)
end
end end
def type? def type?
...@@ -136,8 +133,9 @@ class TodosFinder ...@@ -136,8 +133,9 @@ class TodosFinder
def by_project(items) def by_project(items)
if project? if project?
items = items.where(project: project) items = items.where(project: project)
elsif projects else
items = items.merge(projects).joins(:project) item_projects = projects(items)
items = items.merge(item_projects).joins(:project)
end end
items items
......
...@@ -23,73 +23,36 @@ describe ProjectsFinder do ...@@ -23,73 +23,36 @@ describe ProjectsFinder do
let(:finder) { described_class.new } let(:finder) { described_class.new }
describe 'without a group' do describe 'without a user' do
describe 'without a user' do subject { finder.execute }
subject { finder.execute }
it { is_expected.to eq([public_project]) } it { is_expected.to eq([public_project]) }
end
describe 'with a user' do
subject { finder.execute(user) }
describe 'without private projects' do
it { is_expected.to eq([public_project, internal_project]) }
end
describe 'with private projects' do
before do
private_project.team.add_user(user, Gitlab::Access::MASTER)
end
it do
is_expected.to eq([public_project, internal_project,
private_project])
end
end
end
end end
describe 'with a group' do describe 'with a user' do
describe 'without a user' do subject { finder.execute(user) }
subject { finder.execute(nil, group: group) }
it { is_expected.to eq([public_project]) } describe 'without private projects' do
it { is_expected.to eq([public_project, internal_project]) }
end end
describe 'with a user' do describe 'with private projects' do
subject { finder.execute(user, group: group) } before do
private_project.team.add_user(user, Gitlab::Access::MASTER)
describe 'without shared projects' do
it { is_expected.to eq([public_project, internal_project]) }
end end
describe 'with shared projects and group membership' do it do
before do is_expected.to eq([public_project, internal_project, private_project])
group.add_user(user, Gitlab::Access::DEVELOPER)
shared_project.project_group_links.
create(group_access: Gitlab::Access::MASTER, group: group)
end
it do
is_expected.to eq([shared_project, public_project, internal_project])
end
end end
end
end
describe 'with shared projects and project membership' do describe 'with project_ids_relation' do
before do let(:project_ids_relation) { Project.where(id: internal_project.id) }
shared_project.team.add_user(user, Gitlab::Access::DEVELOPER)
shared_project.project_group_links. subject { finder.execute(user, project_ids_relation) }
create(group_access: Gitlab::Access::MASTER, group: group)
end
it do it { is_expected.to eq([internal_project]) }
is_expected.to eq([shared_project, public_project, internal_project])
end
end
end
end end
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