Skip to content

Use GroupTree ancestors linear scopes

What does this MR do and why?

In this MR, we're switching the behavior of the method GroupTree#filtered_groups_with_ancestors to use the linear version. The new behavior is behind the linear_group_tree_ancestor_scopes feature flag.

Refs #339228 (closed)

Database queries

Disclaimer: The queries shown here are a simplification. The full queries can be found in https://gitlab.com/gitlab-org/gitlab/-/snippets/2176552. The user used to perform the query against is one of our more heavy ones, the gitlab-qa user with around 6k groups.

The original (recursive) query is:

WITH RECURSIVE base_and_ancestors AS (
                                        (SELECT namespaces.*
                                         FROM namespaces
                                         WHERE namespaces.type = 'Group'
                                           AND namespaces.id IN
                                             (SELECT namespaces.id
                                              FROM (
                                                      (WITH RECURSIVE base_and_ancestors AS (
                                                                                               (SELECT namespaces.*
                                                                                                FROM (
                                                                                                        (WITH direct_groups AS MATERIALIZED
                                                                                                           (SELECT namespaces.*
                                                                                                            FROM (
                                                                                                                    (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 = 1614863
                                                                                                                       AND members.requested_at IS NULL
                                                                                                                       AND (access_level >= 10))
                                                                                                                  UNION
                                                                                                                    (SELECT namespaces.*
                                                                                                                     FROM projects
                                                                                                                     INNER JOIN project_authorizations ON projects.id = project_authorizations.project_id
                                                                                                                     INNER JOIN namespaces ON namespaces.id = projects.namespace_id
                                                                                                                     WHERE project_authorizations.user_id = 1614863)) namespaces
                                                                                                            WHERE namespaces.type = 'Group') SELECT namespaces.*
                                                                                                         FROM (
                                                                                                                 (SELECT namespaces.*
                                                                                                                  FROM direct_groups namespaces
                                                                                                                  WHERE namespaces.type = 'Group')
                                                                                                               UNION
                                                                                                                 (SELECT namespaces.*
                                                                                                                  FROM namespaces
                                                                                                                  INNER JOIN group_group_links ON group_group_links.shared_group_id = namespaces.id
                                                                                                                  WHERE namespaces.type = 'Group'
                                                                                                                    AND group_group_links.shared_with_group_id IN
                                                                                                                      (SELECT namespaces.id
                                                                                                                       FROM direct_groups namespaces
                                                                                                                       WHERE namespaces.type = 'Group'))) namespaces
                                                                                                         WHERE namespaces.type = 'Group')) namespaces
                                                                                                WHERE namespaces.type = 'Group')
                                                                                             UNION
                                                                                               (SELECT namespaces.*
                                                                                                FROM namespaces,
                                                                                                     base_and_ancestors
                                                                                                WHERE namespaces.type = 'Group'
                                                                                                  AND namespaces.id = base_and_ancestors.parent_id)),
                                                                      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 = 1614863
                                                                                                    AND members.requested_at IS NULL
                                                                                                    AND (access_level >= 10))
                                                                                               UNION
                                                                                                 (SELECT namespaces.*
                                                                                                  FROM namespaces,
                                                                                                       base_and_descendants
                                                                                                  WHERE namespaces.type = 'Group'
                                                                                                    AND namespaces.parent_id = base_and_descendants.id)) SELECT namespaces.*
                                                       FROM (
                                                               (SELECT namespaces.*
                                                                FROM base_and_ancestors AS namespaces
                                                                WHERE namespaces.type = 'Group')
                                                             UNION
                                                               (SELECT namespaces.*
                                                                FROM base_and_descendants AS namespaces
                                                                WHERE namespaces.type = 'Group')) namespaces
                                                       WHERE namespaces.type = 'Group')
                                                    UNION
                                                      (SELECT namespaces.*
                                                       FROM namespaces
                                                       WHERE namespaces.type = 'Group'
                                                         AND namespaces.visibility_level IN (10,
                                                                                             20))) namespaces
                                              WHERE namespaces.type = 'Group'
                                                AND (namespaces.path ILIKE '%git%'
                                                     OR namespaces.name ILIKE '%git%')
                                              ORDER BY namespaces.id DESC
                                              LIMIT 20
                                              OFFSET 0))
                                      UNION
                                        (SELECT namespaces.*
                                         FROM namespaces,
                                              base_and_ancestors
                                         WHERE namespaces.type = 'Group'
                                           AND namespaces.id = base_and_ancestors.parent_id))
SELECT namespaces.*
FROM base_and_ancestors AS namespaces

This is the query plan and the times are:

 Planning Time: 12.539 ms
 Execution Time: 1134.158 ms

The new linear query is:

SELECT namespaces.*
FROM
  (SELECT namespaces.*
   FROM namespaces
   WHERE namespaces.id IN
       (SELECT unnest(traversal_ids)
        FROM namespaces
        WHERE namespaces.id IN
            (SELECT namespaces.id
             FROM (
                     (WITH RECURSIVE base_and_ancestors AS (
                                                              (SELECT namespaces.*
                                                               FROM (
                                                                       (WITH direct_groups AS MATERIALIZED
                                                                          (SELECT namespaces.*
                                                                           FROM (
                                                                                   (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 = 1614863
                                                                                      AND members.requested_at IS NULL
                                                                                      AND (access_level >= 10))
                                                                                 UNION
                                                                                   (SELECT namespaces.*
                                                                                    FROM projects
                                                                                    INNER JOIN project_authorizations ON projects.id = project_authorizations.project_id
                                                                                    INNER JOIN namespaces ON namespaces.id = projects.namespace_id
                                                                                    WHERE project_authorizations.user_id = 1614863)) namespaces
                                                                           WHERE namespaces.type = 'Group') SELECT namespaces.*
                                                                        FROM (
                                                                                (SELECT namespaces.*
                                                                                 FROM direct_groups namespaces
                                                                                 WHERE namespaces.type = 'Group')
                                                                              UNION
                                                                                (SELECT namespaces.*
                                                                                 FROM namespaces
                                                                                 INNER JOIN group_group_links ON group_group_links.shared_group_id = namespaces.id
                                                                                 WHERE namespaces.type = 'Group'
                                                                                   AND group_group_links.shared_with_group_id IN
                                                                                     (SELECT namespaces.id
                                                                                      FROM direct_groups namespaces
                                                                                      WHERE namespaces.type = 'Group'))) namespaces
                                                                        WHERE namespaces.type = 'Group')) namespaces
                                                               WHERE namespaces.type = 'Group')
                                                            UNION
                                                              (SELECT namespaces.*
                                                               FROM namespaces,
                                                                    base_and_ancestors
                                                               WHERE namespaces.type = 'Group'
                                                                 AND namespaces.id = base_and_ancestors.parent_id)),
                                     base_and_descendants AS (
                                                                (SELECT 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 = 1614863
                                                                   AND members.requested_at IS NULL
                                                                   AND (access_level >= 10))
                                                              UNION
                                                                (SELECT namespaces.*
                                                                 FROM namespaces,
                                                                      base_and_descendants
                                                                 WHERE namespaces.type = 'Group'
                                                                   AND namespaces.parent_id = base_and_descendants.id)) SELECT namespaces.*
                      FROM (
                              (SELECT namespaces.*
                               FROM base_and_ancestors AS namespaces
                               WHERE namespaces.type = 'Group')
                            UNION
                              (SELECT namespaces.*
                               FROM base_and_descendants AS namespaces
                               WHERE namespaces.type = 'Group')) namespaces
                      WHERE namespaces.type = 'Group')
                   UNION
                     (SELECT namespaces.*
                      FROM namespaces
                      WHERE namespaces.type = 'Group'
                        AND namespaces.visibility_level IN (10,
                                                            20))) namespaces
             WHERE namespaces.type = 'Group'
               AND (namespaces.path ILIKE '%git%'
                    OR namespaces.name ILIKE '%git%')
             ORDER BY namespaces.id DESC
             LIMIT 20
             OFFSET 0))) namespaces

This is the query plan and the times are:

Planning Time: 9.953 ms
Execution Time: 933.328 ms

How to set up and validate locally

  1. Enable the feature flag for linear scopes

    Feature.enable(:use_traversal_ids)
  2. Enable the feature flag for linear ancestors scopes

    Feature.enable(:use_traversal_ids_for_ancestor_scopes)
  3. Enable the feature flag for Ci::Runner linear ancestors scopes

    Feature.enable(:linear_group_tree_ancestor_scopes)
  4. Go to the page http://127.0.0.1:3000/dashboard/groups

  5. Set a filter in the group search box:

image

MR acceptance checklist

This checklist encourages us to confirm any changes have been analyzed to reduce risks in quality, performance, reliability, security, and maintainability.

Merge request reports