diff --git a/app/models/ci/build.rb b/app/models/ci/build.rb index cc0e24003d7e0b45ff428d8c930633347708d7ce..98b82681584ba9eebc3cb80fc4242ab549f1bdff 100644 --- a/app/models/ci/build.rb +++ b/app/models/ci/build.rb @@ -143,6 +143,13 @@ class Build < Ci::Processable scope :eager_load_job_artifacts, -> { includes(:job_artifacts) } scope :eager_load_tags, -> { includes(:tags) } scope :eager_load_for_archiving_trace, -> { preload(:project, :pending_state) } + scope :eager_load_for_api, -> do + preload( + :job_artifacts_archive, :job_artifacts, :runner, :tags, :runner_manager, :metadata, + pipeline: :project, + user: [:user_preference, :user_detail, :followees] + ) + end scope :eager_load_everything, -> do includes( diff --git a/lib/api/ci/jobs.rb b/lib/api/ci/jobs.rb index 4794c8d721859d0f89986f0d4c753a7769c9b21a..656ee8d29096c563b70225dc430dd3681e204d07 100644 --- a/lib/api/ci/jobs.rb +++ b/lib/api/ci/jobs.rb @@ -55,12 +55,7 @@ class Jobs < ::API::Base builds = user_project.builds.order(id: :desc) builds = filter_builds(builds, params[:scope]) - - builds = builds.preload( - :job_artifacts_archive, :job_artifacts, :runner, :tags, :runner_manager, :metadata, - pipeline: :project, - user: [:user_preference, :user_detail, :followees] - ) + builds = builds.eager_load_for_api present paginate_with_strategies(builds, user_project, paginator_params: { without_count: true }), with: Entities::Ci::Job end diff --git a/spec/models/ci/build_spec.rb b/spec/models/ci/build_spec.rb index 01de572c8290d11d122cb140a635391467ce8f0e..b0ffba5cf8c8ecdbf17e1d39ea68bc188f47eae9 100644 --- a/spec/models/ci/build_spec.rb +++ b/spec/models/ci/build_spec.rb @@ -479,6 +479,32 @@ end end + describe 'scopes for preloading' do + let_it_be(:runner) { create(:ci_runner) } + let_it_be(:user) { create(:user).tap { |user| create(:user_detail, user: user) } } + + before_all do + build = create(:ci_build, :trace_artifact, :artifacts, :test_reports, pipeline: pipeline) + build.runner = runner + build.user = user + build.save! + end + + describe '.eager_load_for_api' do + subject(:eager_load_for_api) { described_class.eager_load_for_api } + + it { expect(eager_load_for_api.last.association(:user)).to be_loaded } + it { expect(eager_load_for_api.last.user.association(:user_detail)).to be_loaded } + it { expect(eager_load_for_api.last.association(:metadata)).to be_loaded } + it { expect(eager_load_for_api.last.association(:job_artifacts_archive)).to be_loaded } + it { expect(eager_load_for_api.last.association(:job_artifacts)).to be_loaded } + it { expect(eager_load_for_api.last.association(:runner)).to be_loaded } + it { expect(eager_load_for_api.last.association(:tags)).to be_loaded } + it { expect(eager_load_for_api.last.association(:pipeline)).to be_loaded } + it { expect(eager_load_for_api.last.pipeline.association(:project)).to be_loaded } + end + end + describe '#stick_build_if_status_changed' do it 'sticks the build if the status changed' do job = create(:ci_build, :pending, pipeline: pipeline) diff --git a/spec/requests/api/ci/jobs_spec.rb b/spec/requests/api/ci/jobs_spec.rb index 31f7d8f3602324c54fae292c53549aebaed10ab3..f6b482b943ba7ad8ad4138dd61745b03023d49fd 100644 --- a/spec/requests/api/ci/jobs_spec.rb +++ b/spec/requests/api/ci/jobs_spec.rb @@ -426,16 +426,16 @@ def perform_request expect(json_job['pipeline']['status']).to eq job.pipeline.status end - it 'avoids N+1 queries', :skip_before_request do + it 'avoids N+1 queries', :skip_before_request, :use_sql_query_cache do first_build = create(:ci_build, :trace_artifact, :artifacts, :test_reports, pipeline: pipeline) first_build.runner = create(:ci_runner) first_build.user = create(:user) first_build.save! - control = ActiveRecord::QueryRecorder.new { go } + control = ActiveRecord::QueryRecorder.new(skip_cached: false) { go } 5.times do - second_pipeline = create(:ci_empty_pipeline, project: project, sha: project.commit.id, ref: project.default_branch) + second_pipeline = create(:ci_pipeline, project: project, sha: project.commit.id, ref: project.default_branch) second_build = create(:ci_build, :trace_artifact, :artifacts, :test_reports, pipeline: second_pipeline) second_build.runner = create(:ci_runner) second_build.user = create(:user)