Skip to content

Create partial indexes for pending/running builds

Andreas Brandl requested to merge ab/ci-builds-partial-indexes into master

What does this MR do?

This creates partial indexes on ci_builds for pending and running builds.

  1. Issue: #327142 (closed)
  2. Incident: gitlab-com/gl-infra/production#4473 (closed)

Query:

SELECT
  "ci_builds"."id"
FROM
  "ci_builds"
  INNER JOIN "projects" ON "projects"."id" = "ci_builds"."project_id"
  LEFT JOIN project_features ON ci_builds.project_id = project_features.project_id
  LEFT JOIN (
    SELECT
      "ci_builds"."project_id",
      count(*) AS running_builds
    FROM
      "ci_builds"
    WHERE
      "ci_builds"."type" = 'Ci::Build'
      AND ("ci_builds"."status" IN ('running'))
      AND "ci_builds"."runner_id" IN (
        SELECT
          "ci_runners"."id"
        FROM
          "ci_runners"
        WHERE
          "ci_runners"."runner_type" = 1)
      GROUP BY
        "ci_builds"."project_id") AS project_builds ON ci_builds.project_id = project_builds.project_id
WHERE ("ci_builds"."status" IN ('pending'))
AND "ci_builds"."runner_id" IS NULL
AND "projects"."shared_runners_enabled" = TRUE
AND "projects"."pending_delete" = FALSE
AND (project_features.builds_access_level IS NULL
  OR project_features.builds_access_level > 0)
AND "ci_builds"."type" = 'Ci::Build'
AND ("projects"."visibility_level" = 20
  OR (EXISTS ( WITH RECURSIVE "base_and_ancestors" AS ((
          SELECT
            "namespaces".*
          FROM
            "namespaces"
          WHERE (namespaces.id = projects.namespace_id))
      UNION (
        SELECT
          "namespaces".*
        FROM
          "namespaces",
          "base_and_ancestors"
        WHERE
          "namespaces"."id" = "base_and_ancestors"."parent_id"))
      SELECT DISTINCT
        1
      FROM
        "base_and_ancestors" AS "namespaces"
      LEFT JOIN namespace_statistics ON namespace_statistics.namespace_id = namespaces.id
    WHERE
      "namespaces"."parent_id" IS NULL
      AND (COALESCE(namespaces.shared_runners_minutes_limit, 400, 0) = 0
        OR COALESCE(namespace_statistics.shared_runners_seconds, 0) < COALESCE((namespaces.shared_runners_minutes_limit + COALESCE(namespaces.extra_shared_runners_minutes_limit, 0)), (400 + COALESCE(namespaces.extra_shared_runners_minutes_limit, 0)), 0) * 60))))
AND (NOT EXISTS (
    SELECT
      1
    FROM
      "taggings"
    WHERE
      "taggings"."taggable_type" = 'CommitStatus'
      AND "taggings"."context" = 'tags'
      AND (taggable_id = ci_builds.id)
      AND "taggings"."tag_id" != 26114))
AND (EXISTS (
    SELECT
      1
    FROM
      "taggings"
    WHERE
      "taggings"."taggable_type" = 'CommitStatus'
      AND "taggings"."context" = 'tags'
      AND (taggable_id = ci_builds.id)))
ORDER BY
  COALESCE(project_builds.running_builds, 0) ASC,
ci_builds.id ASC;

Query plans

  1. Plan (under normal operations): https://explain.depesz.com/s/5Rjp
  2. Plan (when things are on 🔥 - cause still unknown ): https://explain.depesz.com/s/QHTx
  3. Plan (from dblab with indexes foobar2, foobar3 created): https://explain.depesz.com/s/2q2g

The full execution plan from dblab is here: https://explain.depesz.com/s/1e3A

After seeing this, we included the type = 'Ci::Build' into the partial condition.

Stats

  1. Index sizes: Both < 11 MB
  2. Creation times: Both around 9h on dblab (will be much faster in prod)

Does this MR meet the acceptance criteria?

Conformity

Availability and Testing

Security

If this MR contains changes to processing or storing of credentials or tokens, authorization and authentication methods and other items described in the security review guidelines:

  • 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
Edited by Andreas Brandl

Merge request reports