Fix some N+1 queries in the GET /projects API

Signed-off-by: Rémy Coutable <remy@rymai.me>
This commit is contained in:
Rémy Coutable 2017-07-06 15:09:35 +02:00
parent 050eae8c4d
commit 033acc0120
No known key found for this signature in database
GPG key ID: 46DF07E5CD9E96AB
3 changed files with 57 additions and 4 deletions

View file

@ -503,12 +503,20 @@ module API
class ProjectWithAccess < Project
expose :permissions do
expose :project_access, using: Entities::ProjectAccess do |project, options|
project.project_members.find_by(user_id: options[:current_user].id)
if options.key?(:project_members)
(options[:project_members] || []).find { |member| member.source_id == project.id }
else
project.project_members.find_by(user_id: options[:current_user].id)
end
end
expose :group_access, using: Entities::GroupAccess do |project, options|
if project.group
project.group.group_members.find_by(user_id: options[:current_user].id)
if options.key?(:group_members)
(options[:group_members] || []).find { |member| member.source_id == project.namespace_id }
else
project.group.group_members.find_by(user_id: options[:current_user].id)
end
end
end
end

View file

@ -77,9 +77,17 @@ module API
projects = projects.with_issues_enabled if params[:with_issues_enabled]
projects = projects.with_merge_requests_enabled if params[:with_merge_requests_enabled]
if current_user
projects = projects.includes(:route, :taggings, namespace: :route)
project_members = current_user.project_members
group_members = current_user.group_members
end
options = options.reverse_merge(
with: current_user ? Entities::ProjectWithAccess : Entities::BasicProjectDetails,
statistics: params[:statistics],
project_members: project_members,
group_members: group_members,
current_user: current_user
)
options[:with] = Entities::BasicProjectDetails if params[:simple]

View file

@ -52,6 +52,24 @@ describe API::Projects do
end
end
shared_examples_for 'projects response without N + 1 queries' do
it 'avoids N + 1 queries' do
control_count = ActiveRecord::QueryRecorder.new do
get api('/projects', current_user)
end.count
if defined?(additional_project)
additional_project
else
create(:empty_project, :public)
end
expect do
get api('/projects', current_user)
end.not_to exceed_query_limit(control_count + 8)
end
end
let!(:public_project) { create(:empty_project, :public, name: 'public_project') }
before do
project
@ -62,9 +80,13 @@ describe API::Projects do
context 'when unauthenticated' do
it_behaves_like 'projects response' do
let(:filter) { {} }
let(:filter) { { search: project.name } }
let(:current_user) { user }
let(:projects) { [project] }
end
it_behaves_like 'projects response without N + 1 queries' do
let(:current_user) { nil }
let(:projects) { [public_project] }
end
end
@ -75,6 +97,21 @@ describe API::Projects do
let(:projects) { [public_project, project, project2, project3] }
end
it_behaves_like 'projects response without N + 1 queries' do
let(:current_user) { user }
end
context 'when some projects are in a group' do
before do
create(:empty_project, :public, group: create(:group))
end
it_behaves_like 'projects response without N + 1 queries' do
let(:current_user) { user }
let(:additional_project) { create(:empty_project, :public, group: create(:group)) }
end
end
it 'includes the project labels as the tag_list' do
get api('/projects', user)