diff --git a/app/services/issue_rebalancing_service.rb b/app/services/issue_rebalancing_service.rb new file mode 100644 index 00000000000..4138c6441c8 --- /dev/null +++ b/app/services/issue_rebalancing_service.rb @@ -0,0 +1,71 @@ +# frozen_string_literal: true + +class IssueRebalancingService + MAX_ISSUE_COUNT = 10_000 + TooManyIssues = Class.new(StandardError) + + def initialize(issue) + @issue = issue + @base = Issue.relative_positioning_query_base(issue) + end + + def execute + gates = [issue.project, issue.project.group].compact + return unless gates.any? { |gate| Feature.enabled?(:rebalance_issues, gate) } + + raise TooManyIssues, "#{issue_count} issues" if issue_count > MAX_ISSUE_COUNT + + start = RelativePositioning::START_POSITION - (gaps / 2) * gap_size + + Issue.transaction do + indexed_ids.each_slice(100) { |pairs| assign_positions(start, pairs) } + end + end + + private + + attr_reader :issue, :base + + # rubocop: disable CodeReuse/ActiveRecord + def indexed_ids + base.reorder(:relative_position, :id).pluck(:id).each_with_index + end + # rubocop: enable CodeReuse/ActiveRecord + + # rubocop: disable CodeReuse/ActiveRecord + def assign_positions(start, positions) + values = positions.map do |id, index| + "(#{id}, #{start + (index * gap_size)})" + end.join(', ') + + Issue.connection.exec_query(<<~SQL, "rebalance issue positions") + WITH cte(cte_id, new_pos) AS ( + SELECT * + FROM (VALUES #{values}) as t (id, pos) + ) + UPDATE #{Issue.table_name} + SET relative_position = cte.new_pos + FROM cte + WHERE cte_id = id + SQL + end + # rubocop: enable CodeReuse/ActiveRecord + + def issue_count + @issue_count ||= base.count + end + + def gaps + issue_count - 1 + end + + def gap_size + # We could try to split the available range over the number of gaps we need, + # but IDEAL_DISTANCE * MAX_ISSUE_COUNT is only 0.1% of the available range, + # so we are guaranteed not to exhaust it by using this static value. + # + # If we raise MAX_ISSUE_COUNT or IDEAL_DISTANCE significantly, this may + # change! + RelativePositioning::IDEAL_DISTANCE + end +end diff --git a/app/services/issues/base_service.rb b/app/services/issues/base_service.rb index 9e72f6dad8d..8e42db746dd 100644 --- a/app/services/issues/base_service.rb +++ b/app/services/issues/base_service.rb @@ -19,6 +19,19 @@ module Issues private + NO_REBALANCING_NEEDED = ((RelativePositioning::MIN_POSITION * 0.9999)..(RelativePositioning::MAX_POSITION * 0.9999)).freeze + + def rebalance_if_needed(issue) + return unless issue + return if issue.relative_position.nil? + return if NO_REBALANCING_NEEDED.cover?(issue.relative_position) + + gates = [issue.project, issue.project.group].compact + return unless gates.any? { |gate| Feature.enabled?(:rebalance_issues, gate) } + + IssueRebalancingWorker.perform_async(issue.id) + end + def create_assignee_note(issue, old_assignees) SystemNoteService.change_issuable_assignees( issue, issue.project, current_user, old_assignees) diff --git a/app/services/issues/create_service.rb b/app/services/issues/create_service.rb index c0194f5b847..d9209191c1c 100644 --- a/app/services/issues/create_service.rb +++ b/app/services/issues/create_service.rb @@ -30,6 +30,7 @@ module Issues user_agent_detail_service.create resolve_discussions_with_issue(issuable) delete_milestone_total_issue_counter_cache(issuable.milestone) + rebalance_if_needed(issuable) super end diff --git a/app/services/issues/update_service.rb b/app/services/issues/update_service.rb index ac7baba3b7c..b0cec002a23 100644 --- a/app/services/issues/update_service.rb +++ b/app/services/issues/update_service.rb @@ -83,6 +83,7 @@ module Issues raise ActiveRecord::RecordNotFound unless issue_before || issue_after issue.move_between(issue_before, issue_after) + rebalance_if_needed(issue) end # rubocop: disable CodeReuse/ActiveRecord diff --git a/app/workers/all_queues.yml b/app/workers/all_queues.yml index 5ce588c9dd8..f2ee0cc9204 100644 --- a/app/workers/all_queues.yml +++ b/app/workers/all_queues.yml @@ -5,7 +5,7 @@ --- - :name: authorized_project_update:authorized_project_update_project_create :feature_category: :authentication_and_authorization - :has_external_dependencies: + :has_external_dependencies: :urgency: :low :resource_boundary: :unknown :weight: 1 @@ -13,7 +13,7 @@ :tags: [] - :name: authorized_project_update:authorized_project_update_project_group_link_create :feature_category: :authentication_and_authorization - :has_external_dependencies: + :has_external_dependencies: :urgency: :low :resource_boundary: :unknown :weight: 1 @@ -21,7 +21,7 @@ :tags: [] - :name: authorized_project_update:authorized_project_update_user_refresh_over_user_range :feature_category: :authentication_and_authorization - :has_external_dependencies: + :has_external_dependencies: :urgency: :low :resource_boundary: :unknown :weight: 1 @@ -29,7 +29,7 @@ :tags: [] - :name: authorized_project_update:authorized_project_update_user_refresh_with_low_urgency :feature_category: :authentication_and_authorization - :has_external_dependencies: + :has_external_dependencies: :urgency: :low :resource_boundary: :unknown :weight: 1 @@ -37,87 +37,87 @@ :tags: [] - :name: auto_devops:auto_devops_disable :feature_category: :auto_devops - :has_external_dependencies: + :has_external_dependencies: :urgency: :low :resource_boundary: :unknown :weight: 2 - :idempotent: + :idempotent: :tags: [] - :name: auto_merge:auto_merge_process :feature_category: :continuous_delivery - :has_external_dependencies: + :has_external_dependencies: :urgency: :low :resource_boundary: :cpu :weight: 3 - :idempotent: + :idempotent: :tags: [] - :name: chaos:chaos_cpu_spin :feature_category: :not_owned - :has_external_dependencies: + :has_external_dependencies: :urgency: :low :resource_boundary: :unknown :weight: 2 - :idempotent: + :idempotent: :tags: [] - :name: chaos:chaos_db_spin :feature_category: :not_owned - :has_external_dependencies: + :has_external_dependencies: :urgency: :low :resource_boundary: :unknown :weight: 2 - :idempotent: + :idempotent: :tags: [] - :name: chaos:chaos_kill :feature_category: :not_owned - :has_external_dependencies: + :has_external_dependencies: :urgency: :low :resource_boundary: :unknown :weight: 2 - :idempotent: + :idempotent: :tags: [] - :name: chaos:chaos_leak_mem :feature_category: :not_owned - :has_external_dependencies: + :has_external_dependencies: :urgency: :low :resource_boundary: :unknown :weight: 2 - :idempotent: + :idempotent: :tags: [] - :name: chaos:chaos_sleep :feature_category: :not_owned - :has_external_dependencies: + :has_external_dependencies: :urgency: :low :resource_boundary: :unknown :weight: 2 - :idempotent: + :idempotent: :tags: [] - :name: container_repository:cleanup_container_repository :feature_category: :container_registry - :has_external_dependencies: + :has_external_dependencies: :urgency: :low :resource_boundary: :unknown :weight: 1 - :idempotent: + :idempotent: :tags: [] - :name: container_repository:delete_container_repository :feature_category: :container_registry - :has_external_dependencies: + :has_external_dependencies: :urgency: :low :resource_boundary: :unknown :weight: 1 - :idempotent: + :idempotent: :tags: [] - :name: cronjob:admin_email :feature_category: :source_code_management - :has_external_dependencies: + :has_external_dependencies: :urgency: :low :resource_boundary: :unknown :weight: 1 - :idempotent: + :idempotent: :tags: [] - :name: cronjob:authorized_project_update_periodic_recalculate :feature_category: :source_code_management - :has_external_dependencies: + :has_external_dependencies: :urgency: :low :resource_boundary: :unknown :weight: 1 @@ -125,79 +125,79 @@ :tags: [] - :name: cronjob:ci_archive_traces_cron :feature_category: :continuous_integration - :has_external_dependencies: + :has_external_dependencies: :urgency: :low :resource_boundary: :unknown :weight: 1 - :idempotent: + :idempotent: :tags: [] - :name: cronjob:container_expiration_policy :feature_category: :container_registry - :has_external_dependencies: + :has_external_dependencies: :urgency: :low :resource_boundary: :unknown :weight: 1 - :idempotent: + :idempotent: :tags: [] - :name: cronjob:environments_auto_stop_cron :feature_category: :continuous_delivery - :has_external_dependencies: + :has_external_dependencies: :urgency: :low :resource_boundary: :unknown :weight: 1 - :idempotent: + :idempotent: :tags: [] - :name: cronjob:expire_build_artifacts :feature_category: :continuous_integration - :has_external_dependencies: + :has_external_dependencies: :urgency: :low :resource_boundary: :unknown :weight: 1 - :idempotent: + :idempotent: :tags: [] - :name: cronjob:gitlab_usage_ping :feature_category: :collection - :has_external_dependencies: + :has_external_dependencies: :urgency: :low :resource_boundary: :unknown :weight: 1 - :idempotent: + :idempotent: :tags: [] - :name: cronjob:import_export_project_cleanup :feature_category: :importers - :has_external_dependencies: + :has_external_dependencies: :urgency: :low :resource_boundary: :unknown :weight: 1 - :idempotent: + :idempotent: :tags: [] - :name: cronjob:import_stuck_project_import_jobs :feature_category: :importers - :has_external_dependencies: + :has_external_dependencies: :urgency: :low :resource_boundary: :cpu :weight: 1 - :idempotent: + :idempotent: :tags: [] - :name: cronjob:issue_due_scheduler :feature_category: :issue_tracking - :has_external_dependencies: + :has_external_dependencies: :urgency: :low :resource_boundary: :unknown :weight: 1 - :idempotent: + :idempotent: :tags: [] - :name: cronjob:jira_import_stuck_jira_import_jobs :feature_category: :importers - :has_external_dependencies: + :has_external_dependencies: :urgency: :low :resource_boundary: :cpu :weight: 1 - :idempotent: + :idempotent: :tags: [] - :name: cronjob:metrics_dashboard_schedule_annotations_prune :feature_category: :metrics - :has_external_dependencies: + :has_external_dependencies: :urgency: :low :resource_boundary: :unknown :weight: 1 @@ -205,39 +205,39 @@ :tags: [] - :name: cronjob:namespaces_prune_aggregation_schedules :feature_category: :source_code_management - :has_external_dependencies: + :has_external_dependencies: :urgency: :low :resource_boundary: :cpu :weight: 1 - :idempotent: + :idempotent: :tags: [] - :name: cronjob:pages_domain_removal_cron :feature_category: :pages - :has_external_dependencies: + :has_external_dependencies: :urgency: :low :resource_boundary: :cpu :weight: 1 - :idempotent: + :idempotent: :tags: [] - :name: cronjob:pages_domain_ssl_renewal_cron :feature_category: :pages - :has_external_dependencies: + :has_external_dependencies: :urgency: :low :resource_boundary: :unknown :weight: 1 - :idempotent: + :idempotent: :tags: [] - :name: cronjob:pages_domain_verification_cron :feature_category: :pages - :has_external_dependencies: + :has_external_dependencies: :urgency: :low :resource_boundary: :unknown :weight: 1 - :idempotent: + :idempotent: :tags: [] - :name: cronjob:partition_creation :feature_category: :database - :has_external_dependencies: + :has_external_dependencies: :urgency: :low :resource_boundary: :unknown :weight: 1 @@ -245,135 +245,135 @@ :tags: [] - :name: cronjob:personal_access_tokens_expired_notification :feature_category: :authentication_and_authorization - :has_external_dependencies: + :has_external_dependencies: :urgency: :low :resource_boundary: :unknown :weight: 1 - :idempotent: + :idempotent: :tags: [] - :name: cronjob:personal_access_tokens_expiring :feature_category: :authentication_and_authorization - :has_external_dependencies: + :has_external_dependencies: :urgency: :low :resource_boundary: :unknown :weight: 1 - :idempotent: + :idempotent: :tags: [] - :name: cronjob:pipeline_schedule :feature_category: :continuous_integration - :has_external_dependencies: + :has_external_dependencies: :urgency: :low :resource_boundary: :cpu :weight: 1 - :idempotent: + :idempotent: :tags: [] - :name: cronjob:prune_old_events :feature_category: :users - :has_external_dependencies: + :has_external_dependencies: :urgency: :low :resource_boundary: :unknown :weight: 1 - :idempotent: + :idempotent: :tags: [] - :name: cronjob:prune_web_hook_logs :feature_category: :integrations - :has_external_dependencies: + :has_external_dependencies: :urgency: :low :resource_boundary: :unknown :weight: 1 - :idempotent: + :idempotent: :tags: [] - :name: cronjob:remove_expired_group_links :feature_category: :authentication_and_authorization - :has_external_dependencies: + :has_external_dependencies: :urgency: :low :resource_boundary: :unknown :weight: 1 - :idempotent: + :idempotent: :tags: [] - :name: cronjob:remove_expired_members :feature_category: :authentication_and_authorization - :has_external_dependencies: + :has_external_dependencies: :urgency: :low :resource_boundary: :cpu :weight: 1 - :idempotent: + :idempotent: :tags: [] - :name: cronjob:remove_unreferenced_lfs_objects :feature_category: :git_lfs - :has_external_dependencies: + :has_external_dependencies: :urgency: :low :resource_boundary: :unknown :weight: 1 - :idempotent: + :idempotent: :tags: [] - :name: cronjob:repository_archive_cache :feature_category: :source_code_management - :has_external_dependencies: + :has_external_dependencies: :urgency: :low :resource_boundary: :unknown :weight: 1 - :idempotent: + :idempotent: :tags: [] - :name: cronjob:repository_check_dispatch :feature_category: :source_code_management - :has_external_dependencies: + :has_external_dependencies: :urgency: :low :resource_boundary: :unknown :weight: 1 - :idempotent: + :idempotent: :tags: [] - :name: cronjob:requests_profiles :feature_category: :source_code_management - :has_external_dependencies: + :has_external_dependencies: :urgency: :low :resource_boundary: :unknown :weight: 1 - :idempotent: + :idempotent: :tags: [] - :name: cronjob:schedule_migrate_external_diffs :feature_category: :source_code_management - :has_external_dependencies: + :has_external_dependencies: :urgency: :low :resource_boundary: :unknown :weight: 1 - :idempotent: + :idempotent: :tags: [] - :name: cronjob:stuck_ci_jobs :feature_category: :continuous_integration - :has_external_dependencies: + :has_external_dependencies: :urgency: :low :resource_boundary: :cpu :weight: 1 - :idempotent: + :idempotent: :tags: [] - :name: cronjob:stuck_export_jobs :feature_category: :importers - :has_external_dependencies: + :has_external_dependencies: :urgency: :low :resource_boundary: :cpu :weight: 1 - :idempotent: + :idempotent: :tags: [] - :name: cronjob:stuck_merge_jobs :feature_category: :source_code_management - :has_external_dependencies: + :has_external_dependencies: :urgency: :low :resource_boundary: :unknown :weight: 1 - :idempotent: + :idempotent: :tags: [] - :name: cronjob:trending_projects :feature_category: :source_code_management - :has_external_dependencies: + :has_external_dependencies: :urgency: :low :resource_boundary: :unknown :weight: 1 - :idempotent: + :idempotent: :tags: [] - :name: cronjob:update_container_registry_info :feature_category: :container_registry - :has_external_dependencies: + :has_external_dependencies: :urgency: :low :resource_boundary: :unknown :weight: 1 @@ -381,11 +381,11 @@ :tags: [] - :name: cronjob:users_create_statistics :feature_category: :users - :has_external_dependencies: + :has_external_dependencies: :urgency: :low :resource_boundary: :unknown :weight: 1 - :idempotent: + :idempotent: :tags: [] - :name: cronjob:x509_issuer_crl_check :feature_category: :source_code_management @@ -397,27 +397,27 @@ :tags: [] - :name: deployment:deployments_finished :feature_category: :continuous_delivery - :has_external_dependencies: + :has_external_dependencies: :urgency: :low :resource_boundary: :cpu :weight: 3 - :idempotent: + :idempotent: :tags: [] - :name: deployment:deployments_forward_deployment :feature_category: :continuous_delivery - :has_external_dependencies: + :has_external_dependencies: :urgency: :low :resource_boundary: :unknown :weight: 3 - :idempotent: + :idempotent: :tags: [] - :name: deployment:deployments_success :feature_category: :continuous_delivery - :has_external_dependencies: + :has_external_dependencies: :urgency: :low :resource_boundary: :cpu :weight: 3 - :idempotent: + :idempotent: :tags: [] - :name: gcp_cluster:cluster_configure_istio :feature_category: :kubernetes_management @@ -425,7 +425,7 @@ :urgency: :low :resource_boundary: :unknown :weight: 1 - :idempotent: + :idempotent: :tags: [] - :name: gcp_cluster:cluster_install_app :feature_category: :kubernetes_management @@ -433,7 +433,7 @@ :urgency: :low :resource_boundary: :unknown :weight: 1 - :idempotent: + :idempotent: :tags: [] - :name: gcp_cluster:cluster_patch_app :feature_category: :kubernetes_management @@ -441,7 +441,7 @@ :urgency: :low :resource_boundary: :unknown :weight: 1 - :idempotent: + :idempotent: :tags: [] - :name: gcp_cluster:cluster_provision :feature_category: :kubernetes_management @@ -449,15 +449,15 @@ :urgency: :low :resource_boundary: :unknown :weight: 1 - :idempotent: + :idempotent: :tags: [] - :name: gcp_cluster:cluster_update_app :feature_category: :kubernetes_management - :has_external_dependencies: + :has_external_dependencies: :urgency: :low :resource_boundary: :unknown :weight: 1 - :idempotent: + :idempotent: :tags: [] - :name: gcp_cluster:cluster_upgrade_app :feature_category: :kubernetes_management @@ -465,7 +465,7 @@ :urgency: :low :resource_boundary: :unknown :weight: 1 - :idempotent: + :idempotent: :tags: [] - :name: gcp_cluster:cluster_wait_for_app_installation :feature_category: :kubernetes_management @@ -473,15 +473,15 @@ :urgency: :low :resource_boundary: :cpu :weight: 1 - :idempotent: + :idempotent: :tags: [] - :name: gcp_cluster:cluster_wait_for_app_update :feature_category: :kubernetes_management - :has_external_dependencies: + :has_external_dependencies: :urgency: :low :resource_boundary: :unknown :weight: 1 - :idempotent: + :idempotent: :tags: [] - :name: gcp_cluster:cluster_wait_for_ingress_ip_address :feature_category: :kubernetes_management @@ -489,23 +489,23 @@ :urgency: :low :resource_boundary: :unknown :weight: 1 - :idempotent: + :idempotent: :tags: [] - :name: gcp_cluster:clusters_applications_activate_service :feature_category: :kubernetes_management - :has_external_dependencies: + :has_external_dependencies: :urgency: :low :resource_boundary: :unknown :weight: 1 - :idempotent: + :idempotent: :tags: [] - :name: gcp_cluster:clusters_applications_deactivate_service :feature_category: :kubernetes_management - :has_external_dependencies: + :has_external_dependencies: :urgency: :low :resource_boundary: :unknown :weight: 1 - :idempotent: + :idempotent: :tags: [] - :name: gcp_cluster:clusters_applications_uninstall :feature_category: :kubernetes_management @@ -513,7 +513,7 @@ :urgency: :low :resource_boundary: :unknown :weight: 1 - :idempotent: + :idempotent: :tags: [] - :name: gcp_cluster:clusters_applications_wait_for_uninstall_app :feature_category: :kubernetes_management @@ -521,7 +521,7 @@ :urgency: :low :resource_boundary: :cpu :weight: 1 - :idempotent: + :idempotent: :tags: [] - :name: gcp_cluster:clusters_cleanup_app :feature_category: :kubernetes_management @@ -529,7 +529,7 @@ :urgency: :low :resource_boundary: :unknown :weight: 1 - :idempotent: + :idempotent: :tags: [] - :name: gcp_cluster:clusters_cleanup_project_namespace :feature_category: :kubernetes_management @@ -537,7 +537,7 @@ :urgency: :low :resource_boundary: :unknown :weight: 1 - :idempotent: + :idempotent: :tags: [] - :name: gcp_cluster:clusters_cleanup_service_account :feature_category: :kubernetes_management @@ -545,7 +545,7 @@ :urgency: :low :resource_boundary: :unknown :weight: 1 - :idempotent: + :idempotent: :tags: [] - :name: gcp_cluster:wait_for_cluster_creation :feature_category: :kubernetes_management @@ -553,7 +553,7 @@ :urgency: :low :resource_boundary: :unknown :weight: 1 - :idempotent: + :idempotent: :tags: [] - :name: github_importer:github_import_import_diff_note :feature_category: :importers @@ -561,7 +561,7 @@ :urgency: :low :resource_boundary: :unknown :weight: 1 - :idempotent: + :idempotent: :tags: [] - :name: github_importer:github_import_import_issue :feature_category: :importers @@ -569,7 +569,7 @@ :urgency: :low :resource_boundary: :unknown :weight: 1 - :idempotent: + :idempotent: :tags: [] - :name: github_importer:github_import_import_lfs_object :feature_category: :importers @@ -577,7 +577,7 @@ :urgency: :low :resource_boundary: :unknown :weight: 1 - :idempotent: + :idempotent: :tags: [] - :name: github_importer:github_import_import_note :feature_category: :importers @@ -585,7 +585,7 @@ :urgency: :low :resource_boundary: :unknown :weight: 1 - :idempotent: + :idempotent: :tags: [] - :name: github_importer:github_import_import_pull_request :feature_category: :importers @@ -593,103 +593,103 @@ :urgency: :low :resource_boundary: :unknown :weight: 1 - :idempotent: + :idempotent: :tags: [] - :name: github_importer:github_import_refresh_import_jid :feature_category: :importers - :has_external_dependencies: + :has_external_dependencies: :urgency: :low :resource_boundary: :unknown :weight: 1 - :idempotent: + :idempotent: :tags: [] - :name: github_importer:github_import_stage_finish_import :feature_category: :importers - :has_external_dependencies: + :has_external_dependencies: :urgency: :low :resource_boundary: :unknown :weight: 1 - :idempotent: + :idempotent: :tags: [] - :name: github_importer:github_import_stage_import_base_data :feature_category: :importers - :has_external_dependencies: + :has_external_dependencies: :urgency: :low :resource_boundary: :unknown :weight: 1 - :idempotent: + :idempotent: :tags: [] - :name: github_importer:github_import_stage_import_issues_and_diff_notes :feature_category: :importers - :has_external_dependencies: + :has_external_dependencies: :urgency: :low :resource_boundary: :unknown :weight: 1 - :idempotent: + :idempotent: :tags: [] - :name: github_importer:github_import_stage_import_lfs_objects :feature_category: :importers - :has_external_dependencies: + :has_external_dependencies: :urgency: :low :resource_boundary: :unknown :weight: 1 - :idempotent: + :idempotent: :tags: [] - :name: github_importer:github_import_stage_import_notes :feature_category: :importers - :has_external_dependencies: + :has_external_dependencies: :urgency: :low :resource_boundary: :unknown :weight: 1 - :idempotent: + :idempotent: :tags: [] - :name: github_importer:github_import_stage_import_pull_requests :feature_category: :importers - :has_external_dependencies: + :has_external_dependencies: :urgency: :low :resource_boundary: :unknown :weight: 1 - :idempotent: + :idempotent: :tags: [] - :name: github_importer:github_import_stage_import_repository :feature_category: :importers - :has_external_dependencies: + :has_external_dependencies: :urgency: :low :resource_boundary: :unknown :weight: 1 - :idempotent: + :idempotent: :tags: [] - :name: hashed_storage:hashed_storage_migrator :feature_category: :source_code_management - :has_external_dependencies: + :has_external_dependencies: :urgency: :low :resource_boundary: :unknown :weight: 1 - :idempotent: + :idempotent: :tags: [] - :name: hashed_storage:hashed_storage_project_migrate :feature_category: :source_code_management - :has_external_dependencies: + :has_external_dependencies: :urgency: :low :resource_boundary: :unknown :weight: 1 - :idempotent: + :idempotent: :tags: [] - :name: hashed_storage:hashed_storage_project_rollback :feature_category: :source_code_management - :has_external_dependencies: + :has_external_dependencies: :urgency: :low :resource_boundary: :unknown :weight: 1 - :idempotent: + :idempotent: :tags: [] - :name: hashed_storage:hashed_storage_rollbacker :feature_category: :source_code_management - :has_external_dependencies: + :has_external_dependencies: :urgency: :low :resource_boundary: :unknown :weight: 1 - :idempotent: + :idempotent: :tags: [] - :name: incident_management:clusters_applications_check_prometheus_health :feature_category: :incident_management @@ -701,175 +701,175 @@ :tags: [] - :name: incident_management:incident_management_pager_duty_process_incident :feature_category: :incident_management - :has_external_dependencies: + :has_external_dependencies: :urgency: :low :resource_boundary: :unknown :weight: 2 - :idempotent: + :idempotent: :tags: [] - :name: incident_management:incident_management_process_alert :feature_category: :incident_management - :has_external_dependencies: + :has_external_dependencies: :urgency: :low :resource_boundary: :unknown :weight: 2 - :idempotent: + :idempotent: :tags: [] - :name: incident_management:incident_management_process_prometheus_alert :feature_category: :incident_management - :has_external_dependencies: + :has_external_dependencies: :urgency: :low :resource_boundary: :cpu :weight: 2 - :idempotent: + :idempotent: :tags: [] - :name: jira_importer:jira_import_advance_stage :feature_category: :importers - :has_external_dependencies: + :has_external_dependencies: :urgency: :low :resource_boundary: :unknown :weight: 1 - :idempotent: + :idempotent: :tags: [] - :name: jira_importer:jira_import_import_issue :feature_category: :importers - :has_external_dependencies: + :has_external_dependencies: :urgency: :low :resource_boundary: :unknown :weight: 1 - :idempotent: + :idempotent: :tags: [] - :name: jira_importer:jira_import_stage_finish_import :feature_category: :importers - :has_external_dependencies: + :has_external_dependencies: :urgency: :low :resource_boundary: :unknown :weight: 1 - :idempotent: + :idempotent: :tags: [] - :name: jira_importer:jira_import_stage_import_attachments :feature_category: :importers - :has_external_dependencies: + :has_external_dependencies: :urgency: :low :resource_boundary: :unknown :weight: 1 - :idempotent: + :idempotent: :tags: [] - :name: jira_importer:jira_import_stage_import_issues :feature_category: :importers - :has_external_dependencies: + :has_external_dependencies: :urgency: :low :resource_boundary: :unknown :weight: 1 - :idempotent: + :idempotent: :tags: [] - :name: jira_importer:jira_import_stage_import_labels :feature_category: :importers - :has_external_dependencies: + :has_external_dependencies: :urgency: :low :resource_boundary: :unknown :weight: 1 - :idempotent: + :idempotent: :tags: [] - :name: jira_importer:jira_import_stage_import_notes :feature_category: :importers - :has_external_dependencies: + :has_external_dependencies: :urgency: :low :resource_boundary: :unknown :weight: 1 - :idempotent: + :idempotent: :tags: [] - :name: jira_importer:jira_import_stage_start_import :feature_category: :importers - :has_external_dependencies: + :has_external_dependencies: :urgency: :low :resource_boundary: :unknown :weight: 1 - :idempotent: + :idempotent: :tags: [] - :name: mail_scheduler:mail_scheduler_issue_due :feature_category: :issue_tracking - :has_external_dependencies: + :has_external_dependencies: :urgency: :low :resource_boundary: :unknown :weight: 2 - :idempotent: + :idempotent: :tags: [] - :name: mail_scheduler:mail_scheduler_notification_service :feature_category: :issue_tracking - :has_external_dependencies: + :has_external_dependencies: :urgency: :low :resource_boundary: :cpu :weight: 2 - :idempotent: + :idempotent: :tags: [] - :name: object_pool:object_pool_create :feature_category: :gitaly - :has_external_dependencies: + :has_external_dependencies: :urgency: :low :resource_boundary: :unknown :weight: 1 - :idempotent: + :idempotent: :tags: [] - :name: object_pool:object_pool_destroy :feature_category: :gitaly - :has_external_dependencies: + :has_external_dependencies: :urgency: :low :resource_boundary: :unknown :weight: 1 - :idempotent: + :idempotent: :tags: [] - :name: object_pool:object_pool_join :feature_category: :gitaly - :has_external_dependencies: + :has_external_dependencies: :urgency: :low :resource_boundary: :cpu :weight: 1 - :idempotent: + :idempotent: :tags: [] - :name: object_pool:object_pool_schedule_join :feature_category: :gitaly - :has_external_dependencies: + :has_external_dependencies: :urgency: :low :resource_boundary: :unknown :weight: 1 - :idempotent: + :idempotent: :tags: [] - :name: object_storage:object_storage_background_move :feature_category: :not_owned - :has_external_dependencies: + :has_external_dependencies: :urgency: :low :resource_boundary: :unknown :weight: 1 - :idempotent: + :idempotent: :tags: [] - :name: object_storage:object_storage_migrate_uploads :feature_category: :not_owned - :has_external_dependencies: + :has_external_dependencies: :urgency: :low :resource_boundary: :unknown :weight: 1 - :idempotent: + :idempotent: :tags: [] - :name: package_repositories:packages_nuget_extraction :feature_category: :package_registry - :has_external_dependencies: + :has_external_dependencies: :urgency: :low :resource_boundary: :unknown :weight: 1 - :idempotent: + :idempotent: :tags: [] - :name: pipeline_background:archive_trace :feature_category: :continuous_integration - :has_external_dependencies: + :has_external_dependencies: :urgency: :low :resource_boundary: :unknown :weight: 1 - :idempotent: + :idempotent: :tags: [] - :name: pipeline_background:ci_build_report_result :feature_category: :continuous_integration - :has_external_dependencies: + :has_external_dependencies: :urgency: :low :resource_boundary: :unknown :weight: 1 @@ -877,15 +877,15 @@ :tags: [] - :name: pipeline_background:ci_build_trace_chunk_flush :feature_category: :continuous_integration - :has_external_dependencies: + :has_external_dependencies: :urgency: :low :resource_boundary: :unknown :weight: 1 - :idempotent: + :idempotent: :tags: [] - :name: pipeline_background:ci_daily_build_group_report_results :feature_category: :continuous_integration - :has_external_dependencies: + :has_external_dependencies: :urgency: :low :resource_boundary: :unknown :weight: 1 @@ -893,7 +893,7 @@ :tags: [] - :name: pipeline_background:ci_pipeline_success_unlock_artifacts :feature_category: :continuous_integration - :has_external_dependencies: + :has_external_dependencies: :urgency: :low :resource_boundary: :unknown :weight: 1 @@ -901,7 +901,7 @@ :tags: [] - :name: pipeline_background:ci_pipelines_create_artifact :feature_category: :continuous_integration - :has_external_dependencies: + :has_external_dependencies: :urgency: :low :resource_boundary: :unknown :weight: 1 @@ -909,7 +909,7 @@ :tags: [] - :name: pipeline_background:ci_ref_delete_unlock_artifacts :feature_category: :continuous_integration - :has_external_dependencies: + :has_external_dependencies: :urgency: :low :resource_boundary: :unknown :weight: 1 @@ -917,7 +917,7 @@ :tags: [] - :name: pipeline_cache:expire_job_cache :feature_category: :continuous_integration - :has_external_dependencies: + :has_external_dependencies: :urgency: :high :resource_boundary: :unknown :weight: 3 @@ -925,7 +925,7 @@ :tags: [] - :name: pipeline_cache:expire_pipeline_cache :feature_category: :continuous_integration - :has_external_dependencies: + :has_external_dependencies: :urgency: :high :resource_boundary: :cpu :weight: 3 @@ -933,152 +933,152 @@ :tags: [] - :name: pipeline_creation:create_pipeline :feature_category: :continuous_integration - :has_external_dependencies: + :has_external_dependencies: :urgency: :high :resource_boundary: :cpu :weight: 4 - :idempotent: + :idempotent: :tags: [] - :name: pipeline_creation:run_pipeline_schedule :feature_category: :continuous_integration - :has_external_dependencies: + :has_external_dependencies: :urgency: :low :resource_boundary: :unknown :weight: 4 - :idempotent: + :idempotent: :tags: [] - :name: pipeline_default:build_coverage :feature_category: :continuous_integration - :has_external_dependencies: + :has_external_dependencies: :urgency: :low :resource_boundary: :unknown :weight: 3 - :idempotent: + :idempotent: :tags: [] - :name: pipeline_default:build_trace_sections :feature_category: :continuous_integration - :has_external_dependencies: + :has_external_dependencies: :urgency: :low :resource_boundary: :unknown :weight: 3 - :idempotent: + :idempotent: :tags: [] - :name: pipeline_default:ci_create_cross_project_pipeline :feature_category: :continuous_integration - :has_external_dependencies: + :has_external_dependencies: :urgency: :low :resource_boundary: :cpu :weight: 3 - :idempotent: + :idempotent: :tags: [] - :name: pipeline_default:ci_pipeline_bridge_status :feature_category: :continuous_integration - :has_external_dependencies: + :has_external_dependencies: :urgency: :high :resource_boundary: :cpu :weight: 3 - :idempotent: + :idempotent: :tags: [] - :name: pipeline_default:pipeline_metrics :feature_category: :continuous_integration - :has_external_dependencies: + :has_external_dependencies: :urgency: :high :resource_boundary: :unknown :weight: 3 - :idempotent: + :idempotent: :tags: [] - :name: pipeline_default:pipeline_notification :feature_category: :continuous_integration - :has_external_dependencies: + :has_external_dependencies: :urgency: :high :resource_boundary: :cpu :weight: 3 - :idempotent: + :idempotent: :tags: [] - :name: pipeline_default:pipeline_update_ci_ref_status :feature_category: :continuous_integration - :has_external_dependencies: + :has_external_dependencies: :urgency: :high :resource_boundary: :cpu :weight: 3 - :idempotent: + :idempotent: :tags: [] - :name: pipeline_hooks:build_hooks :feature_category: :continuous_integration - :has_external_dependencies: + :has_external_dependencies: :urgency: :high :resource_boundary: :unknown :weight: 2 - :idempotent: + :idempotent: :tags: [] - :name: pipeline_hooks:pipeline_hooks :feature_category: :continuous_integration - :has_external_dependencies: + :has_external_dependencies: :urgency: :high :resource_boundary: :cpu :weight: 2 - :idempotent: + :idempotent: :tags: [] - :name: pipeline_processing:build_finished :feature_category: :continuous_integration - :has_external_dependencies: + :has_external_dependencies: :urgency: :high :resource_boundary: :cpu :weight: 5 - :idempotent: + :idempotent: :tags: - :requires_disk_io - :name: pipeline_processing:build_queue :feature_category: :continuous_integration - :has_external_dependencies: + :has_external_dependencies: :urgency: :high :resource_boundary: :cpu :weight: 5 - :idempotent: + :idempotent: :tags: [] - :name: pipeline_processing:build_success :feature_category: :continuous_integration - :has_external_dependencies: + :has_external_dependencies: :urgency: :high :resource_boundary: :unknown :weight: 5 - :idempotent: + :idempotent: :tags: [] - :name: pipeline_processing:ci_build_prepare :feature_category: :continuous_integration - :has_external_dependencies: + :has_external_dependencies: :urgency: :low :resource_boundary: :unknown :weight: 5 - :idempotent: + :idempotent: :tags: [] - :name: pipeline_processing:ci_build_schedule :feature_category: :continuous_integration - :has_external_dependencies: + :has_external_dependencies: :urgency: :low :resource_boundary: :cpu :weight: 5 - :idempotent: + :idempotent: :tags: [] - :name: pipeline_processing:ci_resource_groups_assign_resource_from_resource_group :feature_category: :continuous_delivery - :has_external_dependencies: + :has_external_dependencies: :urgency: :low :resource_boundary: :unknown :weight: 5 - :idempotent: + :idempotent: :tags: [] - :name: pipeline_processing:pipeline_process :feature_category: :continuous_integration - :has_external_dependencies: + :has_external_dependencies: :urgency: :high :resource_boundary: :unknown :weight: 5 - :idempotent: + :idempotent: :tags: [] - :name: pipeline_processing:pipeline_update :feature_category: :continuous_integration - :has_external_dependencies: + :has_external_dependencies: :urgency: :high :resource_boundary: :unknown :weight: 5 @@ -1086,7 +1086,7 @@ :tags: [] - :name: pipeline_processing:stage_update :feature_category: :continuous_integration - :has_external_dependencies: + :has_external_dependencies: :urgency: :high :resource_boundary: :unknown :weight: 5 @@ -1094,7 +1094,7 @@ :tags: [] - :name: pipeline_processing:update_head_pipeline_for_merge_request :feature_category: :continuous_integration - :has_external_dependencies: + :has_external_dependencies: :urgency: :high :resource_boundary: :cpu :weight: 5 @@ -1102,71 +1102,71 @@ :tags: [] - :name: repository_check:repository_check_batch :feature_category: :source_code_management - :has_external_dependencies: + :has_external_dependencies: :urgency: :low :resource_boundary: :unknown :weight: 1 - :idempotent: + :idempotent: :tags: [] - :name: repository_check:repository_check_clear :feature_category: :source_code_management - :has_external_dependencies: + :has_external_dependencies: :urgency: :low :resource_boundary: :unknown :weight: 1 - :idempotent: + :idempotent: :tags: [] - :name: repository_check:repository_check_single_repository :feature_category: :source_code_management - :has_external_dependencies: + :has_external_dependencies: :urgency: :low :resource_boundary: :unknown :weight: 1 - :idempotent: + :idempotent: :tags: [] - :name: todos_destroyer:todos_destroyer_confidential_issue :feature_category: :issue_tracking - :has_external_dependencies: + :has_external_dependencies: :urgency: :low :resource_boundary: :unknown :weight: 1 - :idempotent: + :idempotent: :tags: [] - :name: todos_destroyer:todos_destroyer_entity_leave :feature_category: :issue_tracking - :has_external_dependencies: + :has_external_dependencies: :urgency: :low :resource_boundary: :unknown :weight: 1 - :idempotent: + :idempotent: :tags: [] - :name: todos_destroyer:todos_destroyer_group_private :feature_category: :issue_tracking - :has_external_dependencies: + :has_external_dependencies: :urgency: :low :resource_boundary: :unknown :weight: 1 - :idempotent: + :idempotent: :tags: [] - :name: todos_destroyer:todos_destroyer_private_features :feature_category: :issue_tracking - :has_external_dependencies: + :has_external_dependencies: :urgency: :low :resource_boundary: :unknown :weight: 1 - :idempotent: + :idempotent: :tags: [] - :name: todos_destroyer:todos_destroyer_project_private :feature_category: :issue_tracking - :has_external_dependencies: + :has_external_dependencies: :urgency: :low :resource_boundary: :unknown :weight: 1 - :idempotent: + :idempotent: :tags: [] - :name: unassign_issuables:members_destroyer_unassign_issuables :feature_category: :authentication_and_authorization - :has_external_dependencies: + :has_external_dependencies: :urgency: :low :resource_boundary: :unknown :weight: 1 @@ -1174,7 +1174,7 @@ :tags: [] - :name: update_namespace_statistics:namespaces_root_statistics :feature_category: :source_code_management - :has_external_dependencies: + :has_external_dependencies: :urgency: :low :resource_boundary: :unknown :weight: 1 @@ -1182,7 +1182,7 @@ :tags: [] - :name: update_namespace_statistics:namespaces_schedule_aggregation :feature_category: :source_code_management - :has_external_dependencies: + :has_external_dependencies: :urgency: :low :resource_boundary: :unknown :weight: 1 @@ -1190,7 +1190,7 @@ :tags: [] - :name: authorized_keys :feature_category: :source_code_management - :has_external_dependencies: + :has_external_dependencies: :urgency: :high :resource_boundary: :unknown :weight: 2 @@ -1198,7 +1198,7 @@ :tags: [] - :name: authorized_projects :feature_category: :authentication_and_authorization - :has_external_dependencies: + :has_external_dependencies: :urgency: :high :resource_boundary: :unknown :weight: 2 @@ -1206,11 +1206,11 @@ :tags: [] - :name: background_migration :feature_category: :database - :has_external_dependencies: + :has_external_dependencies: :urgency: :throttled :resource_boundary: :unknown :weight: 1 - :idempotent: + :idempotent: :tags: [] - :name: chat_notification :feature_category: :chatops @@ -1218,11 +1218,11 @@ :urgency: :low :resource_boundary: :unknown :weight: 2 - :idempotent: + :idempotent: :tags: [] - :name: create_commit_signature :feature_category: :source_code_management - :has_external_dependencies: + :has_external_dependencies: :urgency: :low :resource_boundary: :unknown :weight: 2 @@ -1230,91 +1230,91 @@ :tags: [] - :name: create_evidence :feature_category: :release_evidence - :has_external_dependencies: + :has_external_dependencies: :urgency: :low :resource_boundary: :unknown :weight: 2 - :idempotent: + :idempotent: :tags: [] - :name: create_note_diff_file :feature_category: :source_code_management - :has_external_dependencies: + :has_external_dependencies: :urgency: :low :resource_boundary: :unknown :weight: 1 - :idempotent: + :idempotent: :tags: [] - :name: default - :feature_category: - :has_external_dependencies: - :urgency: - :resource_boundary: + :feature_category: + :has_external_dependencies: + :urgency: + :resource_boundary: :weight: 1 - :idempotent: + :idempotent: :tags: [] - :name: delete_diff_files :feature_category: :source_code_management - :has_external_dependencies: + :has_external_dependencies: :urgency: :low :resource_boundary: :unknown :weight: 1 - :idempotent: + :idempotent: :tags: [] - :name: delete_merged_branches :feature_category: :source_code_management - :has_external_dependencies: + :has_external_dependencies: :urgency: :low :resource_boundary: :unknown :weight: 1 - :idempotent: + :idempotent: :tags: [] - :name: delete_stored_files :feature_category: :not_owned - :has_external_dependencies: + :has_external_dependencies: :urgency: :low :resource_boundary: :unknown :weight: 1 - :idempotent: + :idempotent: :tags: [] - :name: delete_user :feature_category: :authentication_and_authorization - :has_external_dependencies: + :has_external_dependencies: :urgency: :low :resource_boundary: :unknown :weight: 1 - :idempotent: + :idempotent: :tags: [] - :name: design_management_new_version :feature_category: :design_management - :has_external_dependencies: + :has_external_dependencies: :urgency: :low :resource_boundary: :memory :weight: 1 - :idempotent: + :idempotent: :tags: [] - :name: detect_repository_languages :feature_category: :source_code_management - :has_external_dependencies: + :has_external_dependencies: :urgency: :low :resource_boundary: :unknown :weight: 1 - :idempotent: + :idempotent: :tags: [] - :name: email_receiver :feature_category: :issue_tracking - :has_external_dependencies: + :has_external_dependencies: :urgency: :high :resource_boundary: :unknown :weight: 2 - :idempotent: + :idempotent: :tags: [] - :name: emails_on_push :feature_category: :source_code_management - :has_external_dependencies: + :has_external_dependencies: :urgency: :low :resource_boundary: :cpu :weight: 2 - :idempotent: + :idempotent: :tags: [] - :name: error_tracking_issue_link :feature_category: :error_tracking @@ -1322,23 +1322,23 @@ :urgency: :low :resource_boundary: :unknown :weight: 1 - :idempotent: + :idempotent: :tags: [] - :name: expire_build_instance_artifacts :feature_category: :continuous_integration - :has_external_dependencies: + :has_external_dependencies: :urgency: :low :resource_boundary: :unknown :weight: 1 - :idempotent: + :idempotent: :tags: [] - :name: export_csv :feature_category: :issue_tracking - :has_external_dependencies: + :has_external_dependencies: :urgency: :low :resource_boundary: :cpu :weight: 1 - :idempotent: + :idempotent: :tags: [] - :name: external_service_reactive_caching :feature_category: :not_owned @@ -1346,15 +1346,15 @@ :urgency: :low :resource_boundary: :unknown :weight: 1 - :idempotent: + :idempotent: :tags: [] - :name: file_hook :feature_category: :integrations - :has_external_dependencies: + :has_external_dependencies: :urgency: :low :resource_boundary: :unknown :weight: 1 - :idempotent: + :idempotent: :tags: [] - :name: flush_counter_increments :feature_category: :not_owned @@ -1366,87 +1366,95 @@ :tags: [] - :name: git_garbage_collect :feature_category: :gitaly - :has_external_dependencies: + :has_external_dependencies: :urgency: :low :resource_boundary: :unknown :weight: 1 - :idempotent: + :idempotent: :tags: [] - :name: github_import_advance_stage :feature_category: :importers - :has_external_dependencies: + :has_external_dependencies: :urgency: :low :resource_boundary: :unknown :weight: 1 - :idempotent: + :idempotent: :tags: [] - :name: gitlab_shell :feature_category: :source_code_management - :has_external_dependencies: + :has_external_dependencies: :urgency: :high :resource_boundary: :unknown :weight: 2 - :idempotent: + :idempotent: :tags: [] - :name: group_destroy :feature_category: :subgroups - :has_external_dependencies: + :has_external_dependencies: :urgency: :low :resource_boundary: :unknown :weight: 1 - :idempotent: + :idempotent: :tags: [] - :name: group_export :feature_category: :importers - :has_external_dependencies: + :has_external_dependencies: :urgency: :low :resource_boundary: :unknown :weight: 1 - :idempotent: + :idempotent: :tags: [] - :name: group_import :feature_category: :importers - :has_external_dependencies: + :has_external_dependencies: :urgency: :low :resource_boundary: :unknown :weight: 1 - :idempotent: + :idempotent: :tags: [] - :name: import_issues_csv :feature_category: :issue_tracking - :has_external_dependencies: + :has_external_dependencies: :urgency: :low :resource_boundary: :cpu :weight: 2 - :idempotent: + :idempotent: :tags: [] - :name: invalid_gpg_signature_update :feature_category: :source_code_management - :has_external_dependencies: + :has_external_dependencies: :urgency: :low :resource_boundary: :unknown :weight: 2 - :idempotent: + :idempotent: :tags: [] - :name: irker :feature_category: :integrations - :has_external_dependencies: + :has_external_dependencies: :urgency: :low :resource_boundary: :unknown :weight: 1 - :idempotent: + :idempotent: + :tags: [] +- :name: issue_rebalancing + :feature_category: :issue_tracking + :has_external_dependencies: + :urgency: :low + :resource_boundary: :unknown + :weight: 1 + :idempotent: true :tags: [] - :name: mailers - :feature_category: - :has_external_dependencies: - :urgency: - :resource_boundary: + :feature_category: + :has_external_dependencies: + :urgency: + :resource_boundary: :weight: 2 - :idempotent: + :idempotent: :tags: [] - :name: merge :feature_category: :source_code_management - :has_external_dependencies: + :has_external_dependencies: :urgency: :high :resource_boundary: :unknown :weight: 5 @@ -1454,7 +1462,7 @@ :tags: [] - :name: merge_request_mergeability_check :feature_category: :source_code_management - :has_external_dependencies: + :has_external_dependencies: :urgency: :low :resource_boundary: :unknown :weight: 1 @@ -1462,7 +1470,7 @@ :tags: [] - :name: metrics_dashboard_prune_old_annotations :feature_category: :metrics - :has_external_dependencies: + :has_external_dependencies: :urgency: :low :resource_boundary: :unknown :weight: 1 @@ -1470,67 +1478,67 @@ :tags: [] - :name: migrate_external_diffs :feature_category: :source_code_management - :has_external_dependencies: + :has_external_dependencies: :urgency: :low :resource_boundary: :unknown :weight: 1 - :idempotent: + :idempotent: :tags: [] - :name: namespaceless_project_destroy :feature_category: :authentication_and_authorization - :has_external_dependencies: + :has_external_dependencies: :urgency: :low :resource_boundary: :unknown :weight: 1 - :idempotent: + :idempotent: :tags: [] - :name: new_issue :feature_category: :issue_tracking - :has_external_dependencies: + :has_external_dependencies: :urgency: :high :resource_boundary: :cpu :weight: 2 - :idempotent: + :idempotent: :tags: [] - :name: new_merge_request :feature_category: :source_code_management - :has_external_dependencies: + :has_external_dependencies: :urgency: :high :resource_boundary: :cpu :weight: 2 - :idempotent: + :idempotent: :tags: [] - :name: new_note :feature_category: :issue_tracking - :has_external_dependencies: + :has_external_dependencies: :urgency: :high :resource_boundary: :cpu :weight: 2 - :idempotent: + :idempotent: :tags: [] - :name: pages :feature_category: :pages - :has_external_dependencies: + :has_external_dependencies: :urgency: :low :resource_boundary: :unknown :weight: 1 - :idempotent: + :idempotent: :tags: [] - :name: pages_domain_ssl_renewal :feature_category: :pages - :has_external_dependencies: + :has_external_dependencies: :urgency: :low :resource_boundary: :unknown :weight: 1 - :idempotent: + :idempotent: :tags: [] - :name: pages_domain_verification :feature_category: :pages - :has_external_dependencies: + :has_external_dependencies: :urgency: :low :resource_boundary: :unknown :weight: 1 - :idempotent: + :idempotent: :tags: [] - :name: pages_remove :feature_category: :pages @@ -1550,7 +1558,7 @@ :tags: [] - :name: pages_update_configuration :feature_category: :pages - :has_external_dependencies: + :has_external_dependencies: :urgency: :low :resource_boundary: :unknown :weight: 1 @@ -1558,23 +1566,23 @@ :tags: [] - :name: phabricator_import_import_tasks :feature_category: :importers - :has_external_dependencies: + :has_external_dependencies: :urgency: :low :resource_boundary: :unknown :weight: 1 - :idempotent: + :idempotent: :tags: [] - :name: post_receive :feature_category: :source_code_management - :has_external_dependencies: + :has_external_dependencies: :urgency: :high :resource_boundary: :cpu :weight: 5 - :idempotent: + :idempotent: :tags: [] - :name: process_commit :feature_category: :source_code_management - :has_external_dependencies: + :has_external_dependencies: :urgency: :high :resource_boundary: :unknown :weight: 3 @@ -1582,35 +1590,35 @@ :tags: [] - :name: project_cache :feature_category: :source_code_management - :has_external_dependencies: + :has_external_dependencies: :urgency: :high :resource_boundary: :unknown :weight: 1 - :idempotent: + :idempotent: :tags: [] - :name: project_daily_statistics :feature_category: :source_code_management - :has_external_dependencies: + :has_external_dependencies: :urgency: :low :resource_boundary: :unknown :weight: 1 - :idempotent: + :idempotent: :tags: [] - :name: project_destroy :feature_category: :source_code_management - :has_external_dependencies: + :has_external_dependencies: :urgency: :low :resource_boundary: :unknown :weight: 1 - :idempotent: + :idempotent: :tags: [] - :name: project_export :feature_category: :importers - :has_external_dependencies: + :has_external_dependencies: :urgency: :throttled :resource_boundary: :memory :weight: 1 - :idempotent: + :idempotent: :tags: [] - :name: project_service :feature_category: :integrations @@ -1618,11 +1626,11 @@ :urgency: :low :resource_boundary: :unknown :weight: 1 - :idempotent: + :idempotent: :tags: [] - :name: project_update_repository_storage :feature_category: :gitaly - :has_external_dependencies: + :has_external_dependencies: :urgency: :throttled :resource_boundary: :unknown :weight: 1 @@ -1630,7 +1638,7 @@ :tags: [] - :name: prometheus_create_default_alerts :feature_category: :incident_management - :has_external_dependencies: + :has_external_dependencies: :urgency: :high :resource_boundary: :unknown :weight: 1 @@ -1638,7 +1646,7 @@ :tags: [] - :name: propagate_integration :feature_category: :integrations - :has_external_dependencies: + :has_external_dependencies: :urgency: :low :resource_boundary: :unknown :weight: 1 @@ -1646,51 +1654,51 @@ :tags: [] - :name: propagate_service_template :feature_category: :integrations - :has_external_dependencies: + :has_external_dependencies: :urgency: :low :resource_boundary: :unknown :weight: 1 - :idempotent: + :idempotent: :tags: [] - :name: reactive_caching :feature_category: :not_owned - :has_external_dependencies: + :has_external_dependencies: :urgency: :low :resource_boundary: :cpu :weight: 1 - :idempotent: + :idempotent: :tags: [] - :name: rebase :feature_category: :source_code_management - :has_external_dependencies: + :has_external_dependencies: :urgency: :low :resource_boundary: :unknown :weight: 2 - :idempotent: + :idempotent: :tags: [] - :name: remote_mirror_notification :feature_category: :source_code_management - :has_external_dependencies: + :has_external_dependencies: :urgency: :low :resource_boundary: :unknown :weight: 2 - :idempotent: + :idempotent: :tags: [] - :name: repository_cleanup :feature_category: :source_code_management - :has_external_dependencies: + :has_external_dependencies: :urgency: :low :resource_boundary: :unknown :weight: 1 - :idempotent: + :idempotent: :tags: [] - :name: repository_fork :feature_category: :source_code_management - :has_external_dependencies: + :has_external_dependencies: :urgency: :low :resource_boundary: :unknown :weight: 1 - :idempotent: + :idempotent: :tags: [] - :name: repository_import :feature_category: :importers @@ -1698,15 +1706,15 @@ :urgency: :low :resource_boundary: :unknown :weight: 1 - :idempotent: + :idempotent: :tags: [] - :name: repository_remove_remote :feature_category: :source_code_management - :has_external_dependencies: + :has_external_dependencies: :urgency: :low :resource_boundary: :unknown :weight: 1 - :idempotent: + :idempotent: :tags: [] - :name: repository_update_remote_mirror :feature_category: :source_code_management @@ -1714,51 +1722,51 @@ :urgency: :low :resource_boundary: :unknown :weight: 1 - :idempotent: + :idempotent: :tags: [] - :name: self_monitoring_project_create :feature_category: :metrics - :has_external_dependencies: + :has_external_dependencies: :urgency: :low :resource_boundary: :unknown :weight: 2 - :idempotent: + :idempotent: :tags: [] - :name: self_monitoring_project_delete :feature_category: :metrics - :has_external_dependencies: + :has_external_dependencies: :urgency: :low :resource_boundary: :unknown :weight: 2 - :idempotent: + :idempotent: :tags: [] - :name: service_desk_email_receiver :feature_category: :issue_tracking - :has_external_dependencies: + :has_external_dependencies: :urgency: :low :resource_boundary: :unknown :weight: 1 - :idempotent: + :idempotent: :tags: [] - :name: system_hook_push :feature_category: :source_code_management - :has_external_dependencies: + :has_external_dependencies: :urgency: :low :resource_boundary: :unknown :weight: 1 - :idempotent: + :idempotent: :tags: [] - :name: update_external_pull_requests :feature_category: :source_code_management - :has_external_dependencies: + :has_external_dependencies: :urgency: :low :resource_boundary: :unknown :weight: 3 - :idempotent: + :idempotent: :tags: [] - :name: update_highest_role :feature_category: :authentication_and_authorization - :has_external_dependencies: + :has_external_dependencies: :urgency: :high :resource_boundary: :unknown :weight: 2 @@ -1766,27 +1774,27 @@ :tags: [] - :name: update_merge_requests :feature_category: :source_code_management - :has_external_dependencies: + :has_external_dependencies: :urgency: :high :resource_boundary: :cpu :weight: 3 - :idempotent: + :idempotent: :tags: [] - :name: update_project_statistics :feature_category: :source_code_management - :has_external_dependencies: + :has_external_dependencies: :urgency: :low :resource_boundary: :unknown :weight: 1 - :idempotent: + :idempotent: :tags: [] - :name: upload_checksum :feature_category: :geo_replication - :has_external_dependencies: + :has_external_dependencies: :urgency: :low :resource_boundary: :unknown :weight: 1 - :idempotent: + :idempotent: :tags: [] - :name: web_hook :feature_category: :integrations @@ -1794,11 +1802,11 @@ :urgency: :low :resource_boundary: :unknown :weight: 1 - :idempotent: + :idempotent: :tags: [] - :name: x509_certificate_revoke :feature_category: :source_code_management - :has_external_dependencies: + :has_external_dependencies: :urgency: :low :resource_boundary: :unknown :weight: 1 diff --git a/app/workers/issue_rebalancing_worker.rb b/app/workers/issue_rebalancing_worker.rb new file mode 100644 index 00000000000..c31b6cfbd86 --- /dev/null +++ b/app/workers/issue_rebalancing_worker.rb @@ -0,0 +1,17 @@ +# frozen_string_literal: true + +class IssueRebalancingWorker + include ApplicationWorker + + idempotent! + urgency :low + feature_category :issue_tracking + + def perform(issue_id) + issue = Issue.find(issue_id) + + IssueRebalancingService.new(issue).execute + rescue ActiveRecord::RecordNotFound, IssueRebalancingService::TooManyIssues => e + Gitlab::ErrorTracking.log_exception(e, issue_id: issue_id) + end +end diff --git a/changelogs/unreleased/ajk-relative-positioning-worker.yml b/changelogs/unreleased/ajk-relative-positioning-worker.yml new file mode 100644 index 00000000000..5fe891ec987 --- /dev/null +++ b/changelogs/unreleased/ajk-relative-positioning-worker.yml @@ -0,0 +1,5 @@ +--- +title: Add background worker to rebalance issues +merge_request: 40124 +author: +type: added diff --git a/config/feature_flags/development/rebalance_issues.yml b/config/feature_flags/development/rebalance_issues.yml new file mode 100644 index 00000000000..4c14824a35d --- /dev/null +++ b/config/feature_flags/development/rebalance_issues.yml @@ -0,0 +1,7 @@ +--- +name: rebalance_issues +introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/40124 +rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/239344 +group: 'group::project management' +type: development +default_enabled: false diff --git a/config/sidekiq_queues.yml b/config/sidekiq_queues.yml index a3e98c77746..0452a9b6621 100644 --- a/config/sidekiq_queues.yml +++ b/config/sidekiq_queues.yml @@ -138,6 +138,8 @@ - 2 - - irker - 1 +- - issue_rebalancing + - 1 - - jira_connect - 1 - - jira_importer diff --git a/doc/user/project/issues/index.md b/doc/user/project/issues/index.md index 87b705bca94..17d9cdb1e2a 100644 --- a/doc/user/project/issues/index.md +++ b/doc/user/project/issues/index.md @@ -112,8 +112,6 @@ and modify them if you have the necessary [permissions](../../permissions.md). #### Real-time sidebar **(CORE ONLY)** > - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/17589) in GitLab 13.3. -> - It cannot be enabled or disabled per-project. -> - It's not recommended for production use. Assignees in the sidebar are updated in real time. This feature is **disabled by default**. To enable, you need to enable [ActionCable in-app mode](https://docs.gitlab.com/omnibus/settings/actioncable.html). diff --git a/doc/user/project/merge_requests/squash_and_merge.md b/doc/user/project/merge_requests/squash_and_merge.md index bea3faf9af5..ef49a026c99 100644 --- a/doc/user/project/merge_requests/squash_and_merge.md +++ b/doc/user/project/merge_requests/squash_and_merge.md @@ -65,14 +65,27 @@ meaningful commit messages and: ## Enabling squash for a merge request Anyone who can create or edit a merge request can choose for it to be squashed -on the merge request form: +on the merge request form. Users can select or unselect the checkbox at the moment +they are creating the merge request: ![Squash commits checkbox on edit form](img/squash_edit_form.png) -This can then be overridden at the time of accepting the merge request: +After the merge request is submitted, Squash and Merge can still be enabled or disabled +by editing the merge request description: + +1. Scroll to the top of the merge request page and click **Edit**. +1. Scroll down to the end of the merge request form and select the checkbox +**Squash commits when merge request is accepted**. + +This setting can then be overridden at the time of accepting the merge request. +At the end of the merge request widget, next to the **Merge** button, the **Squash commits** checkbox +can be either selected or unselected: ![Squash commits checkbox on accept merge request form](img/squash_mr_widget.png) +Note that Squash and Merge might not be available depending on the project's configuration +for [Squash Commit Options](#squash-commits-options). + ## Commit metadata for squashed commits The squashed commit has the following metadata: diff --git a/qa/qa/specs/features/browser_ui/3_create/repository/move_project_create_fork_spec.rb b/qa/qa/specs/features/browser_ui/3_create/repository/move_project_create_fork_spec.rb index c9dc82baf41..db1cf1acea9 100644 --- a/qa/qa/specs/features/browser_ui/3_create/repository/move_project_create_fork_spec.rb +++ b/qa/qa/specs/features/browser_ui/3_create/repository/move_project_create_fork_spec.rb @@ -1,8 +1,8 @@ # frozen_string_literal: true module QA - RSpec.describe 'Create', quarantine: { issue: 'https://gitlab.com/gitlab-org/gitlab/-/issues/217002', type: :investigating } do - describe 'Gitaly repository storage', :orchestrated, :repository_storage, :requires_admin do + RSpec.describe 'Create', :orchestrated, :repository_storage, :requires_admin, quarantine: { issue: 'https://gitlab.com/gitlab-org/gitlab/-/issues/217002', type: :investigating } do + describe 'Gitaly repository storage' do let(:user) { Resource::User.fabricate_or_use(Runtime::Env.gitlab_qa_username_1, Runtime::Env.gitlab_qa_password_1) } let(:parent_project) do Resource::Project.fabricate_via_api! do |project| diff --git a/spec/services/issue_rebalancing_service_spec.rb b/spec/services/issue_rebalancing_service_spec.rb new file mode 100644 index 00000000000..94f594c8083 --- /dev/null +++ b/spec/services/issue_rebalancing_service_spec.rb @@ -0,0 +1,101 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe IssueRebalancingService do + let_it_be(:project) { create(:project) } + let_it_be(:user) { project.creator } + let_it_be(:start) { RelativePositioning::START_POSITION } + let_it_be(:max_pos) { RelativePositioning::MAX_POSITION } + let_it_be(:min_pos) { RelativePositioning::MIN_POSITION } + let_it_be(:clump_size) { 300 } + + let_it_be(:unclumped) do + (0..clump_size).to_a.map do |i| + create(:issue, project: project, author: user, relative_position: start + (1024 * i)) + end + end + + let_it_be(:end_clump) do + (0..clump_size).to_a.map do |i| + create(:issue, project: project, author: user, relative_position: max_pos - i) + end + end + + let_it_be(:start_clump) do + (0..clump_size).to_a.map do |i| + create(:issue, project: project, author: user, relative_position: min_pos + i) + end + end + + def issues_in_position_order + project.reload.issues.reorder(relative_position: :asc).to_a + end + + it 'rebalances a set of issues with clumps at the end and start' do + all_issues = start_clump + unclumped + end_clump.reverse + service = described_class.new(project.issues.first) + + expect { service.execute }.not_to change { issues_in_position_order.map(&:id) } + + all_issues.each(&:reset) + + gaps = all_issues.take(all_issues.count - 1).zip(all_issues.drop(1)).map do |a, b| + b.relative_position - a.relative_position + end + + expect(gaps).to all(be > RelativePositioning::MIN_GAP) + expect(all_issues.first.relative_position).to be > (RelativePositioning::MIN_POSITION * 0.9999) + expect(all_issues.last.relative_position).to be < (RelativePositioning::MAX_POSITION * 0.9999) + end + + it 'is idempotent' do + service = described_class.new(project.issues.first) + + expect do + service.execute + service.execute + end.not_to change { issues_in_position_order.map(&:id) } + end + + it 'does nothing if the feature flag is disabled' do + stub_feature_flags(rebalance_issues: false) + issue = project.issues.first + issue.project + issue.project.group + old_pos = issue.relative_position + + service = described_class.new(issue) + + expect { service.execute }.not_to exceed_query_limit(0) + expect(old_pos).to eq(issue.reload.relative_position) + end + + it 'acts if the flag is enabled for the project' do + issue = create(:issue, project: project, author: user, relative_position: max_pos) + stub_feature_flags(rebalance_issues: issue.project) + + service = described_class.new(issue) + + expect { service.execute }.to change { issue.reload.relative_position } + end + + it 'acts if the flag is enabled for the group' do + issue = create(:issue, project: project, author: user, relative_position: max_pos) + project.update!(group: create(:group)) + stub_feature_flags(rebalance_issues: issue.project.group) + + service = described_class.new(issue) + + expect { service.execute }.to change { issue.reload.relative_position } + end + + it 'aborts if there are too many issues' do + issue = project.issues.first + base = double(count: 10_001) + + allow(Issue).to receive(:relative_positioning_query_base).with(issue).and_return(base) + + expect { described_class.new(issue).execute }.to raise_error(described_class::TooManyIssues) + end +end diff --git a/spec/services/issues/create_service_spec.rb b/spec/services/issues/create_service_spec.rb index fdf2326b75e..004c37a0a7a 100644 --- a/spec/services/issues/create_service_spec.rb +++ b/spec/services/issues/create_service_spec.rb @@ -75,6 +75,37 @@ RSpec.describe Issues::CreateService do expect(Todo.where(attributes).count).to eq 1 end + it 'rebalances if needed' do + create(:issue, project: project, relative_position: RelativePositioning::MAX_POSITION) + expect(IssueRebalancingWorker).to receive(:perform_async).with(Integer) + + expect(issue.relative_position).to eq(project.issues.maximum(:relative_position)) + end + + it 'does not rebalance if the flag is disabled' do + stub_feature_flags(rebalance_issues: false) + + create(:issue, project: project, relative_position: RelativePositioning::MAX_POSITION) + expect(IssueRebalancingWorker).not_to receive(:perform_async).with(Integer) + + expect(issue.relative_position).to eq(project.issues.maximum(:relative_position)) + end + + it 'does rebalance if the flag is enabled for the project' do + stub_feature_flags(rebalance_issues: project) + + create(:issue, project: project, relative_position: RelativePositioning::MAX_POSITION) + expect(IssueRebalancingWorker).to receive(:perform_async).with(Integer) + + expect(issue.relative_position).to eq(project.issues.maximum(:relative_position)) + end + + it 'does not rebalance unless needed' do + expect(IssueRebalancingWorker).not_to receive(:perform_async) + + expect(issue.relative_position).to eq(project.issues.maximum(:relative_position)) + end + context 'when label belongs to project group' do let(:group) { create(:group) } let(:group_labels) { create_pair(:group_label, group: group) } diff --git a/spec/services/issues/update_service_spec.rb b/spec/services/issues/update_service_spec.rb index 42452e95f6b..aa9ce4ce1a9 100644 --- a/spec/services/issues/update_service_spec.rb +++ b/spec/services/issues/update_service_spec.rb @@ -106,7 +106,7 @@ RSpec.describe Issues::UpdateService, :mailer do [issue, issue1, issue2].each do |issue| issue.move_to_end - issue.save + issue.save! end opts[:move_between_ids] = [issue1.id, issue2.id] @@ -116,6 +116,66 @@ RSpec.describe Issues::UpdateService, :mailer do expect(issue.relative_position).to be_between(issue1.relative_position, issue2.relative_position) end + it 'does not rebalance even if needed if the flag is disabled' do + stub_feature_flags(rebalance_issues: false) + + range = described_class::NO_REBALANCING_NEEDED + issue1 = create(:issue, project: project, relative_position: range.first - 100) + issue2 = create(:issue, project: project, relative_position: range.first) + issue.update!(relative_position: RelativePositioning::START_POSITION) + + opts[:move_between_ids] = [issue1.id, issue2.id] + + expect(IssueRebalancingWorker).not_to receive(:perform_async).with(issue.id) + + update_issue(opts) + expect(issue.relative_position).to be_between(issue1.relative_position, issue2.relative_position) + end + + it 'rebalances if needed if the flag is enabled for the project' do + stub_feature_flags(rebalance_issues: project) + + range = described_class::NO_REBALANCING_NEEDED + issue1 = create(:issue, project: project, relative_position: range.first - 100) + issue2 = create(:issue, project: project, relative_position: range.first) + issue.update!(relative_position: RelativePositioning::START_POSITION) + + opts[:move_between_ids] = [issue1.id, issue2.id] + + expect(IssueRebalancingWorker).to receive(:perform_async).with(issue.id) + + update_issue(opts) + expect(issue.relative_position).to be_between(issue1.relative_position, issue2.relative_position) + end + + it 'rebalances if needed on the left' do + range = described_class::NO_REBALANCING_NEEDED + issue1 = create(:issue, project: project, relative_position: range.first - 100) + issue2 = create(:issue, project: project, relative_position: range.first) + issue.update!(relative_position: RelativePositioning::START_POSITION) + + opts[:move_between_ids] = [issue1.id, issue2.id] + + expect(IssueRebalancingWorker).to receive(:perform_async).with(issue.id) + + update_issue(opts) + expect(issue.relative_position).to be_between(issue1.relative_position, issue2.relative_position) + end + + it 'rebalances if needed on the right' do + range = described_class::NO_REBALANCING_NEEDED + issue1 = create(:issue, project: project, relative_position: range.last) + issue2 = create(:issue, project: project, relative_position: range.last + 100) + issue.update!(relative_position: RelativePositioning::START_POSITION) + + opts[:move_between_ids] = [issue1.id, issue2.id] + + expect(IssueRebalancingWorker).to receive(:perform_async).with(issue.id) + + update_issue(opts) + expect(issue.relative_position).to be_between(issue1.relative_position, issue2.relative_position) + end + context 'when moving issue between issues from different projects' do let(:group) { create(:group) } let(:subgroup) { create(:group, parent: group) } diff --git a/spec/workers/issue_rebalancing_worker_spec.rb b/spec/workers/issue_rebalancing_worker_spec.rb new file mode 100644 index 00000000000..a2e44e74e06 --- /dev/null +++ b/spec/workers/issue_rebalancing_worker_spec.rb @@ -0,0 +1,32 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe IssueRebalancingWorker do + describe '#perform' do + let_it_be(:issue) { create(:issue) } + + it 'runs an instance of IssueRebalancingService' do + service = double(execute: nil) + expect(IssueRebalancingService).to receive(:new).with(issue).and_return(service) + + described_class.new.perform(issue.id) + end + + it 'anticipates the inability to find the issue' do + expect(Gitlab::ErrorTracking).to receive(:log_exception).with(ActiveRecord::RecordNotFound, include(issue_id: -1)) + expect(IssueRebalancingService).not_to receive(:new) + + described_class.new.perform(-1) + end + + it 'anticipates there being too many issues' do + service = double + allow(service).to receive(:execute) { raise IssueRebalancingService::TooManyIssues } + expect(IssueRebalancingService).to receive(:new).with(issue).and_return(service) + expect(Gitlab::ErrorTracking).to receive(:log_exception).with(IssueRebalancingService::TooManyIssues, include(issue_id: issue.id)) + + described_class.new.perform(issue.id) + end + end +end