From 7856804b9541598b6bea910a2200622a7482b1cd Mon Sep 17 00:00:00 2001 From: Igor Drozdov <idrozdov@gitlab.com> Date: Thu, 25 Mar 2021 14:29:39 +0300 Subject: [PATCH] Resolve N + 1 for JIRA pulls When API request performed for multiple pulls the number of SQL requests depends on the number of MRs --- app/models/merge_request.rb | 1 + .../unreleased/id-n-1-for-jira-pulls.yml | 5 ++ lib/api/v3/github.rb | 7 ++- spec/requests/api/v3/github_spec.rb | 50 +++++++++++++++---- 4 files changed, 50 insertions(+), 13 deletions(-) create mode 100644 changelogs/unreleased/id-n-1-for-jira-pulls.yml diff --git a/app/models/merge_request.rb b/app/models/merge_request.rb index 834d28f1e3c7d..133c6217bc089 100644 --- a/app/models/merge_request.rb +++ b/app/models/merge_request.rb @@ -316,6 +316,7 @@ def public_merge_status } scope :with_csv_entity_associations, -> { preload(:assignees, :approved_by_users, :author, :milestone, metrics: [:merged_by]) } + scope :with_jira_integration_associations, -> { preload(:metrics, :assignees, :author, :target_project, :source_project) } scope :by_target_branch_wildcard, ->(wildcard_branch_name) do where("target_branch LIKE ?", ApplicationRecord.sanitize_sql_like(wildcard_branch_name).tr('*', '%')) diff --git a/changelogs/unreleased/id-n-1-for-jira-pulls.yml b/changelogs/unreleased/id-n-1-for-jira-pulls.yml new file mode 100644 index 0000000000000..e6d37b54c49f2 --- /dev/null +++ b/changelogs/unreleased/id-n-1-for-jira-pulls.yml @@ -0,0 +1,5 @@ +--- +title: Resolve N + 1 for JIRA pulls +merge_request: 57482 +author: +type: performance diff --git a/lib/api/v3/github.rb b/lib/api/v3/github.rb index 3a0609651f4f0..7938339e1b15b 100644 --- a/lib/api/v3/github.rb +++ b/lib/api/v3/github.rb @@ -75,11 +75,14 @@ def find_merge_request_with_access(id, access_level = :read_merge_request) # rubocop: enable CodeReuse/ActiveRecord def authorized_merge_requests - MergeRequestsFinder.new(current_user, authorized_only: !current_user.admin?).execute + MergeRequestsFinder.new(current_user, authorized_only: !current_user.admin?) + .execute.with_jira_integration_associations end def authorized_merge_requests_for_project(project) - MergeRequestsFinder.new(current_user, authorized_only: !current_user.admin?, project_id: project.id).execute + MergeRequestsFinder + .new(current_user, authorized_only: !current_user.admin?, project_id: project.id) + .execute.with_jira_integration_associations end # rubocop: disable CodeReuse/ActiveRecord diff --git a/spec/requests/api/v3/github_spec.rb b/spec/requests/api/v3/github_spec.rb index 197c6cbb0ebe6..521187f0a8189 100644 --- a/spec/requests/api/v3/github_spec.rb +++ b/spec/requests/api/v3/github_spec.rb @@ -3,10 +3,10 @@ require 'spec_helper' RSpec.describe API::V3::Github do - let(:user) { create(:user) } - let(:unauthorized_user) { create(:user) } - let(:admin) { create(:user, :admin) } - let(:project) { create(:project, :repository, creator: user) } + let_it_be(:user) { create(:user) } + let_it_be(:unauthorized_user) { create(:user) } + let_it_be(:admin) { create(:user, :admin) } + let_it_be(:project) { create(:project, :repository, creator: user) } before do project.add_maintainer(user) @@ -210,14 +210,14 @@ end describe 'repo pulls' do - let(:project2) { create(:project, :repository, creator: user) } - let(:assignee) { create(:user) } - let(:assignee2) { create(:user) } - let!(:merge_request) do + let_it_be(:project2) { create(:project, :repository, creator: user) } + let_it_be(:assignee) { create(:user) } + let_it_be(:assignee2) { create(:user) } + let_it_be(:merge_request) do create(:merge_request, source_project: project, target_project: project, author: user, assignees: [assignee]) end - let!(:merge_request_2) do + let_it_be(:merge_request_2) do create(:merge_request, source_project: project2, target_project: project2, author: user, assignees: [assignee, assignee2]) end @@ -225,26 +225,54 @@ project2.add_maintainer(user) end + def perform_request + jira_get v3_api(route, user) + end + describe 'GET /-/jira/pulls' do + let(:route) { '/repos/-/jira/pulls' } + it 'returns an array of merge requests with github format' do - jira_get v3_api('/repos/-/jira/pulls', user) + perform_request expect(response).to have_gitlab_http_status(:ok) expect(json_response).to be_an(Array) expect(json_response.size).to eq(2) expect(response).to match_response_schema('entities/github/pull_requests') end + + it 'returns multiple merge requests without N + 1' do + perform_request + + control_count = ActiveRecord::QueryRecorder.new { perform_request }.count + + create(:merge_request, source_project: project, source_branch: 'fix') + + expect { perform_request }.not_to exceed_query_limit(control_count) + end end describe 'GET /repos/:namespace/:project/pulls' do + let(:route) { "/repos/#{project.namespace.path}/#{project.path}/pulls" } + it 'returns an array of merge requests for the proper project in github format' do - jira_get v3_api("/repos/#{project.namespace.path}/#{project.path}/pulls", user) + perform_request expect(response).to have_gitlab_http_status(:ok) expect(json_response).to be_an(Array) expect(json_response.size).to eq(1) expect(response).to match_response_schema('entities/github/pull_requests') end + + it 'returns multiple merge requests without N + 1' do + perform_request + + control_count = ActiveRecord::QueryRecorder.new { perform_request }.count + + create(:merge_request, source_project: project, source_branch: 'fix') + + expect { perform_request }.not_to exceed_query_limit(control_count) + end end describe 'GET /repos/:namespace/:project/pulls/:id' do -- GitLab