Commit 5eb29e23 authored by Bob Van Landuyt's avatar Bob Van Landuyt

Merge branch 'osw-remove-n-plus-ones-from-branches-api-call' into 'master'

Remove N+1 DB calls from branches API

See merge request gitlab-org/gitlab!19661
parents b173b6ae 12090792
...@@ -39,8 +39,8 @@ module ProtectedRef ...@@ -39,8 +39,8 @@ module ProtectedRef
end end
end end
def developers_can?(action, ref) def developers_can?(action, ref, protected_refs: nil)
access_levels_for_ref(ref, action: action).any? do |access_level| access_levels_for_ref(ref, action: action, protected_refs: protected_refs).any? do |access_level|
access_level.access_level == Gitlab::Access::DEVELOPER access_level.access_level == Gitlab::Access::DEVELOPER
end end
end end
......
...@@ -663,6 +663,11 @@ class Project < ApplicationRecord ...@@ -663,6 +663,11 @@ class Project < ApplicationRecord
end end
end end
def preload_protected_branches
preloader = ActiveRecord::Associations::Preloader.new
preloader.preload(self, protected_branches: [:push_access_levels, :merge_access_levels])
end
# returns all ancestor-groups upto but excluding the given namespace # returns all ancestor-groups upto but excluding the given namespace
# when no namespace is given, all ancestors upto the top are returned # when no namespace is given, all ancestors upto the top are returned
def ancestors_upto(top = nil, hierarchy_order: nil) def ancestors_upto(top = nil, hierarchy_order: nil)
......
...@@ -38,7 +38,7 @@ class ProtectedBranch < ApplicationRecord ...@@ -38,7 +38,7 @@ class ProtectedBranch < ApplicationRecord
end end
def self.protected_refs(project) def self.protected_refs(project)
project.protected_branches.select(:name) project.protected_branches
end end
def self.branch_requires_code_owner_approval?(project, branch_name) def self.branch_requires_code_owner_approval?(project, branch_name)
......
---
title: Remove N+1 DB calls from branches API
merge_request: 19661
author:
type: performance
...@@ -32,7 +32,7 @@ module API ...@@ -32,7 +32,7 @@ module API
use :filter_params use :filter_params
end end
get ':id/repository/branches' do get ':id/repository/branches' do
Gitlab::QueryLimiting.whitelist('https://gitlab.com/gitlab-org/gitlab-foss/issues/42329') user_project.preload_protected_branches
repository = user_project.repository repository = user_project.repository
......
...@@ -489,11 +489,11 @@ module API ...@@ -489,11 +489,11 @@ module API
end end
expose :developers_can_push do |repo_branch, options| expose :developers_can_push do |repo_branch, options|
options[:project].protected_branches.developers_can?(:push, repo_branch.name) ::ProtectedBranch.developers_can?(:push, repo_branch.name, protected_refs: options[:project].protected_branches)
end end
expose :developers_can_merge do |repo_branch, options| expose :developers_can_merge do |repo_branch, options|
options[:project].protected_branches.developers_can?(:merge, repo_branch.name) ::ProtectedBranch.developers_can?(:merge, repo_branch.name, protected_refs: options[:project].protected_branches)
end end
expose :can_push do |repo_branch, options| expose :can_push do |repo_branch, options|
......
...@@ -119,6 +119,25 @@ describe API::Branches do ...@@ -119,6 +119,25 @@ describe API::Branches do
it_behaves_like 'repository branches' it_behaves_like 'repository branches'
end end
it 'does not submit N+1 DB queries', :request_store do
create(:protected_branch, name: 'master', project: project)
# Make sure no setup step query is recorded.
get api(route, current_user), params: { per_page: 100 }
control = ActiveRecord::QueryRecorder.new do
get api(route, current_user), params: { per_page: 100 }
end
new_branch_name = 'protected-branch'
CreateBranchService.new(project, current_user).execute(new_branch_name, 'master')
create(:protected_branch, name: new_branch_name, project: project)
expect do
get api(route, current_user), params: { per_page: 100 }
end.not_to exceed_query_limit(control)
end
end end
context 'when authenticated', 'as a guest' do context 'when authenticated', 'as a guest' do
......
...@@ -180,7 +180,7 @@ describe PipelineSerializer do ...@@ -180,7 +180,7 @@ describe PipelineSerializer do
# pipeline. With the same ref this check is cached but if refs are # pipeline. With the same ref this check is cached but if refs are
# different then there is an extra query per ref # different then there is an extra query per ref
# https://gitlab.com/gitlab-org/gitlab-foss/issues/46368 # https://gitlab.com/gitlab-org/gitlab-foss/issues/46368
expected_queries = Gitlab.ee? ? 44 : 41 expected_queries = Gitlab.ee? ? 41 : 38
expect(recorded.count).to be_within(2).of(expected_queries) expect(recorded.count).to be_within(2).of(expected_queries)
expect(recorded.cached_count).to eq(0) expect(recorded.cached_count).to eq(0)
......
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