Skip to content

Delete escalation rules and notify project owners on user removal

Sarah Yasonik requested to merge sy-delete-esc-rules-on-user-change into master

What does this MR do?

Related issue: #335546 (closed)

This introduces logic to:

  1. Notify project owners when a user is removed from an escalation policy via an administrative action (user is deleted or removed from project/group)
    • One email is sent to each of the project's owners for each project which has escalation rules that directly notify the deleted/removed user. Separate emails are sent for any on-call schedules the user is a part of.
  2. Delete direct-to-user escalation rules on user deletion/removal to avoid sending alert notifications to users without permissions

Query performance

Production records:

  • Rules: ~ 200 (~ 1 is a user-based rule)
  • Policies: ~ 200 (we do not expect growth to be 1:1 with rules)
  • Projects/Users/Routes: Manyyyy (~ 21 mil / 10 mil / 34 mil)

Strategy:

Even with quite a bit of growth & adoption, on-call schedules/escalation rules/escalation policies won't see the scale of our more heavily used tables. User deletion/removals also take place asynchronously, so users won't ultimately see the exact impact of the queries used here. To best utilize the existing optimizations, queries added in this MR are tuned to load the needed records from the incident management tables separately from the heavily used tables.

As a result, the project/routes queries primarily utilize index scans for lookups by id. The escalation rules queries also include the escalation policy to avoid N+1s, and these generally utilize Hash Joins w/ index scans. There are also a couple sequential scans or nested loops used as well, but this is largely in scenarios where there are small number of records at play and performance is still well within reasonable.

Production-like rules data 50X growth
Data composition
  • 238 total rules
  • 109 user rules
  • 129 schedule rules
  • 24 projects
  • 24 policies
  • 5 groups
  • 64 users
  • 27 group members
  • 113 project members
  • 12152 total rules
  • 5779 user rules
  • 6373 schedule rules
  • 1271 projects
  • 1271 policies
  • 255 groups
  • 1311 users
  • 1432 group members
  • 5982 project members
  • rules for group member removal
  • Source code
        collector.call do
          Members::DestroyService.new.send(:cleanup_escalation_rules, member) if member.user
        end
    
incident_management_escalation_rules SELECT query
  • Execution Time: 0.048 ms
  • Visualization: https://explain.depesz.com/s/8puL
  • SQL
    SELECT "incident_management_escalation_rules"."id" AS t0_r0, 
      "incident_management_escalation_rules"."policy_id" AS t0_r1, 
      "incident_management_escalation_rules"."oncall_schedule_id" AS t0_r2, 
      "incident_management_escalation_rules"."status" AS t0_r3, 
      "incident_management_escalation_rules"."elapsed_time_seconds" AS t0_r4, 
      "incident_management_escalation_rules"."is_removed" AS t0_r5, 
      "incident_management_escalation_rules"."user_id" AS t0_r6, 
      "policy"."id" AS t1_r0, 
      "policy"."project_id" AS t1_r1, 
      "policy"."name" AS t1_r2, 
      "policy"."description" AS t1_r3 
    FROM "incident_management_escalation_rules" 
    INNER JOIN "incident_management_escalation_policies" "policy" 
      ON "policy"."id" = "incident_management_escalation_rules"."policy_id" 
    WHERE "policy"."project_id" IN (
    SELECT "projects"."id" 
    FROM "projects" 
    WHERE "projects"."namespace_id" = 3196
    ) 
    AND "incident_management_escalation_rules"."user_id" = 1786;
projects SELECT query
  • Execution Time: 0.029 ms
  • Visualization: https://explain.depesz.com/s/kKoe
  • SQL
    SELECT "projects"."id", 
      "projects"."name", 
      "projects"."path", 
      "projects"."description", 
      "projects"."created_at", 
      "projects"."updated_at", 
      "projects"."creator_id", 
      "projects"."namespace_id", 
      "projects"."last_activity_at", 
      "projects"."import_url", 
      "projects"."visibility_level", 
      "projects"."archived", 
      "projects"."avatar", 
      "projects"."merge_requests_template", 
      "projects"."star_count", 
      "projects"."merge_requests_rebase_enabled", 
      "projects"."import_type", 
      "projects"."import_source", 
      "projects"."approvals_before_merge", 
      "projects"."reset_approvals_on_push", 
      "projects"."merge_requests_ff_only_enabled", 
      "projects"."issues_template", 
      "projects"."mirror", 
      "projects"."mirror_user_id", 
      "projects"."shared_runners_enabled", 
      "projects"."runners_token", 
      "projects"."build_coverage_regex", 
      "projects"."build_allow_git_fetch", 
      "projects"."build_timeout", 
      "projects"."mirror_trigger_builds", 
      "projects"."pending_delete", 
      "projects"."public_builds", 
      "projects"."last_repository_check_failed", 
      "projects"."last_repository_check_at", 
      "projects"."only_allow_merge_if_pipeline_succeeds", 
      "projects"."has_external_issue_tracker", 
      "projects"."repository_storage", 
      "projects"."repository_read_only", 
      "projects"."request_access_enabled", 
      "projects"."has_external_wiki", 
      "projects"."ci_config_path", 
      "projects"."lfs_enabled", 
      "projects"."description_html", 
      "projects"."only_allow_merge_if_all_discussions_are_resolved", 
      "projects"."repository_size_limit", 
      "projects"."printing_merge_request_link_enabled", 
      "projects"."auto_cancel_pending_pipelines", 
      "projects"."service_desk_enabled", 
      "projects"."cached_markdown_version", 
      "projects"."delete_error", 
      "projects"."last_repository_updated_at", 
      "projects"."disable_overriding_approvers_per_merge_request", 
      "projects"."storage_version", 
      "projects"."resolve_outdated_diff_discussions", 
      "projects"."remote_mirror_available_overridden", 
      "projects"."only_mirror_protected_branches", 
      "projects"."pull_mirror_available_overridden", 
      "projects"."jobs_cache_index", 
      "projects"."external_authorization_classification_label", 
      "projects"."mirror_overwrites_diverged_branches", 
      "projects"."pages_https_only", 
      "projects"."external_webhook_token", 
      "projects"."packages_enabled", 
      "projects"."merge_requests_author_approval", 
      "projects"."pool_repository_id", 
      "projects"."runners_token_encrypted", 
      "projects"."bfg_object_map", 
      "projects"."detected_repository_languages", 
      "projects"."merge_requests_disable_committers_approval", 
      "projects"."require_password_to_approve", 
      "projects"."emails_disabled", 
      "projects"."max_pages_size", 
      "projects"."max_artifacts_size", 
      "projects"."remove_source_branch_after_merge", 
      "projects"."marked_for_deletion_at", 
      "projects"."marked_for_deletion_by_user_id", 
      "projects"."autoclose_referenced_issues", 
      "projects"."suggestion_commit_message", 
      "projects"."project_namespace_id" 
    FROM "projects" 
    WHERE "projects"."id" = 1156;
routes SELECT query
  • Avg: ~0.017 ms / query
  • Sum: ~0.034 ms
  • Vis: https://explain.depesz.com/s/v6xl
  • SQL
    SELECT "routes".* 
    FROM "routes" 
    WHERE "routes"."source_type" = 'Project' 
    AND "routes"."source_id" = 1156;
  • All execution times (2)
    [0.019, 0.015]
incident_management_escalation_rules DELETE query
  • Execution Time: 0.02 ms
  • Visualization: https://explain.depesz.com/s/xJXe
  • SQL
    DELETE 
    FROM "incident_management_escalation_rules" 
    WHERE "incident_management_escalation_rules"."id" = 11122;
incident_management_escalation_rules SELECT query
  • Execution Time: 0.039 ms
  • Visualization: https://explain.depesz.com/s/CKnp
  • SQL
    SELECT "incident_management_escalation_rules"."id" AS t0_r0, 
      "incident_management_escalation_rules"."policy_id" AS t0_r1, 
      "incident_management_escalation_rules"."oncall_schedule_id" AS t0_r2, 
      "incident_management_escalation_rules"."status" AS t0_r3, 
      "incident_management_escalation_rules"."elapsed_time_seconds" AS t0_r4, 
      "incident_management_escalation_rules"."is_removed" AS t0_r5, 
      "incident_management_escalation_rules"."user_id" AS t0_r6, 
      "policy"."id" AS t1_r0, 
      "policy"."project_id" AS t1_r1, 
      "policy"."name" AS t1_r2, 
      "policy"."description" AS t1_r3 
    FROM "incident_management_escalation_rules" 
    INNER JOIN "incident_management_escalation_policies" "policy" 
      ON "policy"."id" = "incident_management_escalation_rules"."policy_id" 
    WHERE "policy"."project_id" IN (
    SELECT "projects"."id" 
    FROM "projects" 
    WHERE "projects"."namespace_id" = 4991
    ) 
    AND "incident_management_escalation_rules"."user_id" = 1768;
projects SELECT query
  • Execution Time: 0.03 ms
  • Visualization: https://explain.depesz.com/s/iXzl
  • SQL
    SELECT "projects"."id", 
      "projects"."name", 
      "projects"."path", 
      "projects"."description", 
      "projects"."created_at", 
      "projects"."updated_at", 
      "projects"."creator_id", 
      "projects"."namespace_id", 
      "projects"."last_activity_at", 
      "projects"."import_url", 
      "projects"."visibility_level", 
      "projects"."archived", 
      "projects"."avatar", 
      "projects"."merge_requests_template", 
      "projects"."star_count", 
      "projects"."merge_requests_rebase_enabled", 
      "projects"."import_type", 
      "projects"."import_source", 
      "projects"."approvals_before_merge", 
      "projects"."reset_approvals_on_push", 
      "projects"."merge_requests_ff_only_enabled", 
      "projects"."issues_template", 
      "projects"."mirror", 
      "projects"."mirror_user_id", 
      "projects"."shared_runners_enabled", 
      "projects"."runners_token", 
      "projects"."build_coverage_regex", 
      "projects"."build_allow_git_fetch", 
      "projects"."build_timeout", 
      "projects"."mirror_trigger_builds", 
      "projects"."pending_delete", 
      "projects"."public_builds", 
      "projects"."last_repository_check_failed", 
      "projects"."last_repository_check_at", 
      "projects"."only_allow_merge_if_pipeline_succeeds", 
      "projects"."has_external_issue_tracker", 
      "projects"."repository_storage", 
      "projects"."repository_read_only", 
      "projects"."request_access_enabled", 
      "projects"."has_external_wiki", 
      "projects"."ci_config_path", 
      "projects"."lfs_enabled", 
      "projects"."description_html", 
      "projects"."only_allow_merge_if_all_discussions_are_resolved", 
      "projects"."repository_size_limit", 
      "projects"."printing_merge_request_link_enabled", 
      "projects"."auto_cancel_pending_pipelines", 
      "projects"."service_desk_enabled", 
      "projects"."cached_markdown_version", 
      "projects"."delete_error", 
      "projects"."last_repository_updated_at", 
      "projects"."disable_overriding_approvers_per_merge_request", 
      "projects"."storage_version", 
      "projects"."resolve_outdated_diff_discussions", 
      "projects"."remote_mirror_available_overridden", 
      "projects"."only_mirror_protected_branches", 
      "projects"."pull_mirror_available_overridden", 
      "projects"."jobs_cache_index", 
      "projects"."external_authorization_classification_label", 
      "projects"."mirror_overwrites_diverged_branches", 
      "projects"."pages_https_only", 
      "projects"."external_webhook_token", 
      "projects"."packages_enabled", 
      "projects"."merge_requests_author_approval", 
      "projects"."pool_repository_id", 
      "projects"."runners_token_encrypted", 
      "projects"."bfg_object_map", 
      "projects"."detected_repository_languages", 
      "projects"."merge_requests_disable_committers_approval", 
      "projects"."require_password_to_approve", 
      "projects"."emails_disabled", 
      "projects"."max_pages_size", 
      "projects"."max_artifacts_size", 
      "projects"."remove_source_branch_after_merge", 
      "projects"."marked_for_deletion_at", 
      "projects"."marked_for_deletion_by_user_id", 
      "projects"."autoclose_referenced_issues", 
      "projects"."suggestion_commit_message", 
      "projects"."project_namespace_id" 
    FROM "projects" 
    WHERE "projects"."id" = 1972;
routes SELECT query
  • Avg: ~0.017 ms / query
  • Sum: ~0.033 ms
  • Vis: https://explain.depesz.com/s/pfZT
  • SQL
    SELECT "routes".* 
    FROM "routes" 
    WHERE "routes"."source_type" = 'Project' 
    AND "routes"."source_id" = 1972;
  • All execution times (2)
    [0.016, 0.017]
incident_management_escalation_rules DELETE query
  • Execution Time: 0.021 ms
  • Visualization: https://explain.depesz.com/s/aSW3
  • SQL
    DELETE 
    FROM "incident_management_escalation_rules" 
    WHERE "incident_management_escalation_rules"."id" IN (
    18923, 18924, 18925
    );
  • rules for project member removal
  • Source code
        collector.call do
          Members::DestroyService.new.send(:cleanup_escalation_rules, member) if member.user
        end
    
projects SELECT query
  • Execution Time: 0.035 ms
  • Visualization: https://explain.depesz.com/s/5iJh
  • SQL
    SELECT "projects"."id", 
      "projects"."name", 
      "projects"."path", 
      "projects"."description", 
      "projects"."created_at", 
      "projects"."updated_at", 
      "projects"."creator_id", 
      "projects"."namespace_id", 
      "projects"."last_activity_at", 
      "projects"."import_url", 
      "projects"."visibility_level", 
      "projects"."archived", 
      "projects"."avatar", 
      "projects"."merge_requests_template", 
      "projects"."star_count", 
      "projects"."merge_requests_rebase_enabled", 
      "projects"."import_type", 
      "projects"."import_source", 
      "projects"."approvals_before_merge", 
      "projects"."reset_approvals_on_push", 
      "projects"."merge_requests_ff_only_enabled", 
      "projects"."issues_template", 
      "projects"."mirror", 
      "projects"."mirror_user_id", 
      "projects"."shared_runners_enabled", 
      "projects"."runners_token", 
      "projects"."build_coverage_regex", 
      "projects"."build_allow_git_fetch", 
      "projects"."build_timeout", 
      "projects"."mirror_trigger_builds", 
      "projects"."pending_delete", 
      "projects"."public_builds", 
      "projects"."last_repository_check_failed", 
      "projects"."last_repository_check_at", 
      "projects"."only_allow_merge_if_pipeline_succeeds", 
      "projects"."has_external_issue_tracker", 
      "projects"."repository_storage", 
      "projects"."repository_read_only", 
      "projects"."request_access_enabled", 
      "projects"."has_external_wiki", 
      "projects"."ci_config_path", 
      "projects"."lfs_enabled", 
      "projects"."description_html", 
      "projects"."only_allow_merge_if_all_discussions_are_resolved", 
      "projects"."repository_size_limit", 
      "projects"."printing_merge_request_link_enabled", 
      "projects"."auto_cancel_pending_pipelines", 
      "projects"."service_desk_enabled", 
      "projects"."cached_markdown_version", 
      "projects"."delete_error", 
      "projects"."last_repository_updated_at", 
      "projects"."disable_overriding_approvers_per_merge_request", 
      "projects"."storage_version", 
      "projects"."resolve_outdated_diff_discussions", 
      "projects"."remote_mirror_available_overridden", 
      "projects"."only_mirror_protected_branches", 
      "projects"."pull_mirror_available_overridden", 
      "projects"."jobs_cache_index", 
      "projects"."external_authorization_classification_label", 
      "projects"."mirror_overwrites_diverged_branches", 
      "projects"."pages_https_only", 
      "projects"."external_webhook_token", 
      "projects"."packages_enabled", 
      "projects"."merge_requests_author_approval", 
      "projects"."pool_repository_id", 
      "projects"."runners_token_encrypted", 
      "projects"."bfg_object_map", 
      "projects"."detected_repository_languages", 
      "projects"."merge_requests_disable_committers_approval", 
      "projects"."require_password_to_approve", 
      "projects"."emails_disabled", 
      "projects"."max_pages_size", 
      "projects"."max_artifacts_size", 
      "projects"."remove_source_branch_after_merge", 
      "projects"."marked_for_deletion_at", 
      "projects"."marked_for_deletion_by_user_id", 
      "projects"."autoclose_referenced_issues", 
      "projects"."suggestion_commit_message", 
      "projects"."project_namespace_id" 
    FROM "projects" 
    WHERE "projects"."id" = 1148 
    LIMIT 1;
incident_management_escalation_rules SELECT query
  • Execution Time: 0.031 ms
  • Visualization: https://explain.depesz.com/s/kYXe
  • SQL
    SELECT "incident_management_escalation_rules"."id" AS t0_r0, 
      "incident_management_escalation_rules"."policy_id" AS t0_r1, 
      "incident_management_escalation_rules"."oncall_schedule_id" AS t0_r2, 
      "incident_management_escalation_rules"."status" AS t0_r3, 
      "incident_management_escalation_rules"."elapsed_time_seconds" AS t0_r4, 
      "incident_management_escalation_rules"."is_removed" AS t0_r5, 
      "incident_management_escalation_rules"."user_id" AS t0_r6, 
      "policy"."id" AS t1_r0, 
      "policy"."project_id" AS t1_r1, 
      "policy"."name" AS t1_r2, 
      "policy"."description" AS t1_r3 
    FROM "incident_management_escalation_rules" 
    INNER JOIN "incident_management_escalation_policies" "policy" 
      ON "policy"."id" = "incident_management_escalation_rules"."policy_id" 
    WHERE "policy"."project_id" = 1148 
    AND "incident_management_escalation_rules"."user_id" = 1780;
projects SELECT query 2
  • Execution Time: 0.028 ms
  • Visualization: https://explain.depesz.com/s/M4UE
  • SQL
    SELECT "projects"."id", 
      "projects"."name", 
      "projects"."path", 
      "projects"."description", 
      "projects"."created_at", 
      "projects"."updated_at", 
      "projects"."creator_id", 
      "projects"."namespace_id", 
      "projects"."last_activity_at", 
      "projects"."import_url", 
      "projects"."visibility_level", 
      "projects"."archived", 
      "projects"."avatar", 
      "projects"."merge_requests_template", 
      "projects"."star_count", 
      "projects"."merge_requests_rebase_enabled", 
      "projects"."import_type", 
      "projects"."import_source", 
      "projects"."approvals_before_merge", 
      "projects"."reset_approvals_on_push", 
      "projects"."merge_requests_ff_only_enabled", 
      "projects"."issues_template", 
      "projects"."mirror", 
      "projects"."mirror_user_id", 
      "projects"."shared_runners_enabled", 
      "projects"."runners_token", 
      "projects"."build_coverage_regex", 
      "projects"."build_allow_git_fetch", 
      "projects"."build_timeout", 
      "projects"."mirror_trigger_builds", 
      "projects"."pending_delete", 
      "projects"."public_builds", 
      "projects"."last_repository_check_failed", 
      "projects"."last_repository_check_at", 
      "projects"."only_allow_merge_if_pipeline_succeeds", 
      "projects"."has_external_issue_tracker", 
      "projects"."repository_storage", 
      "projects"."repository_read_only", 
      "projects"."request_access_enabled", 
      "projects"."has_external_wiki", 
      "projects"."ci_config_path", 
      "projects"."lfs_enabled", 
      "projects"."description_html", 
      "projects"."only_allow_merge_if_all_discussions_are_resolved", 
      "projects"."repository_size_limit", 
      "projects"."printing_merge_request_link_enabled", 
      "projects"."auto_cancel_pending_pipelines", 
      "projects"."service_desk_enabled", 
      "projects"."cached_markdown_version", 
      "projects"."delete_error", 
      "projects"."last_repository_updated_at", 
      "projects"."disable_overriding_approvers_per_merge_request", 
      "projects"."storage_version", 
      "projects"."resolve_outdated_diff_discussions", 
      "projects"."remote_mirror_available_overridden", 
      "projects"."only_mirror_protected_branches", 
      "projects"."pull_mirror_available_overridden", 
      "projects"."jobs_cache_index", 
      "projects"."external_authorization_classification_label", 
      "projects"."mirror_overwrites_diverged_branches", 
      "projects"."pages_https_only", 
      "projects"."external_webhook_token", 
      "projects"."packages_enabled", 
      "projects"."merge_requests_author_approval", 
      "projects"."pool_repository_id", 
      "projects"."runners_token_encrypted", 
      "projects"."bfg_object_map", 
      "projects"."detected_repository_languages", 
      "projects"."merge_requests_disable_committers_approval", 
      "projects"."require_password_to_approve", 
      "projects"."emails_disabled", 
      "projects"."max_pages_size", 
      "projects"."max_artifacts_size", 
      "projects"."remove_source_branch_after_merge", 
      "projects"."marked_for_deletion_at", 
      "projects"."marked_for_deletion_by_user_id", 
      "projects"."autoclose_referenced_issues", 
      "projects"."suggestion_commit_message", 
      "projects"."project_namespace_id" 
    FROM "projects" 
    WHERE "projects"."id" = 1148;
routes SELECT query
  • Avg: ~0.014 ms / query
  • Sum: ~0.028 ms
  • Vis: https://explain.depesz.com/s/WtbQ
  • SQL
    SELECT "routes".* 
    FROM "routes" 
    WHERE "routes"."source_type" = 'Project' 
    AND "routes"."source_id" = 1148;
  • All execution times (2)
    [0.015, 0.013]
incident_management_escalation_rules DELETE query
  • Execution Time: 0.017 ms
  • Visualization: https://explain.depesz.com/s/jVY1
  • SQL
    DELETE 
    FROM "incident_management_escalation_rules" 
    WHERE "incident_management_escalation_rules"."id" = 11041;
projects SELECT query
  • Execution Time: 0.039 ms
  • Visualization: https://explain.depesz.com/s/CWnN
  • SQL
    SELECT "projects"."id", 
      "projects"."name", 
      "projects"."path", 
      "projects"."description", 
      "projects"."created_at", 
      "projects"."updated_at", 
      "projects"."creator_id", 
      "projects"."namespace_id", 
      "projects"."last_activity_at", 
      "projects"."import_url", 
      "projects"."visibility_level", 
      "projects"."archived", 
      "projects"."avatar", 
      "projects"."merge_requests_template", 
      "projects"."star_count", 
      "projects"."merge_requests_rebase_enabled", 
      "projects"."import_type", 
      "projects"."import_source", 
      "projects"."approvals_before_merge", 
      "projects"."reset_approvals_on_push", 
      "projects"."merge_requests_ff_only_enabled", 
      "projects"."issues_template", 
      "projects"."mirror", 
      "projects"."mirror_user_id", 
      "projects"."shared_runners_enabled", 
      "projects"."runners_token", 
      "projects"."build_coverage_regex", 
      "projects"."build_allow_git_fetch", 
      "projects"."build_timeout", 
      "projects"."mirror_trigger_builds", 
      "projects"."pending_delete", 
      "projects"."public_builds", 
      "projects"."last_repository_check_failed", 
      "projects"."last_repository_check_at", 
      "projects"."only_allow_merge_if_pipeline_succeeds", 
      "projects"."has_external_issue_tracker", 
      "projects"."repository_storage", 
      "projects"."repository_read_only", 
      "projects"."request_access_enabled", 
      "projects"."has_external_wiki", 
      "projects"."ci_config_path", 
      "projects"."lfs_enabled", 
      "projects"."description_html", 
      "projects"."only_allow_merge_if_all_discussions_are_resolved", 
      "projects"."repository_size_limit", 
      "projects"."printing_merge_request_link_enabled", 
      "projects"."auto_cancel_pending_pipelines", 
      "projects"."service_desk_enabled", 
      "projects"."cached_markdown_version", 
      "projects"."delete_error", 
      "projects"."last_repository_updated_at", 
      "projects"."disable_overriding_approvers_per_merge_request", 
      "projects"."storage_version", 
      "projects"."resolve_outdated_diff_discussions", 
      "projects"."remote_mirror_available_overridden", 
      "projects"."only_mirror_protected_branches", 
      "projects"."pull_mirror_available_overridden", 
      "projects"."jobs_cache_index", 
      "projects"."external_authorization_classification_label", 
      "projects"."mirror_overwrites_diverged_branches", 
      "projects"."pages_https_only", 
      "projects"."external_webhook_token", 
      "projects"."packages_enabled", 
      "projects"."merge_requests_author_approval", 
      "projects"."pool_repository_id", 
      "projects"."runners_token_encrypted", 
      "projects"."bfg_object_map", 
      "projects"."detected_repository_languages", 
      "projects"."merge_requests_disable_committers_approval", 
      "projects"."require_password_to_approve", 
      "projects"."emails_disabled", 
      "projects"."max_pages_size", 
      "projects"."max_artifacts_size", 
      "projects"."remove_source_branch_after_merge", 
      "projects"."marked_for_deletion_at", 
      "projects"."marked_for_deletion_by_user_id", 
      "projects"."autoclose_referenced_issues", 
      "projects"."suggestion_commit_message", 
      "projects"."project_namespace_id" 
    FROM "projects" 
    WHERE "projects"."id" = 1286 
    LIMIT 1;
incident_management_escalation_rules SELECT query
  • Execution Time: 0.027 ms
  • Visualization: https://explain.depesz.com/s/oaoO
  • SQL
    SELECT "incident_management_escalation_rules"."id" AS t0_r0, 
      "incident_management_escalation_rules"."policy_id" AS t0_r1, 
      "incident_management_escalation_rules"."oncall_schedule_id" AS t0_r2, 
      "incident_management_escalation_rules"."status" AS t0_r3, 
      "incident_management_escalation_rules"."elapsed_time_seconds" AS t0_r4, 
      "incident_management_escalation_rules"."is_removed" AS t0_r5, 
      "incident_management_escalation_rules"."user_id" AS t0_r6, 
      "policy"."id" AS t1_r0, 
      "policy"."project_id" AS t1_r1, 
      "policy"."name" AS t1_r2, 
      "policy"."description" AS t1_r3 
    FROM "incident_management_escalation_rules" 
    INNER JOIN "incident_management_escalation_policies" "policy" 
      ON "policy"."id" = "incident_management_escalation_rules"."policy_id" 
    WHERE "policy"."project_id" = 1286 
    AND "incident_management_escalation_rules"."user_id" = 1784;
projects SELECT query 2
  • Execution Time: 0.028 ms
  • Visualization: https://explain.depesz.com/s/X7Ij
  • SQL
    SELECT "projects"."id", 
      "projects"."name", 
      "projects"."path", 
      "projects"."description", 
      "projects"."created_at", 
      "projects"."updated_at", 
      "projects"."creator_id", 
      "projects"."namespace_id", 
      "projects"."last_activity_at", 
      "projects"."import_url", 
      "projects"."visibility_level", 
      "projects"."archived", 
      "projects"."avatar", 
      "projects"."merge_requests_template", 
      "projects"."star_count", 
      "projects"."merge_requests_rebase_enabled", 
      "projects"."import_type", 
      "projects"."import_source", 
      "projects"."approvals_before_merge", 
      "projects"."reset_approvals_on_push", 
      "projects"."merge_requests_ff_only_enabled", 
      "projects"."issues_template", 
      "projects"."mirror", 
      "projects"."mirror_user_id", 
      "projects"."shared_runners_enabled", 
      "projects"."runners_token", 
      "projects"."build_coverage_regex", 
      "projects"."build_allow_git_fetch", 
      "projects"."build_timeout", 
      "projects"."mirror_trigger_builds", 
      "projects"."pending_delete", 
      "projects"."public_builds", 
      "projects"."last_repository_check_failed", 
      "projects"."last_repository_check_at", 
      "projects"."only_allow_merge_if_pipeline_succeeds", 
      "projects"."has_external_issue_tracker", 
      "projects"."repository_storage", 
      "projects"."repository_read_only", 
      "projects"."request_access_enabled", 
      "projects"."has_external_wiki", 
      "projects"."ci_config_path", 
      "projects"."lfs_enabled", 
      "projects"."description_html", 
      "projects"."only_allow_merge_if_all_discussions_are_resolved", 
      "projects"."repository_size_limit", 
      "projects"."printing_merge_request_link_enabled", 
      "projects"."auto_cancel_pending_pipelines", 
      "projects"."service_desk_enabled", 
      "projects"."cached_markdown_version", 
      "projects"."delete_error", 
      "projects"."last_repository_updated_at", 
      "projects"."disable_overriding_approvers_per_merge_request", 
      "projects"."storage_version", 
      "projects"."resolve_outdated_diff_discussions", 
      "projects"."remote_mirror_available_overridden", 
      "projects"."only_mirror_protected_branches", 
      "projects"."pull_mirror_available_overridden", 
      "projects"."jobs_cache_index", 
      "projects"."external_authorization_classification_label", 
      "projects"."mirror_overwrites_diverged_branches", 
      "projects"."pages_https_only", 
      "projects"."external_webhook_token", 
      "projects"."packages_enabled", 
      "projects"."merge_requests_author_approval", 
      "projects"."pool_repository_id", 
      "projects"."runners_token_encrypted", 
      "projects"."bfg_object_map", 
      "projects"."detected_repository_languages", 
      "projects"."merge_requests_disable_committers_approval", 
      "projects"."require_password_to_approve", 
      "projects"."emails_disabled", 
      "projects"."max_pages_size", 
      "projects"."max_artifacts_size", 
      "projects"."remove_source_branch_after_merge", 
      "projects"."marked_for_deletion_at", 
      "projects"."marked_for_deletion_by_user_id", 
      "projects"."autoclose_referenced_issues", 
      "projects"."suggestion_commit_message", 
      "projects"."project_namespace_id" 
    FROM "projects" 
    WHERE "projects"."id" = 1286;
routes SELECT query
  • Avg: ~0.016 ms / query
  • Sum: ~0.032 ms
  • Vis: https://explain.depesz.com/s/CH0
  • SQL
    SELECT "routes".* 
    FROM "routes" 
    WHERE "routes"."source_type" = 'Project' 
    AND "routes"."source_id" = 1286;
  • All execution times (2)
    [0.018, 0.014]
incident_management_escalation_rules DELETE query
  • Execution Time: 0.017 ms
  • Visualization: https://explain.depesz.com/s/8xUi
  • SQL
    DELETE 
    FROM "incident_management_escalation_rules" 
    WHERE "incident_management_escalation_rules"."id" = 12356;
  • rules for user deletion
  • Source code
        collector.call do
          Users::DestroyService.new(nil).send(:escalation_rules_cleanup, user)
        end
    
incident_management_escalation_rules SELECT query
  • Execution Time: 0.012 ms
  • Visualization: https://explain.depesz.com/s/Gzcv
  • SQL
    SELECT "incident_management_escalation_rules".* 
    FROM "incident_management_escalation_rules" 
    WHERE "incident_management_escalation_rules"."user_id" = 1752;
incident_management_escalation_policies SELECT query
  • Execution Time: 0.015 ms
  • Visualization: https://explain.depesz.com/s/RN31
  • SQL
    SELECT "incident_management_escalation_policies".* 
    FROM "incident_management_escalation_policies" 
    WHERE "incident_management_escalation_policies"."id" IN (
    1147, 1149, 1151, 1154, 1155
    );
projects SELECT query
  • Execution Time: 0.038 ms
  • Visualization: https://explain.depesz.com/s/H9Hp
  • SQL
    SELECT "projects"."id", 
      "projects"."name", 
      "projects"."path", 
      "projects"."description", 
      "projects"."created_at", 
      "projects"."updated_at", 
      "projects"."creator_id", 
      "projects"."namespace_id", 
      "projects"."last_activity_at", 
      "projects"."import_url", 
      "projects"."visibility_level", 
      "projects"."archived", 
      "projects"."avatar", 
      "projects"."merge_requests_template", 
      "projects"."star_count", 
      "projects"."merge_requests_rebase_enabled", 
      "projects"."import_type", 
      "projects"."import_source", 
      "projects"."approvals_before_merge", 
      "projects"."reset_approvals_on_push", 
      "projects"."merge_requests_ff_only_enabled", 
      "projects"."issues_template", 
      "projects"."mirror", 
      "projects"."mirror_user_id", 
      "projects"."shared_runners_enabled", 
      "projects"."runners_token", 
      "projects"."build_coverage_regex", 
      "projects"."build_allow_git_fetch", 
      "projects"."build_timeout", 
      "projects"."mirror_trigger_builds", 
      "projects"."pending_delete", 
      "projects"."public_builds", 
      "projects"."last_repository_check_failed", 
      "projects"."last_repository_check_at", 
      "projects"."only_allow_merge_if_pipeline_succeeds", 
      "projects"."has_external_issue_tracker", 
      "projects"."repository_storage", 
      "projects"."repository_read_only", 
      "projects"."request_access_enabled", 
      "projects"."has_external_wiki", 
      "projects"."ci_config_path", 
      "projects"."lfs_enabled", 
      "projects"."description_html", 
      "projects"."only_allow_merge_if_all_discussions_are_resolved", 
      "projects"."repository_size_limit", 
      "projects"."printing_merge_request_link_enabled", 
      "projects"."auto_cancel_pending_pipelines", 
      "projects"."service_desk_enabled", 
      "projects"."cached_markdown_version", 
      "projects"."delete_error", 
      "projects"."last_repository_updated_at", 
      "projects"."disable_overriding_approvers_per_merge_request", 
      "projects"."storage_version", 
      "projects"."resolve_outdated_diff_discussions", 
      "projects"."remote_mirror_available_overridden", 
      "projects"."only_mirror_protected_branches", 
      "projects"."pull_mirror_available_overridden", 
      "projects"."jobs_cache_index", 
      "projects"."external_authorization_classification_label", 
      "projects"."mirror_overwrites_diverged_branches", 
      "projects"."pages_https_only", 
      "projects"."external_webhook_token", 
      "projects"."packages_enabled", 
      "projects"."merge_requests_author_approval", 
      "projects"."pool_repository_id", 
      "projects"."runners_token_encrypted", 
      "projects"."bfg_object_map", 
      "projects"."detected_repository_languages", 
      "projects"."merge_requests_disable_committers_approval", 
      "projects"."require_password_to_approve", 
      "projects"."emails_disabled", 
      "projects"."max_pages_size", 
      "projects"."max_artifacts_size", 
      "projects"."remove_source_branch_after_merge", 
      "projects"."marked_for_deletion_at", 
      "projects"."marked_for_deletion_by_user_id", 
      "projects"."autoclose_referenced_issues", 
      "projects"."suggestion_commit_message", 
      "projects"."project_namespace_id" 
    FROM "projects" 
    WHERE "projects"."id" IN (
    1147, 1149, 1151, 1154, 1155
    );
routes SELECT query
  • Avg: ~0.019 ms / query
  • Sum: ~0.037 ms
  • Vis: https://explain.depesz.com/s/8CBW
  • SQL
    SELECT "routes".* 
    FROM "routes" 
    WHERE "routes"."source_type" = 'Project' 
    AND "routes"."source_id" IN (
    1147, 1149, 1151, 1154, 1155
    );
  • All execution times (2)
    [0.019, 0.018]
incident_management_escalation_rules DELETE query
  • Execution Time: 0.024 ms
  • Visualization: https://explain.depesz.com/s/KiYC
  • SQL
    DELETE 
    FROM "incident_management_escalation_rules" 
    WHERE "incident_management_escalation_rules"."id" IN (
    11030, 11051, 11064, 11097, 11100, 11107
    );
incident_management_escalation_rules SELECT query
  • Execution Time: 0.036 ms
  • Visualization: https://explain.depesz.com/s/EtOB
  • SQL
    SELECT "incident_management_escalation_rules".* 
    FROM "incident_management_escalation_rules" 
    WHERE "incident_management_escalation_rules"."user_id" = 1760;
incident_management_escalation_policies SELECT query
  • Execution Time: 0.117 ms
  • Visualization: https://explain.depesz.com/s/1AbW
  • SQL
    SELECT "incident_management_escalation_policies".* 
    FROM "incident_management_escalation_policies" 
    WHERE "incident_management_escalation_policies"."id" IN (
    1144, 1150, 1152, 1187, 1200, 1230, 1240, 1247, 1266, 1270, 1271, 1281, 1300, 1310, 1374, 1388, 1405, 1408, 1422, 1436, 1437, 1439, 1444, 1449, 1466, 1471, 1485, 1491, 1540, 1550, 1588, 1598, 1608, 1611, 1648, 1655, 1662, 1708, 1769, 1772, 1781, 1794, 1812, 1828, 1833, 1859, 1876, 1890, 1913, 1925, 1927, 1929, 1931, 1933, 1981, 1982, 1987, 1990, 2013, 2014, 2030, 2042, 2052, 2053, 2056, 2069, 2080, 2087, 2098, 2127, 2134, 2137, 2146, 2151, 2168, 2176, 2205, 2224, 2232, 2242, 2265, 2267, 2271, 2272, 2281, 2326, 2346, 2383, 2398, 2399
    );
projects SELECT query
  • Execution Time: 0.232 ms
  • Visualization: https://explain.depesz.com/s/sTxJ
  • SQL
    SELECT "projects"."id", 
      "projects"."name", 
      "projects"."path", 
      "projects"."description", 
      "projects"."created_at", 
      "projects"."updated_at", 
      "projects"."creator_id", 
      "projects"."namespace_id", 
      "projects"."last_activity_at", 
      "projects"."import_url", 
      "projects"."visibility_level", 
      "projects"."archived", 
      "projects"."avatar", 
      "projects"."merge_requests_template", 
      "projects"."star_count", 
      "projects"."merge_requests_rebase_enabled", 
      "projects"."import_type", 
      "projects"."import_source", 
      "projects"."approvals_before_merge", 
      "projects"."reset_approvals_on_push", 
      "projects"."merge_requests_ff_only_enabled", 
      "projects"."issues_template", 
      "projects"."mirror", 
      "projects"."mirror_user_id", 
      "projects"."shared_runners_enabled", 
      "projects"."runners_token", 
      "projects"."build_coverage_regex", 
      "projects"."build_allow_git_fetch", 
      "projects"."build_timeout", 
      "projects"."mirror_trigger_builds", 
      "projects"."pending_delete", 
      "projects"."public_builds", 
      "projects"."last_repository_check_failed", 
      "projects"."last_repository_check_at", 
      "projects"."only_allow_merge_if_pipeline_succeeds", 
      "projects"."has_external_issue_tracker", 
      "projects"."repository_storage", 
      "projects"."repository_read_only", 
      "projects"."request_access_enabled", 
      "projects"."has_external_wiki", 
      "projects"."ci_config_path", 
      "projects"."lfs_enabled", 
      "projects"."description_html", 
      "projects"."only_allow_merge_if_all_discussions_are_resolved", 
      "projects"."repository_size_limit", 
      "projects"."printing_merge_request_link_enabled", 
      "projects"."auto_cancel_pending_pipelines", 
      "projects"."service_desk_enabled", 
      "projects"."cached_markdown_version", 
      "projects"."delete_error", 
      "projects"."last_repository_updated_at", 
      "projects"."disable_overriding_approvers_per_merge_request", 
      "projects"."storage_version", 
      "projects"."resolve_outdated_diff_discussions", 
      "projects"."remote_mirror_available_overridden", 
      "projects"."only_mirror_protected_branches", 
      "projects"."pull_mirror_available_overridden", 
      "projects"."jobs_cache_index", 
      "projects"."external_authorization_classification_label", 
      "projects"."mirror_overwrites_diverged_branches", 
      "projects"."pages_https_only", 
      "projects"."external_webhook_token", 
      "projects"."packages_enabled", 
      "projects"."merge_requests_author_approval", 
      "projects"."pool_repository_id", 
      "projects"."runners_token_encrypted", 
      "projects"."bfg_object_map", 
      "projects"."detected_repository_languages", 
      "projects"."merge_requests_disable_committers_approval", 
      "projects"."require_password_to_approve", 
      "projects"."emails_disabled", 
      "projects"."max_pages_size", 
      "projects"."max_artifacts_size", 
      "projects"."remove_source_branch_after_merge", 
      "projects"."marked_for_deletion_at", 
      "projects"."marked_for_deletion_by_user_id", 
      "projects"."autoclose_referenced_issues", 
      "projects"."suggestion_commit_message", 
      "projects"."project_namespace_id" 
    FROM "projects" 
    WHERE "projects"."id" IN (
    1144, 1150, 1152, 1187, 1200, 1230, 1240, 1247, 1266, 1270, 1271, 1281, 1300, 1310, 1374, 1388, 1405, 1408, 1422, 1436, 1437, 1439, 1444, 1449, 1466, 1471, 1485, 1491, 1540, 1550, 1588, 1598, 1608, 1611, 1648, 1655, 1662, 1708, 1769, 1772, 1781, 1794, 1812, 1828, 1833, 1859, 1876, 1890, 1913, 1925, 1927, 1929, 1931, 1933, 1981, 1982, 1987, 1990, 2013, 2014, 2030, 2042, 2052, 2053, 2056, 2069, 2080, 2087, 2098, 2127, 2134, 2137, 2146, 2151, 2168, 2176, 2205, 2224, 2232, 2242, 2265, 2267, 2271, 2272, 2281, 2326, 2346, 2383, 2398, 2399
    );
routes SELECT query
  • Avg: ~1.005 ms / query
  • Sum: ~2.009 ms
  • Vis: https://explain.depesz.com/s/BLco
  • SQL
    SELECT "routes".* 
    FROM "routes" 
    WHERE "routes"."source_type" = 'Project' 
    AND "routes"."source_id" IN (
    1144, 1150, 1152, 1187, 1200, 1230, 1240, 1247, 1266, 1270, 1271, 1281, 1300, 1310, 1374, 1388, 1405, 1408, 1422, 1436, 1437, 1439, 1444, 1449, 1466, 1471, 1485, 1491, 1540, 1550, 1588, 1598, 1608, 1611, 1648, 1655, 1662, 1708, 1769, 1772, 1781, 1794, 1812, 1828, 1833, 1859, 1876, 1890, 1913, 1925, 1927, 1929, 1931, 1933, 1981, 1982, 1987, 1990, 2013, 2014, 2030, 2042, 2052, 2053, 2056, 2069, 2080, 2087, 2098, 2127, 2134, 2137, 2146, 2151, 2168, 2176, 2205, 2224, 2232, 2242, 2265, 2267, 2271, 2272, 2281, 2326, 2346, 2383, 2398, 2399
    );
  • All execution times (2)
    [0.903, 1.106]
incident_management_escalation_rules DELETE query
  • Execution Time: 0.168 ms
  • Visualization: https://explain.depesz.com/s/CBKW
  • SQL
    DELETE 
    FROM "incident_management_escalation_rules" 
    WHERE "incident_management_escalation_rules"."id" IN (
    10998, 11058, 11059, 11077, 11078, 11079, 11410, 11534, 11821, 11919, 11984, 12166, 12203, 12211, 12212, 12310, 12312, 12493, 12598, 13213, 13215, 13349, 13350, 13510, 13511, 13541, 13681, 13817, 13825, 13842, 13884, 13936, 14099, 14100, 14145, 14287, 14288, 14343, 14805, 14808, 14900, 15258, 15259, 15357, 15453, 15454, 15456, 15483, 15853, 15915, 15916, 15986, 16413, 16414, 16984, 17016, 17017, 17102, 17221, 17385, 17538, 17539, 17589, 17829, 17992, 18130, 18355, 18357, 18469, 18486, 18508, 18529, 18552, 18554, 19004, 19007, 19013, 19059, 19089, 19307, 19316, 19465, 19580, 19676, 19690, 19720, 19721, 19722, 19853, 19854, 19961, 20022, 20131, 20413, 20475, 20508, 20511, 20596, 20646, 20804, 20883, 21164, 21342, 21414, 21415, 21513, 21733, 21752, 21789, 21799, 21889, 22309, 22499, 22500, 22843, 22844, 22989, 22990, 23000
    );

Screenshots or screen recordings

Modal HTML email
Screen_Shot_2021-10-26_at_1.44.40_PM Screen_Shot_2021-10-27_at_3.55.32_PM

Plain text email:

Earl Marvin (colton) was removed from the following escalation policies in autodevops:

  - Testing (notifying after 0 minutes unless acknowledged)

Please review the updated escalation policies for autodevops. It is recommended that you reach out to the current on-call responder to ensure continuity of on-call coverage.

How to set up and validate locally

  1. Open & tail the logs from the gitlab directory with tail -f log/development.log | [Skip if your gdk sends emails]
  2. Create a user that you don't mind deleting & add them to a project (project A). (If not tailing the logs, ensure you are an owner of the project).
  3. Navigate to Monitor > Escalation Policies. Add one or more escalation rules which route directly to the user you'll be deleting. Optionally, you can add whatever other rules you might want.
  4. Repeat - add the user to another project (Project B) & add an escalation policy/rule.
  5. Repeat - add the user to a group (Group Z) & add an escalation policy/rule to a project (Project C) within that group.
  6. Verification 1 - removing user from a group:
    • Navigate to Group information > Members in Group Z & find the user.
    • Click the trash icon to remove the user from the group.
    • You can see the email (both txt version & html) in the logs.
    • Navigate to Project C > Monitor > Escalation Policies to see the policy. It should be either empty or containing any rules which did not route directly to the removed user.
  7. Verification 2 - removing user from a project:
    • Navigate to Project information > Members in Project B & find the user.
    • Click the trash icon to remove the user from the project.
    • You can see the email (both txt version & html) in the logs.
    • Navigate to Monitor > Escalation Policies to see the policy. It should be either empty or containing any rules which did not route directly to the removed user.
  8. Verification 3 - deleting a user:
    • Navigate to Menu > Admin > Users & find the user.
    • Click the settings icon to delete the user (either deletion method should behave the same).
    • You can see the email (both txt version & html) in the logs.
    • Navigate to Monitor > Escalation Policies to see the policy. It should be either empty or containing any rules which did not route directly to the removed user.

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 Mayra Cabrera

Merge request reports

Loading