n+1 queries in Package Pipelines endpoint

Creating this as a separate issue. This was discovered during review of !117539 (merged) (thread).

When loading ::Ci::Pipelines there is an n+1 query situation.

n+1
Project Load (0.2ms)  SELECT "projects".* FROM "projects" WHERE "projects"."id" = 135 LIMIT 1 /*application:web,correlation_id:01H2883YSV4HKKTY52XH36JDEW,endpoint_id:GET /api/:version/projects/:id/packages/:package_id/pipelines,db_config_name:main,line:/lib/api/entities/ci/pipeline_basic.rb:22:in `block in <class:PipelineBasic>'*/
  ↳ lib/api/entities/ci/pipeline_basic.rb:22:in `block in <class:PipelineBasic>'
  Namespace Load (0.1ms)  SELECT "namespaces".* FROM "namespaces" WHERE "namespaces"."id" = 295 LIMIT 1 /*application:web,correlation_id:01H2883YSV4HKKTY52XH36JDEW,endpoint_id:GET /api/:version/projects/:id/packages/:package_id/pipelines,db_config_name:main,line:/config/routes.rb:319:in `block (4 levels) in <main>'*/
  ↳ config/routes.rb:319:in `block (4 levels) in <main>'
  Route Load (0.1ms)  SELECT "routes".* FROM "routes" WHERE "routes"."source_id" = 295 AND "routes"."source_type" = 'Namespace' LIMIT 1 /*application:web,correlation_id:01H2883YSV4HKKTY52XH36JDEW,endpoint_id:GET /api/:version/projects/:id/packages/:package_id/pipelines,db_config_name:main,line:/app/models/concerns/routable.rb:141:in `block in full_attribute'*/
  ↳ app/models/concerns/routable.rb:141:in `block in full_attribute'
  Project Load (0.2ms)  SELECT "projects".* FROM "projects" WHERE "projects"."id" = 135 LIMIT 1 /*application:web,correlation_id:01H2883YSV4HKKTY52XH36JDEW,endpoint_id:GET /api/:version/projects/:id/packages/:package_id/pipelines,db_config_name:main,line:/lib/api/entities/ci/pipeline_basic.rb:22:in `block in <class:PipelineBasic>'*/
  ↳ lib/api/entities/ci/pipeline_basic.rb:22:in `block in <class:PipelineBasic>'
  Namespace Load (0.1ms)  SELECT "namespaces".* FROM "namespaces" WHERE "namespaces"."id" = 295 LIMIT 1 /*application:web,correlation_id:01H2883YSV4HKKTY52XH36JDEW,endpoint_id:GET /api/:version/projects/:id/packages/:package_id/pipelines,db_config_name:main,line:/config/routes.rb:319:in `block (4 levels) in <main>'*/
  ↳ config/routes.rb:319:in `block (4 levels) in <main>'
  Route Load (0.1ms)  SELECT "routes".* FROM "routes" WHERE "routes"."source_id" = 295 AND "routes"."source_type" = 'Namespace' LIMIT 1 /*application:web,correlation_id:01H2883YSV4HKKTY52XH36JDEW,endpoint_id:GET /api/:version/projects/:id/packages/:package_id/pipelines,db_config_name:main,line:/app/models/concerns/routable.rb:141:in `block in full_attribute'*/
  ↳ app/models/concerns/routable.rb:141:in `block in full_attribute'
  Project Load (0.2ms)  SELECT "projects".* FROM "projects" WHERE "projects"."id" = 135 LIMIT 1 /*application:web,correlation_id:01H2883YSV4HKKTY52XH36JDEW,endpoint_id:GET /api/:version/projects/:id/packages/:package_id/pipelines,db_config_name:main,line:/lib/api/entities/ci/pipeline_basic.rb:22:in `block in <class:PipelineBasic>'*/
  ↳ lib/api/entities/ci/pipeline_basic.rb:22:in `block in <class:PipelineBasic>'
  Namespace Load (0.1ms)  SELECT "namespaces".* FROM "namespaces" WHERE "namespaces"."id" = 295 LIMIT 1 /*application:web,correlation_id:01H2883YSV4HKKTY52XH36JDEW,endpoint_id:GET /api/:version/projects/:id/packages/:package_id/pipelines,db_config_name:main,line:/config/routes.rb:319:in `block (4 levels) in <main>'*/
  ↳ config/routes.rb:319:in `block (4 levels) in <main>'
  Route Load (0.1ms)  SELECT "routes".* FROM "routes" WHERE "routes"."source_id" = 295 AND "routes"."source_type" = 'Namespace' LIMIT 1 /*application:web,correlation_id:01H2883YSV4HKKTY52XH36JDEW,endpoint_id:GET /api/:version/projects/:id/packages/:package_id/pipelines,db_config_name:main,line:/app/models/concerns/routable.rb:141:in `block in full_attribute'*/
  ↳ app/models/concerns/routable.rb:141:in `block in full_attribute'
  Project Load (0.2ms)  SELECT "projects".* FROM "projects" WHERE "projects"."id" = 135 LIMIT 1 /*application:web,correlation_id:01H2883YSV4HKKTY52XH36JDEW,endpoint_id:GET /api/:version/projects/:id/packages/:package_id/pipelines,db_config_name:main,line:/lib/api/entities/ci/pipeline_basic.rb:22:in `block in <class:PipelineBasic>'*/
  ↳ lib/api/entities/ci/pipeline_basic.rb:22:in `block in <class:PipelineBasic>'
  Namespace Load (0.1ms)  SELECT "namespaces".* FROM "namespaces" WHERE "namespaces"."id" = 295 LIMIT 1 /*application:web,correlation_id:01H2883YSV4HKKTY52XH36JDEW,endpoint_id:GET /api/:version/projects/:id/packages/:package_id/pipelines,db_config_name:main,line:/config/routes.rb:319:in `block (4 levels) in <main>'*/
  ↳ config/routes.rb:319:in `block (4 levels) in <main>'
  Route Load (0.1ms)  SELECT "routes".* FROM "routes" WHERE "routes"."source_id" = 295 AND "routes"."source_type" = 'Namespace' LIMIT 1 /*application:web,correlation_id:01H2883YSV4HKKTY52XH36JDEW,endpoint_id:GET /api/:version/projects/:id/packages/:package_id/pipelines,db_config_name:main,line:/app/models/concerns/routable.rb:141:in `block in full_attribute'*/
  ↳ app/models/concerns/routable.rb:141:in `block in full_attribute'
  Project Load (0.2ms)  SELECT "projects".* FROM "projects" WHERE "projects"."id" = 135 LIMIT 1 /*application:web,correlation_id:01H2883YSV4HKKTY52XH36JDEW,endpoint_id:GET /api/:version/projects/:id/packages/:package_id/pipelines,db_config_name:main,line:/lib/api/entities/ci/pipeline_basic.rb:22:in `block in <class:PipelineBasic>'*/
  ↳ lib/api/entities/ci/pipeline_basic.rb:22:in `block in <class:PipelineBasic>'
  Namespace Load (0.1ms)  SELECT "namespaces".* FROM "namespaces" WHERE "namespaces"."id" = 295 LIMIT 1 /*application:web,correlation_id:01H2883YSV4HKKTY52XH36JDEW,endpoint_id:GET /api/:version/projects/:id/packages/:package_id/pipelines,db_config_name:main,line:/config/routes.rb:319:in `block (4 levels) in <main>'*/
  ↳ config/routes.rb:319:in `block (4 levels) in <main>'
  Route Load (0.1ms)  SELECT "routes".* FROM "routes" WHERE "routes"."source_id" = 295 AND "routes"."source_type" = 'Namespace' LIMIT 1 /*application:web,correlation_id:01H2883YSV4HKKTY52XH36JDEW,endpoint_id:GET /api/:version/projects/:id/packages/:package_id/pipelines,db_config_name:main,line:/app/models/concerns/routable.rb:141:in `block in full_attribute'*/
  ↳ app/models/concerns/routable.rb:141:in `block in full_attribute'
  Project Load (0.2ms)  SELECT "projects".* FROM "projects" WHERE "projects"."id" = 135 LIMIT 1 /*application:web,correlation_id:01H2883YSV4HKKTY52XH36JDEW,endpoint_id:GET /api/:version/projects/:id/packages/:package_id/pipelines,db_config_name:main,line:/lib/api/entities/ci/pipeline_basic.rb:22:in `block in <class:PipelineBasic>'*/
  ↳ lib/api/entities/ci/pipeline_basic.rb:22:in `block in <class:PipelineBasic>'
  Namespace Load (0.1ms)  SELECT "namespaces".* FROM "namespaces" WHERE "namespaces"."id" = 295 LIMIT 1 /*application:web,correlation_id:01H2883YSV4HKKTY52XH36JDEW,endpoint_id:GET /api/:version/projects/:id/packages/:package_id/pipelines,db_config_name:main,line:/config/routes.rb:319:in `block (4 levels) in <main>'*/
  ↳ config/routes.rb:319:in `block (4 levels) in <main>'
  Route Load (0.1ms)  SELECT "routes".* FROM "routes" WHERE "routes"."source_id" = 295 AND "routes"."source_type" = 'Namespace' LIMIT 1 /*application:web,correlation_id:01H2883YSV4HKKTY52XH36JDEW,endpoint_id:GET /api/:version/projects/:id/packages/:package_id/pipelines,db_config_name:main,line:/app/models/concerns/routable.rb:141:in `block in full_attribute'*/
  ↳ app/models/concerns/routable.rb:141:in `block in full_attribute'
  Project Load (0.2ms)  SELECT "projects".* FROM "projects" WHERE "projects"."id" = 135 LIMIT 1 /*application:web,correlation_id:01H2883YSV4HKKTY52XH36JDEW,endpoint_id:GET /api/:version/projects/:id/packages/:package_id/pipelines,db_config_name:main,line:/lib/api/entities/ci/pipeline_basic.rb:22:in `block in <class:PipelineBasic>'*/
  ↳ lib/api/entities/ci/pipeline_basic.rb:22:in `block in <class:PipelineBasic>'
  Namespace Load (0.1ms)  SELECT "namespaces".* FROM "namespaces" WHERE "namespaces"."id" = 295 LIMIT 1 /*application:web,correlation_id:01H2883YSV4HKKTY52XH36JDEW,endpoint_id:GET /api/:version/projects/:id/packages/:package_id/pipelines,db_config_name:main,line:/config/routes.rb:319:in `block (4 levels) in <main>'*/
  ↳ config/routes.rb:319:in `block (4 levels) in <main>'
  Route Load (0.1ms)  SELECT "routes".* FROM "routes" WHERE "routes"."source_id" = 295 AND "routes"."source_type" = 'Namespace' LIMIT 1 /*application:web,correlation_id:01H2883YSV4HKKTY52XH36JDEW,endpoint_id:GET /api/:version/projects/:id/packages/:package_id/pipelines,db_config_name:main,line:/app/models/concerns/routable.rb:141:in `block in full_attribute'*/
  ↳ app/models/concerns/routable.rb:141:in `block in full_attribute'
  Project Load (0.2ms)  SELECT "projects".* FROM "projects" WHERE "projects"."id" = 135 LIMIT 1 /*application:web,correlation_id:01H2883YSV4HKKTY52XH36JDEW,endpoint_id:GET /api/:version/projects/:id/packages/:package_id/pipelines,db_config_name:main,line:/lib/api/entities/ci/pipeline_basic.rb:22:in `block in <class:PipelineBasic>'*/
  ↳ lib/api/entities/ci/pipeline_basic.rb:22:in `block in <class:PipelineBasic>'
  Namespace Load (0.1ms)  SELECT "namespaces".* FROM "namespaces" WHERE "namespaces"."id" = 295 LIMIT 1 /*application:web,correlation_id:01H2883YSV4HKKTY52XH36JDEW,endpoint_id:GET /api/:version/projects/:id/packages/:package_id/pipelines,db_config_name:main,line:/config/routes.rb:319:in `block (4 levels) in <main>'*/
  ↳ config/routes.rb:319:in `block (4 levels) in <main>'
  Route Load (0.1ms)  SELECT "routes".* FROM "routes" WHERE "routes"."source_id" = 295 AND "routes"."source_type" = 'Namespace' LIMIT 1 /*application:web,correlation_id:01H2883YSV4HKKTY52XH36JDEW,endpoint_id:GET /api/:version/projects/:id/packages/:package_id/pipelines,db_config_name:main,line:/app/models/concerns/routable.rb:141:in `block in full_attribute'*/
  ↳ app/models/concerns/routable.rb:141:in `block in full_attribute'
  Project Load (0.2ms)  SELECT "projects".* FROM "projects" WHERE "projects"."id" = 135 LIMIT 1 /*application:web,correlation_id:01H2883YSV4HKKTY52XH36JDEW,endpoint_id:GET /api/:version/projects/:id/packages/:package_id/pipelines,db_config_name:main,line:/lib/api/entities/ci/pipeline_basic.rb:22:in `block in <class:PipelineBasic>'*/
  ↳ lib/api/entities/ci/pipeline_basic.rb:22:in `block in <class:PipelineBasic>'
  Namespace Load (0.1ms)  SELECT "namespaces".* FROM "namespaces" WHERE "namespaces"."id" = 295 LIMIT 1 /*application:web,correlation_id:01H2883YSV4HKKTY52XH36JDEW,endpoint_id:GET /api/:version/projects/:id/packages/:package_id/pipelines,db_config_name:main,line:/config/routes.rb:319:in `block (4 levels) in <main>'*/
  ↳ config/routes.rb:319:in `block (4 levels) in <main>'
  Route Load (0.1ms)  SELECT "routes".* FROM "routes" WHERE "routes"."source_id" = 295 AND "routes"."source_type" = 'Namespace' LIMIT 1 /*application:web,correlation_id:01H2883YSV4HKKTY52XH36JDEW,endpoint_id:GET /api/:version/projects/:id/packages/:package_id/pipelines,db_config_name:main,line:/app/models/concerns/routable.rb:141:in `block in full_attribute'*/
  ↳ app/models/concerns/routable.rb:141:in `block in full_attribute'
  Project Load (0.2ms)  SELECT "projects".* FROM "projects" WHERE "projects"."id" = 135 LIMIT 1 /*application:web,correlation_id:01H2883YSV4HKKTY52XH36JDEW,endpoint_id:GET /api/:version/projects/:id/packages/:package_id/pipelines,db_config_name:main,line:/lib/api/entities/ci/pipeline_basic.rb:22:in `block in <class:PipelineBasic>'*/
  ↳ lib/api/entities/ci/pipeline_basic.rb:22:in `block in <class:PipelineBasic>'
  Namespace Load (0.1ms)  SELECT "namespaces".* FROM "namespaces" WHERE "namespaces"."id" = 295 LIMIT 1 /*application:web,correlation_id:01H2883YSV4HKKTY52XH36JDEW,endpoint_id:GET /api/:version/projects/:id/packages/:package_id/pipelines,db_config_name:main,line:/config/routes.rb:319:in `block (4 levels) in <main>'*/
  ↳ config/routes.rb:319:in `block (4 levels) in <main>'
  Route Load (0.1ms)  SELECT "routes".* FROM "routes" WHERE "routes"."source_id" = 295 AND "routes"."source_type" = 'Namespace' LIMIT 1 /*application:web,correlation_id:01H2883YSV4HKKTY52XH36JDEW,endpoint_id:GET /api/:version/projects/:id/packages/:package_id/pipelines,db_config_name:main,line:/app/models/concerns/routable.rb:141:in `block in full_attribute'*/
  ↳ app/models/concerns/routable.rb:141:in `block in full_attribute'
  Project Load (0.2ms)  SELECT "projects".* FROM "projects" WHERE "projects"."id" = 135 LIMIT 1 /*application:web,correlation_id:01H2883YSV4HKKTY52XH36JDEW,endpoint_id:GET /api/:version/projects/:id/packages/:package_id/pipelines,db_config_name:main,line:/lib/api/entities/ci/pipeline_basic.rb:22:in `block in <class:PipelineBasic>'*/
  ↳ lib/api/entities/ci/pipeline_basic.rb:22:in `block in <class:PipelineBasic>'
  Namespace Load (0.1ms)  SELECT "namespaces".* FROM "namespaces" WHERE "namespaces"."id" = 295 LIMIT 1 /*application:web,correlation_id:01H2883YSV4HKKTY52XH36JDEW,endpoint_id:GET /api/:version/projects/:id/packages/:package_id/pipelines,db_config_name:main,line:/config/routes.rb:319:in `block (4 levels) in <main>'*/
  ↳ config/routes.rb:319:in `block (4 levels) in <main>'
  Route Load (0.1ms)  SELECT "routes".* FROM "routes" WHERE "routes"."source_id" = 295 AND "routes"."source_type" = 'Namespace' LIMIT 1 /*application:web,correlation_id:01H2883YSV4HKKTY52XH36JDEW,endpoint_id:GET /api/:version/projects/:id/packages/:package_id/pipelines,db_config_name:main,line:/app/models/concerns/routable.rb:141:in `block in full_attribute'*/
  ↳ app/models/concerns/routable.rb:141:in `block in full_attribute'
  Project Load (0.2ms)  SELECT "projects".* FROM "projects" WHERE "projects"."id" = 135 LIMIT 1 /*application:web,correlation_id:01H2883YSV4HKKTY52XH36JDEW,endpoint_id:GET /api/:version/projects/:id/packages/:package_id/pipelines,db_config_name:main,line:/lib/api/entities/ci/pipeline_basic.rb:22:in `block in <class:PipelineBasic>'*/
  ↳ lib/api/entities/ci/pipeline_basic.rb:22:in `block in <class:PipelineBasic>'
  Namespace Load (0.1ms)  SELECT "namespaces".* FROM "namespaces" WHERE "namespaces"."id" = 295 LIMIT 1 /*application:web,correlation_id:01H2883YSV4HKKTY52XH36JDEW,endpoint_id:GET /api/:version/projects/:id/packages/:package_id/pipelines,db_config_name:main,line:/config/routes.rb:319:in `block (4 levels) in <main>'*/
  ↳ config/routes.rb:319:in `block (4 levels) in <main>'
  Route Load (0.1ms)  SELECT "routes".* FROM "routes" WHERE "routes"."source_id" = 295 AND "routes"."source_type" = 'Namespace' LIMIT 1 /*application:web,correlation_id:01H2883YSV4HKKTY52XH36JDEW,endpoint_id:GET /api/:version/projects/:id/packages/:package_id/pipelines,db_config_name:main,line:/app/models/concerns/routable.rb:141:in `block in full_attribute'*/
  ↳ app/models/concerns/routable.rb:141:in `block in full_attribute'
  Project Load (0.3ms)  SELECT "projects".* FROM "projects" WHERE "projects"."id" = 135 LIMIT 1 /*application:web,correlation_id:01H2883YSV4HKKTY52XH36JDEW,endpoint_id:GET /api/:version/projects/:id/packages/:package_id/pipelines,db_config_name:main,line:/lib/api/entities/ci/pipeline_basic.rb:22:in `block in <class:PipelineBasic>'*/
  ↳ lib/api/entities/ci/pipeline_basic.rb:22:in `block in <class:PipelineBasic>'
  Namespace Load (0.1ms)  SELECT "namespaces".* FROM "namespaces" WHERE "namespaces"."id" = 295 LIMIT 1 /*application:web,correlation_id:01H2883YSV4HKKTY52XH36JDEW,endpoint_id:GET /api/:version/projects/:id/packages/:package_id/pipelines,db_config_name:main,line:/config/routes.rb:319:in `block (4 levels) in <main>'*/
  ↳ config/routes.rb:319:in `block (4 levels) in <main>'
  Route Load (0.1ms)  SELECT "routes".* FROM "routes" WHERE "routes"."source_id" = 295 AND "routes"."source_type" = 'Namespace' LIMIT 1 /*application:web,correlation_id:01H2883YSV4HKKTY52XH36JDEW,endpoint_id:GET /api/:version/projects/:id/packages/:package_id/pipelines,db_config_name:main,line:/app/models/concerns/routable.rb:141:in `block in full_attribute'*/
  ↳ app/models/concerns/routable.rb:141:in `block in full_attribute'
  Project Load (0.2ms)  SELECT "projects".* FROM "projects" WHERE "projects"."id" = 135 LIMIT 1 /*application:web,correlation_id:01H2883YSV4HKKTY52XH36JDEW,endpoint_id:GET /api/:version/projects/:id/packages/:package_id/pipelines,db_config_name:main,line:/lib/api/entities/ci/pipeline_basic.rb:22:in `block in <class:PipelineBasic>'*/
  ↳ lib/api/entities/ci/pipeline_basic.rb:22:in `block in <class:PipelineBasic>'
  Namespace Load (0.1ms)  SELECT "namespaces".* FROM "namespaces" WHERE "namespaces"."id" = 295 LIMIT 1 /*application:web,correlation_id:01H2883YSV4HKKTY52XH36JDEW,endpoint_id:GET /api/:version/projects/:id/packages/:package_id/pipelines,db_config_name:main,line:/config/routes.rb:319:in `block (4 levels) in <main>'*/
  ↳ config/routes.rb:319:in `block (4 levels) in <main>'
  Route Load (0.1ms)  SELECT "routes".* FROM "routes" WHERE "routes"."source_id" = 295 AND "routes"."source_type" = 'Namespace' LIMIT 1 /*application:web,correlation_id:01H2883YSV4HKKTY52XH36JDEW,endpoint_id:GET /api/:version/projects/:id/packages/:package_id/pipelines,db_config_name:main,line:/app/models/concerns/routable.rb:141:in `block in full_attribute'*/
  ↳ app/models/concerns/routable.rb:141:in `block in full_attribute'
  Route Load (0.1ms)  SELECT "routes".* FROM "routes" WHERE "routes"."source_id" = 135 AND "routes"."source_type" = 'Project' LIMIT 1 /*application:web,correlation_id:01H2883YSV4HKKTY52XH36JDEW,endpoint_id:GET /api/:version/projects/:id/packages/:package_id/pipelines,db_config_name:main,line:/app/models/concerns/routable.rb:141:in `block in full_attribute'*/
  ↳ app/models/concerns/routable.rb:141:in `block in full_attribute'

This is mitigated by the fact that we only return a maximum of 20 pipelines per request.

This line is the source of the n+1. The project and its 'namespace' gets loaded.

We can fix that with a .includes(project: :namespace) when loading ::Ci::Pipelines. The only thing is that we can't use that in the API controller directly. We'd like to avoid modifying the ::Ci::Pipielines model if possible.

Maybe a compromise is to introduce a new scope in the ::Ci::Pipelines model, and use it in a new Finder class to be used by the API controller.

Edited by Radamanthus Batnag