Skip to content

Denormalize namespace traversal ids to match builds for group runners

What does this MR do and why?

This merge request decouples group runners query from namespaces table.

Database

The merge request add a denormalization of namespace_traversal_ids to match builds for group runners more efficiently and to decouple this query from namespaces and projects tables to unblock groupsharding team.

This should be also more efficient because we are using self and ancestors strategy to match builds and namespaces in the most efficient way possible, making use of the limit of nesting groups / namespaces we have set to 20 levels.

The old query for group runners

SELECT "ci_pending_builds".*
FROM "ci_pending_builds"
WHERE "ci_pending_builds"."project_id" IN
    (SELECT "projects"."id"
     FROM "projects"
     INNER JOIN "project_ci_cd_settings" ON "project_ci_cd_settings"."project_id" = "projects"."id"
     LEFT JOIN project_features ON projects.id = project_features.project_id
     WHERE "projects"."namespace_id" IN
         (WITH RECURSIVE "base_and_descendants" AS (
                                                      (SELECT "namespaces"."id",
                                                              "namespaces"."name",
                                                              "namespaces"."path",
                                                              "namespaces"."owner_id",
                                                              "namespaces"."created_at",
                                                              "namespaces"."updated_at",
                                                              "namespaces"."type",
                                                              "namespaces"."description",
                                                              "namespaces"."avatar",
                                                              "namespaces"."membership_lock",
                                                              "namespaces"."share_with_group_lock",
                                                              "namespaces"."visibility_level",
                                                              "namespaces"."request_access_enabled",
                                                              "namespaces"."ldap_sync_status",
                                                              "namespaces"."ldap_sync_error",
                                                              "namespaces"."ldap_sync_last_update_at",
                                                              "namespaces"."ldap_sync_last_successful_update_at",
                                                              "namespaces"."ldap_sync_last_sync_at",
                                                              "namespaces"."description_html",
                                                              "namespaces"."lfs_enabled",
                                                              "namespaces"."parent_id",
                                                              "namespaces"."shared_runners_minutes_limit",
                                                              "namespaces"."repository_size_limit",
                                                              "namespaces"."require_two_factor_authentication",
                                                              "namespaces"."two_factor_grace_period",
                                                              "namespaces"."cached_markdown_version",
                                                              "namespaces"."project_creation_level",
                                                              "namespaces"."runners_token",
                                                              "namespaces"."file_template_project_id",
                                                              "namespaces"."saml_discovery_token",
                                                              "namespaces"."runners_token_encrypted",
                                                              "namespaces"."custom_project_templates_group_id",
                                                              "namespaces"."auto_devops_enabled",
                                                              "namespaces"."extra_shared_runners_minutes_limit",
                                                              "namespaces"."last_ci_minutes_notification_at",
                                                              "namespaces"."last_ci_minutes_usage_notification_level",
                                                              "namespaces"."subgroup_creation_level",
                                                              "namespaces"."emails_disabled",
                                                              "namespaces"."max_pages_size",
                                                              "namespaces"."max_artifacts_size",
                                                              "namespaces"."mentions_disabled",
                                                              "namespaces"."default_branch_protection",
                                                              "namespaces"."unlock_membership_to_ldap",
                                                              "namespaces"."max_personal_access_token_lifetime",
                                                              "namespaces"."push_rule_id",
                                                              "namespaces"."shared_runners_enabled",
                                                              "namespaces"."allow_descendants_override_disabled_shared_runners",
                                                              "namespaces"."traversal_ids"
                                                       FROM "namespaces"
                                                       INNER JOIN "ci_runner_namespaces" ON "ci_runner_namespaces"."namespace_id" = "namespaces"."id"
                                                       WHERE "namespaces"."type" = 'Group'
                                                         AND "ci_runner_namespaces"."runner_id" = 6)
                                                    UNION
                                                      (SELECT "namespaces"."id",
                                                              "namespaces"."name",
                                                              "namespaces"."path",
                                                              "namespaces"."owner_id",
                                                              "namespaces"."created_at",
                                                              "namespaces"."updated_at",
                                                              "namespaces"."type",
                                                              "namespaces"."description",
                                                              "namespaces"."avatar",
                                                              "namespaces"."membership_lock",
                                                              "namespaces"."share_with_group_lock",
                                                              "namespaces"."visibility_level",
                                                              "namespaces"."request_access_enabled",
                                                              "namespaces"."ldap_sync_status",
                                                              "namespaces"."ldap_sync_error",
                                                              "namespaces"."ldap_sync_last_update_at",
                                                              "namespaces"."ldap_sync_last_successful_update_at",
                                                              "namespaces"."ldap_sync_last_sync_at",
                                                              "namespaces"."description_html",
                                                              "namespaces"."lfs_enabled",
                                                              "namespaces"."parent_id",
                                                              "namespaces"."shared_runners_minutes_limit",
                                                              "namespaces"."repository_size_limit",
                                                              "namespaces"."require_two_factor_authentication",
                                                              "namespaces"."two_factor_grace_period",
                                                              "namespaces"."cached_markdown_version",
                                                              "namespaces"."project_creation_level",
                                                              "namespaces"."runners_token",
                                                              "namespaces"."file_template_project_id",
                                                              "namespaces"."saml_discovery_token",
                                                              "namespaces"."runners_token_encrypted",
                                                              "namespaces"."custom_project_templates_group_id",
                                                              "namespaces"."auto_devops_enabled",
                                                              "namespaces"."extra_shared_runners_minutes_limit",
                                                              "namespaces"."last_ci_minutes_notification_at",
                                                              "namespaces"."last_ci_minutes_usage_notification_level",
                                                              "namespaces"."subgroup_creation_level",
                                                              "namespaces"."emails_disabled",
                                                              "namespaces"."max_pages_size",
                                                              "namespaces"."max_artifacts_size",
                                                              "namespaces"."mentions_disabled",
                                                              "namespaces"."default_branch_protection",
                                                              "namespaces"."unlock_membership_to_ldap",
                                                              "namespaces"."max_personal_access_token_lifetime",
                                                              "namespaces"."push_rule_id",
                                                              "namespaces"."shared_runners_enabled",
                                                              "namespaces"."allow_descendants_override_disabled_shared_runners",
                                                              "namespaces"."traversal_ids"
                                                       FROM "namespaces",
                                                            "base_and_descendants"
                                                       WHERE "namespaces"."type" = 'Group'
                                                         AND "namespaces"."parent_id" = "base_and_descendants"."id")) SELECT "namespaces"."id"
          FROM "base_and_descendants" AS "namespaces")
       AND "project_ci_cd_settings"."group_runners_enabled" = TRUE
       AND ("project_features"."builds_access_level" > 0
            OR "project_features"."builds_access_level" IS NULL)
       AND "projects"."pending_delete" = FALSE)
  AND (tag_ids = {})
ORDER BY build_id ASC;

The new query for group runners

New query:

SELECT
    "ci_pending_builds".*
FROM
    "ci_pending_builds"
WHERE (namespace_traversal_ids && ARRAY[193]::int[])
    AND (tag_ids <@ ARRAY[123]::int[]);
    /* AND (tag_ids = '{}'); */

10k pending builds

No index: query plan

 Planning Time: 1.463 ms
 Execution Time: 18.912 ms

Gin index: query plan

 Planning Time: 0.927 ms
 Execution Time: 0.198 ms

120k pending builds

No index: query plan

 Planning Time: 0.228 ms
 Execution Time: 50.327 ms

Gin index: query plan

 Planning Time: 0.770 ms
 Execution Time: 2.338 ms

520k pending builds

No index: query plan

 Planning Time: 0.256 ms
 Execution Time: 80.576 ms

Gin index: query plan

 Planning Time: 0.237 ms
 Execution Time: 16.418 ms

1M pending builds

No index: query plan

 Planning Time: 0.220 ms
 Execution Time: 149.576 ms

GIN index: query plan

 Planning Time: 0.407 ms
 Execution Time: 19.044 ms

5M pending builds

Gin index: query plan

 Planning Time: 0.586 ms
 Execution Time: 72.837 ms
Edited by Grzegorz Bizon

Merge request reports

Loading