Skip to content

Resolve "Slow ci runners query at /api/:version/runners"

Alex Pooley requested to merge 335733-faster-ci-runners-query into master

What does this MR do?

Leverage the contacted_at indexes to improve the performance of the /api/:version/runners endpoint.

There's lots of specific info about the problem in the issue description.

This is only a one line MR. There's an existing spec for the modified .offline scope at https://gitlab.com/gitlab-org/gitlab/-/blob/master/spec/models/ci/runner_spec.rb#L371

Original query (NOT IN)

SELECT 
  "ci_runners".* 
FROM 
  (
    (
      SELECT 
        ci_runners.* 
      FROM 
        "ci_runner_projects" 
        INNER JOIN "ci_runners" ON "ci_runners"."id" = "ci_runner_projects"."runner_id" 
      WHERE 
        "ci_runner_projects"."project_id" IN (
          SELECT 
            "projects"."id" 
          FROM 
            "projects" 
            INNER JOIN "project_authorizations" ON "projects"."id" = "project_authorizations"."project_id" 
          WHERE 
            "project_authorizations"."user_id" = 123456 
            AND (
              project_authorizations.access_level >= 40
            )
        )
    ) 
    UNION 
      (
        SELECT 
          ci_runners.* 
        FROM 
          "ci_runner_namespaces" 
          INNER JOIN "ci_runners" ON "ci_runners"."id" = "ci_runner_namespaces"."runner_id" 
        WHERE 
          "ci_runner_namespaces"."namespace_id" IN (
            WITH RECURSIVE "base_and_descendants" AS (
              (
                SELECT 
                  "namespaces".* 
                FROM 
                  "namespaces" 
                  INNER JOIN "members" ON "namespaces"."id" = "members"."source_id" 
                WHERE 
                  "members"."type" = 'GroupMember' 
                  AND "members"."source_type" = 'Namespace' 
                  AND "namespaces"."type" = 'Group' 
                  AND "members"."user_id" = 123456
                  AND "members"."requested_at" IS NULL 
                  AND (access_level >= 10) 
                  AND "members"."access_level" = 50
              ) 
              UNION 
                (
                  SELECT 
                    "namespaces".* 
                  FROM 
                    "namespaces", 
                    "base_and_descendants" 
                  WHERE 
                    "namespaces"."type" = 'Group' 
                    AND "namespaces"."parent_id" = "base_and_descendants"."id"
                )
            ) 
            SELECT 
              id 
            FROM 
              "base_and_descendants" AS "namespaces"
          )
      )
  ) ci_runners 
WHERE 
  "ci_runners"."id" NOT IN (
    SELECT 
      "ci_runners"."id" 
    FROM 
      (
        (
          SELECT 
            ci_runners.* 
          FROM 
            "ci_runner_projects" 
            INNER JOIN "ci_runners" ON "ci_runners"."id" = "ci_runner_projects"."runner_id" 
          WHERE 
            "ci_runner_projects"."project_id" IN (
              SELECT 
                "projects"."id" 
              FROM 
                "projects" 
                INNER JOIN "project_authorizations" ON "projects"."id" = "project_authorizations"."project_id" 
              WHERE 
                "project_authorizations"."user_id" = 123456
                AND (
                  project_authorizations.access_level >= 40
                )
            )
        ) 
        UNION 
          (
            SELECT 
              ci_runners.* 
            FROM 
              "ci_runner_namespaces" 
              INNER JOIN "ci_runners" ON "ci_runners"."id" = "ci_runner_namespaces"."runner_id" 
            WHERE 
              "ci_runner_namespaces"."namespace_id" IN (
                WITH RECURSIVE "base_and_descendants" AS (
                  (
                    SELECT 
                      "namespaces".* 
                    FROM 
                      "namespaces" 
                      INNER JOIN "members" ON "namespaces"."id" = "members"."source_id" 
                    WHERE 
                      "members"."type" = 'GroupMember' 
                      AND "members"."source_type" = 'Namespace' 
                      AND "namespaces"."type" = 'Group' 
                      AND "members"."user_id" = 123456
                      AND "members"."requested_at" IS NULL 
                      AND (access_level >= 10) 
                      AND "members"."access_level" = 50
                  ) 
                  UNION 
                    (
                      SELECT 
                        "namespaces".* 
                      FROM 
                        "namespaces", 
                        "base_and_descendants" 
                      WHERE 
                        "namespaces"."type" = 'Group' 
                        AND "namespaces"."parent_id" = "base_and_descendants"."id"
                    )
                ) 
                SELECT 
                  id 
                FROM 
                  "base_and_descendants" AS "namespaces"
              )
          )
      ) ci_runners 
    WHERE 
      (
        contacted_at > '2021-07-17 03:20:43.164122'
      )
  ) 
ORDER BY 
  "ci_runners"."id" ASC 
LIMIT 
  20 OFFSET 0
Time: 1.226 min  
  - planning: 17.319 ms  
  - execution: 1.225 min  
    - I/O read: 1.098 min  
    - I/O write: 0.000 ms  
  
Shared buffers:  
  - hits: 4231419 (~32.30 GiB) from the buffer pool  
  - reads: 61716 (~482.20 MiB) from the OS file cache, including disk I/O  
  - dirtied: 3231 (~25.20 MiB)  
  - writes: 0  

https://postgres.ai/console/gitlab/gitlab-production-tunnel-pg12/sessions/5211/commands/18399

New query (contacted_at <= ?)

SELECT 
  "ci_runners".* 
FROM 
  (
    (
      SELECT 
        ci_runners.* 
      FROM 
        "ci_runner_projects" 
        INNER JOIN "ci_runners" ON "ci_runners"."id" = "ci_runner_projects"."runner_id" 
      WHERE 
        "ci_runner_projects"."project_id" IN (
          SELECT 
            "projects"."id" 
          FROM 
            "projects" 
            INNER JOIN "project_authorizations" ON "projects"."id" = "project_authorizations"."project_id" 
          WHERE 
            "project_authorizations"."user_id" = 123456 
            AND (
              project_authorizations.access_level >= 40
            )
        )
    ) 
    UNION 
      (
        SELECT 
          ci_runners.* 
        FROM 
          "ci_runner_namespaces" 
          INNER JOIN "ci_runners" ON "ci_runners"."id" = "ci_runner_namespaces"."runner_id" 
        WHERE 
          "ci_runner_namespaces"."namespace_id" IN (
            WITH RECURSIVE "base_and_descendants" AS (
              (
                SELECT 
                  "namespaces".* 
                FROM 
                  "namespaces" 
                  INNER JOIN "members" ON "namespaces"."id" = "members"."source_id" 
                WHERE 
                  "members"."type" = 'GroupMember' 
                  AND "members"."source_type" = 'Namespace' 
                  AND "namespaces"."type" = 'Group' 
                  AND "members"."user_id" = 123456 
                  AND "members"."requested_at" IS NULL 
                  AND (access_level >= 10) 
                  AND "members"."access_level" = 50
              ) 
              UNION 
                (
                  SELECT 
                    "namespaces".* 
                  FROM 
                    "namespaces", 
                    "base_and_descendants" 
                  WHERE 
                    "namespaces"."type" = 'Group' 
                    AND "namespaces"."parent_id" = "base_and_descendants"."id"
                )
            ) 
            SELECT 
              id 
            FROM 
              "base_and_descendants" AS "namespaces"
          )
      )
  ) ci_runners 
WHERE 
  (
    contacted_at <= '2021-07-17 03:20:43.164122'
  ) 
ORDER BY 
  "ci_runners"."id" ASC 
LIMIT 
  20 OFFSET 0
Time: 1.743 s  
  - planning: 3.731 ms  
  - execution: 1.740 s  
    - I/O read: 0.000 ms  
    - I/O write: 0.000 ms  
  
Shared buffers:  
  - hits: 2146584 (~16.40 GiB) from the buffer pool  
  - reads: 0 from the OS file cache, including disk I/O  
  - dirtied: 9 (~72.00 KiB)  
  - writes: 0  

https://postgres.ai/console/gitlab/gitlab-production-tunnel-pg12/sessions/5211/commands/18411

Screenshots or Screencasts (strongly suggested)

Does this MR meet the acceptance criteria?

Conformity

Availability and Testing

Security

Does this MR contain changes to processing or storing of credentials or tokens, authorization and authentication methods or other items described in the security review guidelines? If not, then delete this Security section.

  • [-] Label as security and @ mention @gitlab-com/gl-security/appsec
  • [-] The MR includes necessary changes to maintain consistency between UI, API, email, or other methods
  • [-] Security reports checked/validated by a reviewer from the AppSec team

Related to #335733 (closed)

Edited by Mayra Cabrera

Merge request reports