Commit c624b1e0 authored by David Kim's avatar David Kim

Merge branch '21121-fj-fix-n+1-projects-endpoint-forked-projects' into 'master'

Fix N+1 in projects REST endpoint and forked projects

See merge request gitlab-org/gitlab!57798
parents 4223146f 8d7bcb8f
---
title: Fix N+1 in projects REST endpoint with forked projects
merge_request: 57798
author:
type: performance
......@@ -8,11 +8,10 @@ module API
expose :default_branch, if: -> (project, options) { Ability.allowed?(options[:current_user], :download_code, project) }
# Avoids an N+1 query: https://github.com/mbleigh/acts-as-taggable-on/issues/91#issuecomment-168273770
expose :tag_list do |project|
# project.tags.order(:name).pluck(:name) is the most suitable option
# to avoid loading all the ActiveRecord objects but, if we use it here
# it override the preloaded associations and makes a query
# (fixed in https://github.com/rails/rails/pull/25976).
project.tags.map(&:name).sort
# Tags is a preloaded association. If we perform then sorting
# through the database, it will trigger a new query, ending up
# in an N+1 if we have several projects
project.tags.pluck(:name).sort # rubocop:disable CodeReuse/ActiveRecord
end
expose :ssh_url_to_repo, :http_url_to_repo, :web_url, :readme_url
......
......@@ -135,7 +135,7 @@ module API
.preload(project_group_links: { group: :route },
fork_network: :root_project,
fork_network_member: :forked_from_project,
forked_from_project: [:route, :forks, :tags, namespace: :route])
forked_from_project: [:route, :forks, :tags, :group, :project_feature, namespace: [:route, :owner]])
end
# rubocop: enable CodeReuse/ActiveRecord
......
......@@ -810,6 +810,31 @@ RSpec.describe API::Projects do
end
end
end
context 'with forked projects', :use_clean_rails_memory_store_caching do
include ProjectForksHelper
let_it_be(:admin) { create(:admin) }
it 'avoids N+1 queries' do
get api('/projects', admin)
base_project = create(:project, :public, namespace: admin.namespace)
fork_project1 = fork_project(base_project, admin, namespace: create(:user).namespace)
fork_project2 = fork_project(fork_project1, admin, namespace: create(:user).namespace)
control = ActiveRecord::QueryRecorder.new do
get api('/projects', admin)
end
fork_project(fork_project2, admin, namespace: create(:user).namespace)
expect do
get api('/projects', admin)
end.not_to exceed_query_limit(control.count)
end
end
end
describe 'POST /projects' 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