Creation of new runner with N+1 query pattern

Coming from https://gitlab.com/gitlab-org/gitlab/-/issues/299451#note_507492978, we observed a N+1 query pattern upon the creation of a runner.

Going to copy in @pbair 's findings from the other issue below.

This issue is about removing the N+1 pattern if we can.


Okay, the issues seems to be coming from here: https://gitlab.com/gitlab-org/gitlab/-/blob/b382088c8f748934b1db0fb8735fd6ea71ce283d/app/models/namespace.rb#L70

During creation of a runner, validation must be triggered on the associated namespace, hitting that line. This results in the namespace validating every associated runner:

So first it loads all runners:

  Ci::Runner Load (2.4ms)  SELECT "ci_runners".* FROM "ci_runners" INNER JOIN "ci_runner_namespaces" ON "ci_runners"."id" = "ci_runner_namespaces"."runner_id" WHERE "ci_runner_namespaces"."namespace_id" = 22
  ↳ lib/api/ci/runner.rb:47:in `block (2 levels) in <class:Runner>'

Then, for each runner in that set, runs a series of queries to check that tags and project/namespaces ownership is valid:

  ActsAsTaggableOn::Tagging Load (0.6ms)  SELECT "taggings".* FROM "taggings" WHERE "taggings"."taggable_id" = 1 AND "taggings"."taggable_type" = 'Ci::Runner'
  ↳ app/models/ci/runner.rb:269:in `has_tags?'
  ActsAsTaggableOn::Tag Load (0.5ms)  SELECT "tags".* FROM "tags" INNER JOIN "taggings" ON "tags"."id" = "taggings"."tag_id" WHERE "taggings"."taggable_id" = 1 AND "taggings"."taggable_type" = 'Ci::Runner' AND (taggings.context = 'tags' AND taggings.tagger_id IS NULL)
  ↳ app/models/ci/runner.rb:269:in `has_tags?'
  Project Exists? (0.7ms)  SELECT 1 AS one FROM "projects" INNER JOIN "ci_runner_projects" ON "projects"."id" = "ci_runner_projects"."project_id" WHERE "ci_runner_projects"."runner_id" = 1 LIMIT 1
  ↳ app/models/ci/runner.rb:349:in `no_projects'
   (1.3ms)  SELECT COUNT(*) FROM "namespaces" INNER JOIN "ci_runner_namespaces" ON "namespaces"."id" = "ci_runner_namespaces"."namespace_id" WHERE "namespaces"."type" = 'Group' AND "ci_runner_namespaces"."runner_id" = 1
  ↳ app/models/ci/runner.rb:367:in `exactly_one_group'

So in this example case with ~7000 runners tied to one namespace, it's not hard to see why it's timing out somewhere after running 20k+ queries. Commenting out that line prevents this entire chain of validations from running.