Preload saml providers for cross-group child epics
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.
-
I have evaluated the MR acceptance checklist for this MR.