Skip to content

Improve performance of querying alert assignees

Sarah Yasonik requested to merge sy-fix-assignee-n-plus-one into master

What does this MR do?

Querying AlertManagementAlerts with the assignee field through the GraphQL API would result in a N+1 query.

This fixes the query by using BatchLoader::Graphql to group the assignee queries at the end of the query, only once all alerts have been processed. The accomodate authorizations, this logic circumvents our GraphQL auth layer, asserting the same authorization only after the BatchLoader has completed its work.

This is currently behind a feature flag (:alert_assignee) which simply returns an empty collection if not enabled. I'm leaving the flag in place for now.

Related issue: #219571 (closed)

Blocks: #219571 (closed)

Screenshots

GraphQL testing

Test query Output
Screen_Shot_2020-06-02_at_6.21.34_PM Screen_Shot_2020-06-02_at_6.24.29_PM
----- Click here for text version of relevant logs!-----

Text output:

Started POST "/api/graphql" for 24.185.40.98 at 2020-06-02 18:14:07 -0400
  ApplicationSetting Load (8.5ms)  SELECT "application_settings".* FROM "application_settings" ORDER BY "application_settings"."id" DESC LIMIT 1
  ↳ app/models/concerns/cacheable_attributes.rb:19:in `current_without_cache'
  User Load (5.1ms)  SELECT "users".* FROM "users" WHERE "users"."id" = 1 ORDER BY "users"."id" ASC LIMIT 1
  ↳ ee/lib/gitlab/jira/middleware.rb:19:in `call'
  Feature::FlipperFeature Load (0.9ms)  SELECT "features".* FROM "features"
  ↳ lib/feature.rb:55:in `persisted_names'
Processing by GraphqlController#execute as */*
  Parameters: {"query"=>"# Welcome to GraphiQL\n#\n# GraphiQL is an in-browser tool for writing, validating, and\n# testing GraphQL queries.\n#\n# Type queries into this side of the screen, and you will see intelligent\n# typeaheads aware of the current GraphQL type schema and live syntax and\n# validation errors highlighted within the text.\n#\n# GraphQL queries typically start with a \"{\" character. Lines that starts\n# with a # are ignored.\n#\n# An example GraphQL query might look like:\n#\n#     {\n#       field(arg: \"value\") {\n#         subField\n#       }\n#     }\n#\n# Keyboard shortcuts:\n#\n#  Prettify Query:  Shift-Ctrl-P (or press the prettify button above)\n#\n#       Run Query:  Ctrl-Enter (or press the play button above)\n#\n#   Auto Complete:  Ctrl-Space (or just start typing)\n#\n\n\n# mutation ($projectPath: ID!, $status: AlertManagementStatus!, $iid: String!) {\n#   updateAlertStatus(input: { iid: $iid, status: $status, projectPath: $projectPath }) {\n#     errors\n#     alert {\n#       iid,\n#       status\n#     }\n#   }\n# }\n\n\n# {\n#   project(fullPath: \"root/autodevops\") {\n# \t\tid\n#     fullPath\n#     alertManagementAlerts(statuses: [TRIGGERED, RESOLVED]) {\n#       nodes {\n#         iid\n#         endedAt\n#         eventCount\n#         monitoringTool\n#         service\n#         severity\n#         startedAt\n#         status\n#         title\n#       }\n#     }\n#   }\n# }\n\n# {\n#   project(fullPath: \"root/autodevops\") {\n# \t\tid\n#     fullPath\n#     vulnerabilitySeveritiesCount {\n#       high\n#       low\n#       critical\n#       unknown\n#     }\n#     sentryErrors {\n#       errors{\n#         nodes {\n#           id\n#           sentryId\n#           sentryProjectName\n#         }\n        \n#       }\n#     }\n# \t\talertManagementAlertStatusCounts {\n#       acknowledged\n#       triggered\n#       open\n#       resolved\n#       all \n# \t\t}\n#   }\n# }\n\n# {\n#   namespace(fullPath: \"root\") {\n# \t\tprojects(includeSubgroups: true) {\n#       edges {\n#         node {\n#           id\n#         }\n#       }\n#     }\n#   }\n# }\n\n{\n  project(fullPath: \"root/autodevops\") {\n\t\tid\n    fullPath\n\t\talertManagementAlerts {\n      nodes {\n        iid\n\t\t\t\tstatus\n\t\t\t\tassignees {\n          username\n\t\t\t\t}\n      }\n    }\n  }\n}\n\n# {\n#   project(fullPath: \"root/autodevops\") {\n# \t\tid\n#     fullPath\n# \t\tmergeRequests {\n#       nodes {\n#         iid\n#         title\n# \t\t\t\tassignees {\n#           nodes {\n# \t          username\n#           }\n# \t\t\t\t}\n#       }\n#     }\n#   }\n# }\n\n# {\n#   project(fullPath: \"root/autodevops\") {\n# \t\tid\n#     fullPath\n# \t\talertManagementAlerts {\n#       nodes {\n#         iid\n# \t\t\t\tstatus\n# \t\t\t\tassignees {\n#           username\n# \t\t\t\t}\n#         notes {\n#           nodes {\n#             id\n#             system\n#             confidential\n#             body\n#           }\n#         }\n#         discussions {\n#           nodes {\n#             id\n#             notes {\n#               nodes {\n#                 id\n#                 body\n#               }\n#             }\n#           }\n#         }\n#       }\n#     }\n#     issues {\n#       nodes {\n#         iid\n#         description\n#         notes {\n#           nodes {\n#             id\n#             system\n#             confidential\n#             body\n#           }\n#         }\n#         discussions {\n#           nodes {\n#             id\n#             notes {\n#               nodes {\n#                 id\n#                 body\n#               }\n#             }\n#           }\n#         }\n#       }\n#     }\n#   }\n# }\n\n\n\n# mutation alertManagementSetAlertAssignees($input: SetAlertAssigneesInput!) {\n#   setAlertAssignees(input: $input) {\n#     errors\n#     alert {\n#       iid\n#       status\n#       title\n#       assignees {\n#         username\n#       }\n#       notes {\n#         nodes {\n#           author\n#           system\n#           body\n#         }\n#       }\n#     }\n#   }\n# }\n\n", "variables"=>"[FILTERED]", "operationName"=>nil, "graphql"=>{"query"=>"# Welcome to GraphiQL\n#\n# GraphiQL is an in-browser tool for writing, validating, and\n# testing GraphQL queries.\n#\n# Type queries into this side of the screen, and you will see intelligent\n# typeaheads aware of the current GraphQL type schema and live syntax and\n# validation errors highlighted within the text.\n#\n# GraphQL queries typically start with a \"{\" character. Lines that starts\n# with a # are ignored.\n#\n# An example GraphQL query might look like:\n#\n#     {\n#       field(arg: \"value\") {\n#         subField\n#       }\n#     }\n#\n# Keyboard shortcuts:\n#\n#  Prettify Query:  Shift-Ctrl-P (or press the prettify button above)\n#\n#       Run Query:  Ctrl-Enter (or press the play button above)\n#\n#   Auto Complete:  Ctrl-Space (or just start typing)\n#\n\n\n# mutation ($projectPath: ID!, $status: AlertManagementStatus!, $iid: String!) {\n#   updateAlertStatus(input: { iid: $iid, status: $status, projectPath: $projectPath }) {\n#     errors\n#     alert {\n#       iid,\n#       status\n#     }\n#   }\n# }\n\n\n# {\n#   project(fullPath: \"root/autodevops\") {\n# \t\tid\n#     fullPath\n#     alertManagementAlerts(statuses: [TRIGGERED, RESOLVED]) {\n#       nodes {\n#         iid\n#         endedAt\n#         eventCount\n#         monitoringTool\n#         service\n#         severity\n#         startedAt\n#         status\n#         title\n#       }\n#     }\n#   }\n# }\n\n# {\n#   project(fullPath: \"root/autodevops\") {\n# \t\tid\n#     fullPath\n#     vulnerabilitySeveritiesCount {\n#       high\n#       low\n#       critical\n#       unknown\n#     }\n#     sentryErrors {\n#       errors{\n#         nodes {\n#           id\n#           sentryId\n#           sentryProjectName\n#         }\n        \n#       }\n#     }\n# \t\talertManagementAlertStatusCounts {\n#       acknowledged\n#       triggered\n#       open\n#       resolved\n#       all \n# \t\t}\n#   }\n# }\n\n# {\n#   namespace(fullPath: \"root\") {\n# \t\tprojects(includeSubgroups: true) {\n#       edges {\n#         node {\n#           id\n#         }\n#       }\n#     }\n#   }\n# }\n\n{\n  project(fullPath: \"root/autodevops\") {\n\t\tid\n    fullPath\n\t\talertManagementAlerts {\n      nodes {\n        iid\n\t\t\t\tstatus\n\t\t\t\tassignees {\n          username\n\t\t\t\t}\n      }\n    }\n  }\n}\n\n# {\n#   project(fullPath: \"root/autodevops\") {\n# \t\tid\n#     fullPath\n# \t\tmergeRequests {\n#       nodes {\n#         iid\n#         title\n# \t\t\t\tassignees {\n#           nodes {\n# \t          username\n#           }\n# \t\t\t\t}\n#       }\n#     }\n#   }\n# }\n\n# {\n#   project(fullPath: \"root/autodevops\") {\n# \t\tid\n#     fullPath\n# \t\talertManagementAlerts {\n#       nodes {\n#         iid\n# \t\t\t\tstatus\n# \t\t\t\tassignees {\n#           username\n# \t\t\t\t}\n#         notes {\n#           nodes {\n#             id\n#             system\n#             confidential\n#             body\n#           }\n#         }\n#         discussions {\n#           nodes {\n#             id\n#             notes {\n#               nodes {\n#                 id\n#                 body\n#               }\n#             }\n#           }\n#         }\n#       }\n#     }\n#     issues {\n#       nodes {\n#         iid\n#         description\n#         notes {\n#           nodes {\n#             id\n#             system\n#             confidential\n#             body\n#           }\n#         }\n#         discussions {\n#           nodes {\n#             id\n#             notes {\n#               nodes {\n#                 id\n#                 body\n#               }\n#             }\n#           }\n#         }\n#       }\n#     }\n#   }\n# }\n\n\n\n# mutation alertManagementSetAlertAssignees($input: SetAlertAssigneesInput!) {\n#   setAlertAssignees(input: $input) {\n#     errors\n#     alert {\n#       iid\n#       status\n#       title\n#       assignees {\n#         username\n#       }\n#       notes {\n#         nodes {\n#           author\n#           system\n#           body\n#         }\n#       }\n#     }\n#   }\n# }\n\n", "variables"=>"[FILTERED]", "operationName"=>nil}}
  GeoNode Exists? (0.7ms)  SELECT 1 AS one FROM "geo_nodes" LIMIT 1
  ↳ ee/lib/gitlab/geo.rb:36:in `block in enabled?'
  SQL (5.2ms)  SELECT "projects"."id" AS t0_r0, "projects"."name" AS t0_r1, "projects"."path" AS t0_r2, "projects"."description" AS t0_r3, "projects"."created_at" AS t0_r4, "projects"."updated_at" AS t0_r5, "projects"."creator_id" AS t0_r6, "projects"."namespace_id" AS t0_r7, "projects"."last_activity_at" AS t0_r8, "projects"."import_url" AS t0_r9, "projects"."visibility_level" AS t0_r10, "projects"."archived" AS t0_r11, "projects"."avatar" AS t0_r12, "projects"."merge_requests_template" AS t0_r13, "projects"."star_count" AS t0_r14, "projects"."merge_requests_rebase_enabled" AS t0_r15, "projects"."import_type" AS t0_r16, "projects"."import_source" AS t0_r17, "projects"."approvals_before_merge" AS t0_r18, "projects"."reset_approvals_on_push" AS t0_r19, "projects"."merge_requests_ff_only_enabled" AS t0_r20, "projects"."issues_template" AS t0_r21, "projects"."mirror" AS t0_r22, "projects"."mirror_last_update_at" AS t0_r23, "projects"."mirror_last_successful_update_at" AS t0_r24, "projects"."mirror_user_id" AS t0_r25, "projects"."shared_runners_enabled" AS t0_r26, "projects"."runners_token" AS t0_r27, "projects"."build_coverage_regex" AS t0_r28, "projects"."build_allow_git_fetch" AS t0_r29, "projects"."build_timeout" AS t0_r30, "projects"."mirror_trigger_builds" AS t0_r31, "projects"."pending_delete" AS t0_r32, "projects"."public_builds" AS t0_r33, "projects"."last_repository_check_failed" AS t0_r34, "projects"."last_repository_check_at" AS t0_r35, "projects"."container_registry_enabled" AS t0_r36, "projects"."only_allow_merge_if_pipeline_succeeds" AS t0_r37, "projects"."has_external_issue_tracker" AS t0_r38, "projects"."repository_storage" AS t0_r39, "projects"."repository_read_only" AS t0_r40, "projects"."request_access_enabled" AS t0_r41, "projects"."has_external_wiki" AS t0_r42, "projects"."ci_config_path" AS t0_r43, "projects"."lfs_enabled" AS t0_r44, "projects"."description_html" AS t0_r45, "projects"."only_allow_merge_if_all_discussions_are_resolved" AS t0_r46, "projects"."repository_size_limit" AS t0_r47, "projects"."printing_merge_request_link_enabled" AS t0_r48, "projects"."auto_cancel_pending_pipelines" AS t0_r49, "projects"."service_desk_enabled" AS t0_r50, "projects"."cached_markdown_version" AS t0_r51, "projects"."delete_error" AS t0_r52, "projects"."last_repository_updated_at" AS t0_r53, "projects"."disable_overriding_approvers_per_merge_request" AS t0_r54, "projects"."storage_version" AS t0_r55, "projects"."resolve_outdated_diff_discussions" AS t0_r56, "projects"."remote_mirror_available_overridden" AS t0_r57, "projects"."only_mirror_protected_branches" AS t0_r58, "projects"."pull_mirror_available_overridden" AS t0_r59, "projects"."jobs_cache_index" AS t0_r60, "projects"."external_authorization_classification_label" AS t0_r61, "projects"."mirror_overwrites_diverged_branches" AS t0_r62, "projects"."pages_https_only" AS t0_r63, "projects"."external_webhook_token" AS t0_r64, "projects"."packages_enabled" AS t0_r65, "projects"."merge_requests_author_approval" AS t0_r66, "projects"."pool_repository_id" AS t0_r67, "projects"."runners_token_encrypted" AS t0_r68, "projects"."bfg_object_map" AS t0_r69, "projects"."detected_repository_languages" AS t0_r70, "projects"."merge_requests_disable_committers_approval" AS t0_r71, "projects"."require_password_to_approve" AS t0_r72, "projects"."emails_disabled" AS t0_r73, "projects"."max_pages_size" AS t0_r74, "projects"."max_artifacts_size" AS t0_r75, "projects"."pull_mirror_branch_prefix" AS t0_r76, "projects"."remove_source_branch_after_merge" AS t0_r77, "projects"."marked_for_deletion_at" AS t0_r78, "projects"."marked_for_deletion_by_user_id" AS t0_r79, "projects"."autoclose_referenced_issues" AS t0_r80, "projects"."suggestion_commit_message" AS t0_r81, "routes"."id" AS t1_r0, "routes"."source_id" AS t1_r1, "routes"."source_type" AS t1_r2, "routes"."path" AS t1_r3, "routes"."created_at" AS t1_r4, "routes"."updated_at" AS t1_r5, "routes"."name" AS t1_r6 FROM "projects" LEFT OUTER JOIN "routes" ON "routes"."source_type" = 'Project' AND "routes"."source_id" = "projects"."id" WHERE ((LOWER(routes.path) = LOWER('root/autodevops')))
  ↳ app/graphql/resolvers/full_path_resolver.rb:16:in `block in model_by_full_path'
  Group Load (1.7ms)  SELECT "namespaces".* FROM "namespaces" WHERE "namespaces"."type" = 'Group' AND "namespaces"."id" = 1 AND "namespaces"."type" = 'Group' LIMIT 1
  ↳ ee/app/policies/ee/project_policy.rb:344:in `block (2 levels) in <module:ProjectPolicy>'
  AlertManagement::Alert Load (1.0ms)  SELECT "alert_management_alerts".* FROM "alert_management_alerts" WHERE "alert_management_alerts"."project_id" = 21 ORDER BY "alert_management_alerts"."id" DESC LIMIT 100
  ↳ lib/gitlab/graphql/pagination/keyset/connection.rb:100:in `nodes'
  AlertManagement::AlertAssignee Load (1.5ms)  SELECT "alert_management_alert_assignees".* FROM "alert_management_alert_assignees" WHERE "alert_management_alert_assignees"."alert_id" IN (5, 4, 3, 2, 1)
  ↳ lib/gitlab/graphql/loaders/alert_management/alerts/assignees_loader.rb:31:in `load_assignees'
  User Load (1.2ms)  SELECT "users".* FROM "users" WHERE "users"."id" IN (12, 15)
  ↳ lib/gitlab/graphql/loaders/alert_management/alerts/assignees_loader.rb:31:in `load_assignees'
Completed 200 OK in 186ms (Views: 0.4ms | ActiveRecord: 17.8ms | Elasticsearch: 0.0ms | Allocations: 138881)


  License Load (0.5ms)  SELECT "licenses".* FROM "licenses" ORDER BY "licenses"."id" DESC LIMIT 100
  ↳ ee/app/models/license.rb:265:in `load_license'

Application testing

Alert list view Log output
Screen_Shot_2020-06-02_at_6.28.09_PM Screen_Shot_2020-06-02_at_6.29.33_PM
----- Click here for text version of relevant logs!-----

Text output:

Started POST "/api/graphql" for 24.185.40.98 at 2020-06-02 18:28:00 -0400
  User Load (0.5ms)  SELECT "users".* FROM "users" WHERE "users"."id" = 1 ORDER BY "users"."id" ASC LIMIT 1
  ↳ ee/lib/gitlab/jira/middleware.rb:19:in `call'
Processing by Peek::ResultsController#show as JSON
  Parameters: {"request_id"=>"KTB1gIQWBO3"}
Completed 200 OK in 11ms (Views: 0.1ms | ActiveRecord: 0.0ms | Elasticsearch: 0.0ms | Allocations: 21275)


  License Load (0.3ms)  SELECT "licenses".* FROM "licenses" ORDER BY "licenses"."id" DESC LIMIT 100
  ↳ ee/app/models/license.rb:265:in `load_license'
  User Load (0.8ms)  SELECT "users".* FROM "users" WHERE "users"."id" = 1 ORDER BY "users"."id" ASC LIMIT 1
  ↳ ee/lib/gitlab/jira/middleware.rb:19:in `call'
Processing by GraphqlController#execute as */*
  Parameters: {"_json"=>[{"operationName"=>"getAlerts", "variables"=>"[FILTERED]", "query"=>"query getAlerts($projectPath: ID!, $statuses: [AlertManagementStatus!], $sort: AlertManagementAlertSort, $firstPageSize: Int, $lastPageSize: Int, $prevPageCursor: String = \"\", $nextPageCursor: String = \"\") {\n  project(fullPath: $projectPath) {\n    alertManagementAlerts(statuses: $statuses, sort: $sort, first: $firstPageSize, last: $lastPageSize, after: $nextPageCursor, before: $prevPageCursor) {\n      nodes {\n        ...AlertListItem\n        __typename\n      }\n      pageInfo {\n        hasNextPage\n        endCursor\n        hasPreviousPage\n        startCursor\n        __typename\n      }\n      __typename\n    }\n    __typename\n  }\n}\n\nfragment AlertListItem on AlertManagementAlert {\n  iid\n  title\n  severity\n  status\n  startedAt\n  endedAt\n  eventCount\n  issueIid\n  assignees {\n    username\n    __typename\n  }\n  __typename\n}\n"}, {"operationName"=>"getAlertsCount", "variables"=>"[FILTERED]", "query"=>"query getAlertsCount($projectPath: ID!) {\n  project(fullPath: $projectPath) {\n    alertManagementAlertStatusCounts {\n      all\n      open\n      acknowledged\n      resolved\n      triggered\n      __typename\n    }\n    __typename\n  }\n}\n"}, {"operationName"=>"getAlerts", "variables"=>"[FILTERED]", "query"=>"query getAlerts($projectPath: ID!, $statuses: [AlertManagementStatus!], $sort: AlertManagementAlertSort, $firstPageSize: Int, $lastPageSize: Int, $prevPageCursor: String = \"\", $nextPageCursor: String = \"\") {\n  project(fullPath: $projectPath) {\n    alertManagementAlerts(statuses: $statuses, sort: $sort, first: $firstPageSize, last: $lastPageSize, after: $nextPageCursor, before: $prevPageCursor) {\n      nodes {\n        ...AlertListItem\n        __typename\n      }\n      pageInfo {\n        hasNextPage\n        endCursor\n        hasPreviousPage\n        startCursor\n        __typename\n      }\n      __typename\n    }\n    __typename\n  }\n}\n\nfragment AlertListItem on AlertManagementAlert {\n  iid\n  title\n  severity\n  status\n  startedAt\n  endedAt\n  eventCount\n  issueIid\n  assignees {\n    username\n    __typename\n  }\n  __typename\n}\n"}], "graphql"=>{"_json"=>[{"operationName"=>"getAlerts", "variables"=>"[FILTERED]", "query"=>"query getAlerts($projectPath: ID!, $statuses: [AlertManagementStatus!], $sort: AlertManagementAlertSort, $firstPageSize: Int, $lastPageSize: Int, $prevPageCursor: String = \"\", $nextPageCursor: String = \"\") {\n  project(fullPath: $projectPath) {\n    alertManagementAlerts(statuses: $statuses, sort: $sort, first: $firstPageSize, last: $lastPageSize, after: $nextPageCursor, before: $prevPageCursor) {\n      nodes {\n        ...AlertListItem\n        __typename\n      }\n      pageInfo {\n        hasNextPage\n        endCursor\n        hasPreviousPage\n        startCursor\n        __typename\n      }\n      __typename\n    }\n    __typename\n  }\n}\n\nfragment AlertListItem on AlertManagementAlert {\n  iid\n  title\n  severity\n  status\n  startedAt\n  endedAt\n  eventCount\n  issueIid\n  assignees {\n    username\n    __typename\n  }\n  __typename\n}\n"}, {"operationName"=>"getAlertsCount", "variables"=>"[FILTERED]", "query"=>"query getAlertsCount($projectPath: ID!) {\n  project(fullPath: $projectPath) {\n    alertManagementAlertStatusCounts {\n      all\n      open\n      acknowledged\n      resolved\n      triggered\n      __typename\n    }\n    __typename\n  }\n}\n"}, {"operationName"=>"getAlerts", "variables"=>"[FILTERED]", "query"=>"query getAlerts($projectPath: ID!, $statuses: [AlertManagementStatus!], $sort: AlertManagementAlertSort, $firstPageSize: Int, $lastPageSize: Int, $prevPageCursor: String = \"\", $nextPageCursor: String = \"\") {\n  project(fullPath: $projectPath) {\n    alertManagementAlerts(statuses: $statuses, sort: $sort, first: $firstPageSize, last: $lastPageSize, after: $nextPageCursor, before: $prevPageCursor) {\n      nodes {\n        ...AlertListItem\n        __typename\n      }\n      pageInfo {\n        hasNextPage\n        endCursor\n        hasPreviousPage\n        startCursor\n        __typename\n      }\n      __typename\n    }\n    __typename\n  }\n}\n\nfragment AlertListItem on AlertManagementAlert {\n  iid\n  title\n  severity\n  status\n  startedAt\n  endedAt\n  eventCount\n  issueIid\n  assignees {\n    username\n    __typename\n  }\n  __typename\n}\n"}]}}
  SQL (2.4ms)  SELECT "projects"."id" AS t0_r0, "projects"."name" AS t0_r1, "projects"."path" AS t0_r2, "projects"."description" AS t0_r3, "projects"."created_at" AS t0_r4, "projects"."updated_at" AS t0_r5, "projects"."creator_id" AS t0_r6, "projects"."namespace_id" AS t0_r7, "projects"."last_activity_at" AS t0_r8, "projects"."import_url" AS t0_r9, "projects"."visibility_level" AS t0_r10, "projects"."archived" AS t0_r11, "projects"."avatar" AS t0_r12, "projects"."merge_requests_template" AS t0_r13, "projects"."star_count" AS t0_r14, "projects"."merge_requests_rebase_enabled" AS t0_r15, "projects"."import_type" AS t0_r16, "projects"."import_source" AS t0_r17, "projects"."approvals_before_merge" AS t0_r18, "projects"."reset_approvals_on_push" AS t0_r19, "projects"."merge_requests_ff_only_enabled" AS t0_r20, "projects"."issues_template" AS t0_r21, "projects"."mirror" AS t0_r22, "projects"."mirror_last_update_at" AS t0_r23, "projects"."mirror_last_successful_update_at" AS t0_r24, "projects"."mirror_user_id" AS t0_r25, "projects"."shared_runners_enabled" AS t0_r26, "projects"."runners_token" AS t0_r27, "projects"."build_coverage_regex" AS t0_r28, "projects"."build_allow_git_fetch" AS t0_r29, "projects"."build_timeout" AS t0_r30, "projects"."mirror_trigger_builds" AS t0_r31, "projects"."pending_delete" AS t0_r32, "projects"."public_builds" AS t0_r33, "projects"."last_repository_check_failed" AS t0_r34, "projects"."last_repository_check_at" AS t0_r35, "projects"."container_registry_enabled" AS t0_r36, "projects"."only_allow_merge_if_pipeline_succeeds" AS t0_r37, "projects"."has_external_issue_tracker" AS t0_r38, "projects"."repository_storage" AS t0_r39, "projects"."repository_read_only" AS t0_r40, "projects"."request_access_enabled" AS t0_r41, "projects"."has_external_wiki" AS t0_r42, "projects"."ci_config_path" AS t0_r43, "projects"."lfs_enabled" AS t0_r44, "projects"."description_html" AS t0_r45, "projects"."only_allow_merge_if_all_discussions_are_resolved" AS t0_r46, "projects"."repository_size_limit" AS t0_r47, "projects"."printing_merge_request_link_enabled" AS t0_r48, "projects"."auto_cancel_pending_pipelines" AS t0_r49, "projects"."service_desk_enabled" AS t0_r50, "projects"."cached_markdown_version" AS t0_r51, "projects"."delete_error" AS t0_r52, "projects"."last_repository_updated_at" AS t0_r53, "projects"."disable_overriding_approvers_per_merge_request" AS t0_r54, "projects"."storage_version" AS t0_r55, "projects"."resolve_outdated_diff_discussions" AS t0_r56, "projects"."remote_mirror_available_overridden" AS t0_r57, "projects"."only_mirror_protected_branches" AS t0_r58, "projects"."pull_mirror_available_overridden" AS t0_r59, "projects"."jobs_cache_index" AS t0_r60, "projects"."external_authorization_classification_label" AS t0_r61, "projects"."mirror_overwrites_diverged_branches" AS t0_r62, "projects"."pages_https_only" AS t0_r63, "projects"."external_webhook_token" AS t0_r64, "projects"."packages_enabled" AS t0_r65, "projects"."merge_requests_author_approval" AS t0_r66, "projects"."pool_repository_id" AS t0_r67, "projects"."runners_token_encrypted" AS t0_r68, "projects"."bfg_object_map" AS t0_r69, "projects"."detected_repository_languages" AS t0_r70, "projects"."merge_requests_disable_committers_approval" AS t0_r71, "projects"."require_password_to_approve" AS t0_r72, "projects"."emails_disabled" AS t0_r73, "projects"."max_pages_size" AS t0_r74, "projects"."max_artifacts_size" AS t0_r75, "projects"."pull_mirror_branch_prefix" AS t0_r76, "projects"."remove_source_branch_after_merge" AS t0_r77, "projects"."marked_for_deletion_at" AS t0_r78, "projects"."marked_for_deletion_by_user_id" AS t0_r79, "projects"."autoclose_referenced_issues" AS t0_r80, "projects"."suggestion_commit_message" AS t0_r81, "routes"."id" AS t1_r0, "routes"."source_id" AS t1_r1, "routes"."source_type" AS t1_r2, "routes"."path" AS t1_r3, "routes"."created_at" AS t1_r4, "routes"."updated_at" AS t1_r5, "routes"."name" AS t1_r6 FROM "projects" LEFT OUTER JOIN "routes" ON "routes"."source_type" = 'Project' AND "routes"."source_id" = "projects"."id" WHERE ((LOWER(routes.path) = LOWER('root/autodevops')))
  ↳ app/graphql/resolvers/full_path_resolver.rb:16:in `block in model_by_full_path'
  Group Load (0.5ms)  SELECT "namespaces".* FROM "namespaces" WHERE "namespaces"."type" = 'Group' AND "namespaces"."id" = 1 AND "namespaces"."type" = 'Group' LIMIT 1
  ↳ ee/app/policies/ee/project_policy.rb:344:in `block (2 levels) in <module:ProjectPolicy>'
  AlertManagement::Alert Load (1.1ms)  SELECT "alert_management_alerts".* FROM "alert_management_alerts" WHERE "alert_management_alerts"."project_id" = 21 ORDER BY "alert_management_alerts"."started_at" ASC, "alert_management_alerts"."id" DESC LIMIT 10
  ↳ lib/gitlab/graphql/pagination/keyset/connection.rb:100:in `nodes'
   (0.4ms)  SELECT COUNT(*) FROM (SELECT 1 AS one FROM "alert_management_alerts" WHERE "alert_management_alerts"."project_id" = 21 LIMIT 11) subquery_for_count
  ↳ lib/gitlab/graphql/pagination/keyset/connection.rb:70:in `block in has_next_page'
   (0.4ms)  SELECT COUNT(*) AS count_all, "alert_management_alerts"."status" AS alert_management_alerts_status FROM "alert_management_alerts" WHERE "alert_management_alerts"."project_id" = 21 GROUP BY "alert_management_alerts"."status"
  ↳ app/models/alert_management/alert.rb:113:in `block in <class:Alert>'
  AlertManagement::Alert Load (0.4ms)  SELECT "alert_management_alerts".* FROM "alert_management_alerts" WHERE "alert_management_alerts"."project_id" = 21 AND "alert_management_alerts"."status" IN (0, 1) ORDER BY "alert_management_alerts"."started_at" ASC, "alert_management_alerts"."id" DESC LIMIT 10
  ↳ lib/gitlab/graphql/pagination/keyset/connection.rb:100:in `nodes'
   (0.4ms)  SELECT COUNT(*) FROM (SELECT 1 AS one FROM "alert_management_alerts" WHERE "alert_management_alerts"."project_id" = 21 AND "alert_management_alerts"."status" IN (0, 1) LIMIT 11) subquery_for_count
  ↳ lib/gitlab/graphql/pagination/keyset/connection.rb:70:in `block in has_next_page'
  AlertManagement::AlertAssignee Load (1.0ms)  SELECT "alert_management_alert_assignees".* FROM "alert_management_alert_assignees" WHERE "alert_management_alert_assignees"."alert_id" IN (1, 2, 3, 4, 5)
  ↳ lib/gitlab/graphql/loaders/alert_management/alerts/assignees_loader.rb:31:in `load_assignees'
  User Load (51.0ms)  SELECT "users".* FROM "users" WHERE "users"."id" IN (12, 15)
  ↳ lib/gitlab/graphql/loaders/alert_management/alerts/assignees_loader.rb:31:in `load_assignees'
Completed 200 OK in 421ms (Views: 1.0ms | ActiveRecord: 57.7ms | Elasticsearch: 0.0ms | Allocations: 247192)


  License Load (0.8ms)  SELECT "licenses".* FROM "licenses" ORDER BY "licenses"."id" DESC LIMIT 100
  ↳ ee/app/models/license.rb:265:in `load_license'

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 Rémy Coutable

Merge request reports