Skip to content
Snippets Groups Projects
Commit 1138a8a7 authored by Furkan Ayhan's avatar Furkan Ayhan
Browse files

Eliminate N+1 queries for pipeline GraphQL endpoint

The 'metadata' relation is the most common N+1 query.
The 'downstream_pipeline' relation is for bridge jobs, not so common.

In this commit, the structure of preloading is also changed.

Changelog: performance
parent 10932045
No related branches found
No related tags found
No related merge requests found
Loading
Loading
@@ -52,13 +52,20 @@ def groups(lookahead:)
 
# rubocop: disable CodeReuse/ActiveRecord
def jobs_for_pipeline(pipeline, stage_ids, include_needs)
builds_results = pipeline.latest_builds.where(stage_id: stage_ids).preload(:job_artifacts, :project)
bridges_results = pipeline.bridges.where(stage_id: stage_ids).preload(:project)
builds_results = builds_results.preload(:needs) if include_needs
bridges_results = bridges_results.preload(:needs) if include_needs
commit_status_results = pipeline.latest_statuses.where(stage_id: stage_ids)
results = pipeline.statuses.latest
 
results = builds_results | bridges_results | commit_status_results
common_relations = [:project]
common_relations << :needs if include_needs
preloaders = {
::Ci::Build => [:metadata, :job_artifacts],
::Ci::Bridge => [:metadata, :downstream_pipeline],
::GenericCommitStatus => []
}
preloaders.each do |klass, relations|
ActiveRecord::Associations::Preloader.new.preload(results.select { |job| job.is_a?(klass) }, relations + common_relations)
end
 
results.group_by(&:stage_id)
end
Loading
Loading
Loading
Loading
@@ -66,6 +66,7 @@ class Pipeline < Ci::ApplicationRecord
has_many :processables, class_name: 'Ci::Processable', foreign_key: :commit_id, inverse_of: :pipeline
has_many :bridges, class_name: 'Ci::Bridge', foreign_key: :commit_id, inverse_of: :pipeline
has_many :builds, foreign_key: :commit_id, inverse_of: :pipeline
has_many :generic_commit_statuses, foreign_key: :commit_id, inverse_of: :pipeline, class_name: 'GenericCommitStatus'
has_many :job_artifacts, through: :builds
has_many :trigger_requests, dependent: :destroy, foreign_key: :commit_id # rubocop:disable Cop/ActiveRecordDependent
has_many :variables, class_name: 'Ci::PipelineVariable'
Loading
Loading
Loading
Loading
@@ -311,6 +311,10 @@ def successful_pipeline
end
 
it 'does not generate N+1 queries', :request_store, :use_sql_query_cache do
# create extra statuses
create(:generic_commit_status, :pending, name: 'generic-build-a', pipeline: pipeline, stage_idx: 0, stage: 'build')
create(:ci_bridge, :failed, name: 'deploy-a', pipeline: pipeline, stage_idx: 2, stage: 'deploy')
# warm up
post_graphql(query, current_user: current_user)
 
Loading
Loading
@@ -318,9 +322,11 @@ def successful_pipeline
post_graphql(query, current_user: current_user)
end
 
create(:ci_build, name: 'test-a', pipeline: pipeline, stage_idx: 1, stage: 'test')
create(:ci_build, name: 'test-b', pipeline: pipeline, stage_idx: 1, stage: 'test')
create(:ci_build, name: 'deploy-a', pipeline: pipeline, stage_idx: 2, stage: 'deploy')
create(:generic_commit_status, :pending, name: 'generic-build-b', pipeline: pipeline, stage_idx: 0, stage: 'build')
create(:ci_build, :failed, name: 'test-a', pipeline: pipeline, stage_idx: 1, stage: 'test')
create(:ci_build, :running, name: 'test-b', pipeline: pipeline, stage_idx: 1, stage: 'test')
create(:ci_build, :pending, name: 'deploy-b', pipeline: pipeline, stage_idx: 2, stage: 'deploy')
create(:ci_bridge, :failed, name: 'deploy-c', pipeline: pipeline, stage_idx: 2, stage: 'deploy')
 
expect do
post_graphql(query, current_user: current_user)
Loading
Loading
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment