Skip to content

Preload saml providers for cross-group child epics

Eugenia Grieff requested to merge fix-child-epics-preload into master

What does this MR do and why?

Listing child epics executes N+1 queries for each group's saml_provider when filtering the epics that current_user has access to.

This MR adds the method Group#preload_root_saml_providers that presets the group root_saml_provider to fix the problem.

Database changes

  • Using finder without preload:
Epics::CrossHierarchyChildrenFinder.new(current_user, { parent: parent, sort: 'relative_position' }).execute
SQL queries
SELECT "saml_providers".*
FROM "saml_providers"
WHERE "saml_providers"."group_id" = 121 LIMIT 1 -- Different query (1)

WITH "base_ancestors_cte" AS MATERIALIZED 
    (SELECT DISTINCT "namespaces"."traversal_ids"
    FROM "namespaces"
    INNER JOIN 
        (SELECT "epics"."group_id"
        FROM "epics"
        WHERE "epics"."parent_id" = 1205) AS epics
            ON epics.group_id = namespaces.id
        WHERE "namespaces"."type" = 'Group'
                AND 1=1)
    SELECT "namespaces"."id",
         "namespaces"."parent_id",
         "gitlab_subscriptions"."hosted_plan_id"
FROM "namespaces"
INNER JOIN 
    (SELECT DISTINCT unnest("base_ancestors_cte"."traversal_ids")
    FROM base_ancestors_cte) AS ancestors(ancestor_id)
    ON namespaces.id = ancestors.ancestor_id LEFT OUTER
JOIN gitlab_subscriptions
    ON gitlab_subscriptions.namespace_id=namespaces.id
WHERE "namespaces"."type" = 'Group' -- Same query (2)

SELECT DISTINCT "namespaces".*
FROM "namespaces"
INNER JOIN 
    (SELECT "epics"."group_id"
    FROM "epics"
    WHERE "epics"."parent_id" = 1205) AS epics
    ON epics.group_id = namespaces.id
WHERE "namespaces"."type" = 'Group' AND 1=1 -- Extra query

SELECT "namespaces".*
FROM "namespaces"
WHERE "namespaces"."id" = 121 LIMIT 1 -- Extra query

SELECT "saml_providers".*
FROM "saml_providers"
WHERE "saml_providers"."group_id" = 121 LIMIT 1 -- Extra query

SELECT MAX("members"."access_level") AS maximum_access_level,
         "members"."user_id" AS members_user_id
FROM (
    (SELECT "members"."id",
         "members"."access_level",
         "members"."source_id",
         "members"."source_type",
         "members"."user_id",
         "members"."notification_level",
         "members"."type",
         "members"."created_at",
         "members"."updated_at",
         "members"."created_by_id",
         "members"."invite_email",
         "members"."invite_token",
         "members"."invite_accepted_at",
         "members"."requested_at",
         "members"."expires_at",
         "members"."ldap",
         "members"."override",
         "members"."state",
         "members"."invite_email_success",
         "members"."member_namespace_id",
         "members"."member_role_id"
    FROM "members" LEFT OUTER
    JOIN "users"
        ON "users"."id" = "members"."user_id"
    WHERE "members"."type" = 'GroupMember'
            AND "members"."source_type" = 'Namespace'
            AND "users"."state" = 'active'
            AND "members"."state" = 0
            AND "members"."requested_at" IS NULL
            AND "members"."invite_token" IS NULL
            AND (members.access_level > 5)
            AND (members.access_level > 5)
            AND "members"."source_id" IN 
        (SELECT "namespaces"."id"
        FROM "namespaces"
        WHERE "namespaces"."type" = 'Group'
                AND "namespaces"."id" IN (121, 122)))
        UNION
        (WITH "group_group_links_cte" AS MATERIALIZED 
            (SELECT "group_group_links".*
            FROM "group_group_links"
            WHERE "group_group_links"."shared_group_id" IN 
                (SELECT "namespaces"."id"
                FROM "namespaces"
                WHERE "namespaces"."type" = 'Group'
                        AND "namespaces"."id" IN (121, 122)))
                SELECT "members"."id",
         LEAST("group_group_links"."group_access",
         "members"."access_level") AS access_level,
         "members"."source_id",
         "members"."source_type",
         "members"."user_id",
         "members"."notification_level",
         "members"."type",
         "members"."created_at",
         "members"."updated_at",
         "members"."created_by_id",
         "members"."invite_email",
         "members"."invite_token",
         "members"."invite_accepted_at",
         "members"."requested_at",
         "members"."expires_at",
         "members"."ldap",
         "members"."override",
         "members"."state",
         "members"."invite_email_success",
         "members"."member_namespace_id",
         "members"."member_role_id"
                FROM "members", "group_group_links_cte" AS "group_group_links"
                WHERE "members"."type" = 'GroupMember'
                        AND "members"."source_type" = 'Namespace'
                        AND "members"."requested_at" IS NULL
                        AND "members"."source_id" = "group_group_links"."shared_with_group_id"
                        AND "members"."source_type" = 'Namespace'
                        AND "members"."state" = 0
                        AND (members.access_level > 5))) members
            WHERE "members"."type" = 'GroupMember'
        AND "members"."source_type" = 'Namespace'
        AND "members"."user_id" = 47
GROUP BY  "members"."user_id" # Same query (3)

SELECT "saml_providers".*
FROM "saml_providers"
WHERE "saml_providers"."group_id" = 123 LIMIT 1 -- Extra query

SELECT MAX("members"."access_level") AS maximum_access_level,
         "members"."user_id" AS members_user_id
FROM (
    (SELECT "members"."id",
         "members"."access_level",
         "members"."source_id",
         "members"."source_type",
         "members"."user_id",
         "members"."notification_level",
         "members"."type",
         "members"."created_at",
         "members"."updated_at",
         "members"."created_by_id",
         "members"."invite_email",
         "members"."invite_token",
         "members"."invite_accepted_at",
         "members"."requested_at",
         "members"."expires_at",
         "members"."ldap",
         "members"."override",
         "members"."state",
         "members"."invite_email_success",
         "members"."member_namespace_id",
         "members"."member_role_id"
    FROM "members" LEFT OUTER
    JOIN "users"
        ON "users"."id" = "members"."user_id"
    WHERE "members"."type" = 'GroupMember'
            AND "members"."source_type" = 'Namespace'
            AND "users"."state" = 'active'
            AND "members"."state" = 0
            AND "members"."requested_at" IS NULL
            AND "members"."invite_token" IS NULL
            AND (members.access_level > 5)
            AND (members.access_level > 5)
            AND "members"."source_id" = 123)
    UNION
    (WITH "group_group_links_cte" AS MATERIALIZED 
        (SELECT "group_group_links".*
        FROM "group_group_links"
        WHERE "group_group_links"."shared_group_id" = 123)
        SELECT "members"."id",
         LEAST("group_group_links"."group_access",
         "members"."access_level") AS access_level,
         "members"."source_id",
         "members"."source_type",
         "members"."user_id",
         "members"."notification_level",
         "members"."type",
         "members"."created_at",
         "members"."updated_at",
         "members"."created_by_id",
         "members"."invite_email",
         "members"."invite_token",
         "members"."invite_accepted_at",
         "members"."requested_at",
         "members"."expires_at",
         "members"."ldap",
         "members"."override",
         "members"."state",
         "members"."invite_email_success",
         "members"."member_namespace_id",
         "members"."member_role_id"
        FROM "members", "group_group_links_cte" AS "group_group_links"
        WHERE "members"."type" = 'GroupMember'
                AND "members"."source_type" = 'Namespace'
                AND "members"."requested_at" IS NULL
                AND "members"."source_id" = "group_group_links"."shared_with_group_id"
                AND "members"."source_type" = 'Namespace'
                AND "members"."state" = 0
                AND (members.access_level > 5))) members
    WHERE "members"."type" = 'GroupMember'
        AND "members"."source_type" = 'Namespace'
        AND "members"."user_id" = 47
GROUP BY  "members"."user_id" -- Same query (4)

SELECT 1 AS one
FROM (
    (SELECT "projects".*
    FROM "projects"
    WHERE "projects"."namespace_id" IN 
        (SELECT "namespaces"."id"
        FROM "namespaces"
        WHERE "namespaces"."type" = 'Group'
                AND (traversal_ids @> ('{123}'))))) projects
    WHERE (EXISTS 
        (SELECT 1
        FROM "project_authorizations"
        WHERE "project_authorizations"."user_id" = 47
                AND (project_authorizations.project_id = projects.id))
                OR projects.visibility_level IN (10,20))
            AND "projects"."pending_delete" = FALSE
        AND "projects"."hidden" = FALSE LIMIT 1 # Same query (5)
SELECT 1 AS one
FROM "epics"
WHERE "epics"."parent_id" = 1205
        AND "epics"."group_id" IN (122, 123)
        AND "epics"."confidential" = TRUE LIMIT 1 -- Same query (6)
        
SELECT "epics".*
FROM "epics"
WHERE "epics"."parent_id" = 1205
        AND "epics"."group_id" IN (122, 123)
ORDER BY  relative_position ASC, id DESC, "epics"."id" DESC -- Same query (7)
  • Using finder with preload:
Epics::CrossHierarchyChildrenFinder.new(current_user, { parent: parent, sort: 'relative_position' }).execute(preload: true)
SQL queries
SELECT
	"namespaces".*
FROM
	"namespaces"
WHERE
	"namespaces"."id" = 121
LIMIT
	1 -- New query

SELECT
	"saml_providers".*
FROM
	"saml_providers"
WHERE
	"saml_providers"."group_id" IN (121, 123) -- Diffrent query (1)

WITH "base_ancestors_cte" AS MATERIALIZED (
	SELECT
		"namespaces"."traversal_ids"
	FROM
		"namespaces"
	WHERE
		"namespaces"."type" = 'Group'
		AND "namespaces"."id" IN (122, 123)
)
SELECT
	"namespaces"."id",
	"namespaces"."parent_id",
	"gitlab_subscriptions"."hosted_plan_id"
FROM
	"namespaces"
	INNER JOIN (
		SELECT
			DISTINCT unnest("base_ancestors_cte"."traversal_ids")
		FROM
			base_ancestors_cte
	) AS ancestors(ancestor_id) ON namespaces.id = ancestors.ancestor_id
	LEFT OUTER JOIN gitlab_subscriptions ON gitlab_subscriptions.namespace_id = namespaces.id
WHERE
	"namespaces"."type" = 'Group' -- Same query (2)

SELECT
	MAX("members"."access_level") AS maximum_access_level,
	"members"."user_id" AS members_user_id
FROM
	(
		(
			SELECT
				"members"."id",
				"members"."access_level",
				"members"."source_id",
				"members"."source_type",
				"members"."user_id",
				"members"."notification_level",
				"members"."type",
				"members"."created_at",
				"members"."updated_at",
				"members"."created_by_id",
				"members"."invite_email",
				"members"."invite_token",
				"members"."invite_accepted_at",
				"members"."requested_at",
				"members"."expires_at",
				"members"."ldap",
				"members"."override",
				"members"."state",
				"members"."invite_email_success",
				"members"."member_namespace_id",
				"members"."member_role_id"
			FROM
				"members"
				LEFT OUTER JOIN "users" ON "users"."id" = "members"."user_id"
			WHERE
				"members"."type" = 'GroupMember'
				AND "members"."source_type" = 'Namespace'
				AND "users"."state" = 'active'
				AND "members"."state" = 0
				AND "members"."requested_at" IS NULL
				AND "members"."invite_token" IS NULL
				AND (members.access_level > 5)
				AND (members.access_level > 5)
				AND "members"."source_id" IN (
					SELECT
						"namespaces"."id"
					FROM
						"namespaces"
					WHERE
						"namespaces"."type" = 'Group'
						AND "namespaces"."id" IN (121, 122)
				)
		)
		UNION
			(
				WITH "group_group_links_cte" AS MATERIALIZED (
					SELECT
						"group_group_links".*
					FROM
						"group_group_links"
					WHERE
						"group_group_links"."shared_group_id" IN (
							SELECT
								"namespaces"."id"
							FROM
								"namespaces"
							WHERE
								"namespaces"."type" = 'Group'
								AND "namespaces"."id" IN (121, 122)
						)
				)
				SELECT
					"members"."id",
					LEAST(
						"group_group_links"."group_access",
						"members"."access_level"
					) AS access_level,
					"members"."source_id",
					"members"."source_type",
					"members"."user_id",
					"members"."notification_level",
					"members"."type",
					"members"."created_at",
					"members"."updated_at",
					"members"."created_by_id",
					"members"."invite_email",
					"members"."invite_token",
					"members"."invite_accepted_at",
					"members"."requested_at",
					"members"."expires_at",
					"members"."ldap",
					"members"."override",
					"members"."state",
					"members"."invite_email_success",
					"members"."member_namespace_id",
					"members"."member_role_id"
				FROM
					"members",
					"group_group_links_cte" AS "group_group_links"
				WHERE
					"members"."type" = 'GroupMember'
					AND "members"."source_type" = 'Namespace'
					AND "members"."requested_at" IS NULL
					AND "members"."source_id" = "group_group_links"."shared_with_group_id"
					AND "members"."source_type" = 'Namespace'
					AND "members"."state" = 0
					AND (members.access_level > 5)
			)
	) members
WHERE
	"members"."type" = 'GroupMember'
	AND "members"."source_type" = 'Namespace'
	AND "members"."user_id" = 47
GROUP BY
	"members"."user_id" -- Same query (3)

SELECT
	MAX("members"."access_level") AS maximum_access_level,
	"members"."user_id" AS members_user_id
FROM
	(
		(
			SELECT
				"members"."id",
				"members"."access_level",
				"members"."source_id",
				"members"."source_type",
				"members"."user_id",
				"members"."notification_level",
				"members"."type",
				"members"."created_at",
				"members"."updated_at",
				"members"."created_by_id",
				"members"."invite_email",
				"members"."invite_token",
				"members"."invite_accepted_at",
				"members"."requested_at",
				"members"."expires_at",
				"members"."ldap",
				"members"."override",
				"members"."state",
				"members"."invite_email_success",
				"members"."member_namespace_id",
				"members"."member_role_id"
			FROM
				"members"
				LEFT OUTER JOIN "users" ON "users"."id" = "members"."user_id"
			WHERE
				"members"."type" = 'GroupMember'
				AND "members"."source_type" = 'Namespace'
				AND "users"."state" = 'active'
				AND "members"."state" = 0
				AND "members"."requested_at" IS NULL
				AND "members"."invite_token" IS NULL
				AND (members.access_level > 5)
				AND (members.access_level > 5)
				AND "members"."source_id" = 123
		)
		UNION
			(
				WITH "group_group_links_cte" AS MATERIALIZED (
					SELECT
						"group_group_links".*
					FROM
						"group_group_links"
					WHERE
						"group_group_links"."shared_group_id" = 123
				)
				SELECT
					"members"."id",
					LEAST(
						"group_group_links"."group_access",
						"members"."access_level"
					) AS access_level,
					"members"."source_id",
					"members"."source_type",
					"members"."user_id",
					"members"."notification_level",
					"members"."type",
					"members"."created_at",
					"members"."updated_at",
					"members"."created_by_id",
					"members"."invite_email",
					"members"."invite_token",
					"members"."invite_accepted_at",
					"members"."requested_at",
					"members"."expires_at",
					"members"."ldap",
					"members"."override",
					"members"."state",
					"members"."invite_email_success",
					"members"."member_namespace_id",
					"members"."member_role_id"
				FROM
					"members",
					"group_group_links_cte" AS "group_group_links"
				WHERE
					"members"."type" = 'GroupMember'
					AND "members"."source_type" = 'Namespace'
					AND "members"."requested_at" IS NULL
					AND "members"."source_id" = "group_group_links"."shared_with_group_id"
					AND "members"."source_type" = 'Namespace'
					AND "members"."state" = 0
					AND (members.access_level > 5)
			)
	) members
WHERE
	"members"."type" = 'GroupMember'
	AND "members"."source_type" = 'Namespace'
	AND "members"."user_id" = 47
GROUP BY
	"members"."user_id" -- Same query (4)

SELECT
	1 AS one
FROM
	(
		(
			SELECT
				"projects".*
			FROM
				"projects"
			WHERE
				"projects"."namespace_id" IN (
					SELECT
						"namespaces"."id"
					FROM
						"namespaces"
					WHERE
						"namespaces"."type" = 'Group'
						AND (traversal_ids @ > ('{123}'))
				)
		)
	) projects
WHERE
	(
		EXISTS (
			SELECT
				1
			FROM
				"project_authorizations"
			WHERE
				"project_authorizations"."user_id" = 47
				AND (project_authorizations.project_id = projects.id)
		)
		OR projects.visibility_level IN (10, 20)
	)
	AND "projects"."pending_delete" = FALSE
	AND "projects"."hidden" = FALSE
LIMIT
	1 -- Same query (5)

SELECT
	1 AS one
FROM
	"epics"
WHERE
	"epics"."parent_id" = 1205
	AND "epics"."group_id" IN (122, 123)
	AND "epics"."confidential" = TRUE
LIMIT
	1 -- Same query (6)

SELECT
	"epics".*
FROM
	"epics"
WHERE
	"epics"."parent_id" = 1205
	AND "epics"."group_id" IN (122, 123)
ORDER BY
	relative_position ASC,
	id DESC,
	"epics"."id" DESC -- Same query (7)

Main difference: Instead of executing single queries for saml_providers when checking maximum_access_level for each group, this change will query all saml_providers:

SELECT "saml_providers".* FROM "saml_providers" WHERE "saml_providers"."group_id" = $ID1 LIMIT 1

SELECT "saml_providers".* FROM "saml_providers" WHERE "saml_providers"."group_id" = $ID2 LIMIT 1

SELECT "saml_providers".* FROM "saml_providers" WHERE "saml_providers"."group_id" = $ID3 LIMIT 1

SELECT "saml_providers".* FROM "saml_providers" WHERE "saml_providers"."group_id" IN ($ID1, $ID2, $ID3)

MR acceptance checklist

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

Edited by Eugenia Grieff

Merge request reports