diff options
Diffstat (limited to 'app/services/merge_requests/mergeability')
3 files changed, 178 insertions, 1 deletions
diff --git a/app/services/merge_requests/mergeability/detailed_merge_status_service.rb b/app/services/merge_requests/mergeability/detailed_merge_status_service.rb new file mode 100644 index 00000000000..d25234183fd --- /dev/null +++ b/app/services/merge_requests/mergeability/detailed_merge_status_service.rb @@ -0,0 +1,63 @@ +# frozen_string_literal: true + +module MergeRequests + module Mergeability + class DetailedMergeStatusService + include ::Gitlab::Utils::StrongMemoize + + def initialize(merge_request:) + @merge_request = merge_request + end + + def execute + return :checking if checking? + return :unchecked if unchecked? + + if check_results.success? + + # If everything else is mergeable, but CI is not, the frontend expects two potential states to be returned + # See discussion: gitlab.com/gitlab-org/gitlab/-/merge_requests/96778#note_1093063523 + if check_ci_results.success? + :mergeable + else + ci_check_failure_reason + end + else + check_results.failure_reason + end + end + + private + + attr_reader :merge_request, :checks, :ci_check + + def checking? + merge_request.cannot_be_merged_rechecking? || merge_request.preparing? || merge_request.checking? + end + + def unchecked? + merge_request.unchecked? + end + + def check_results + strong_memoize(:check_results) do + merge_request.execute_merge_checks(params: { skip_ci_check: true }) + end + end + + def check_ci_results + strong_memoize(:check_ci_results) do + ::MergeRequests::Mergeability::CheckCiStatusService.new(merge_request: merge_request, params: {}).execute + end + end + + def ci_check_failure_reason + if merge_request.actual_head_pipeline&.running? + :ci_still_running + else + check_ci_results.payload.fetch(:reason) + end + end + end + end +end diff --git a/app/services/merge_requests/mergeability/logger.rb b/app/services/merge_requests/mergeability/logger.rb new file mode 100644 index 00000000000..8b45d231e03 --- /dev/null +++ b/app/services/merge_requests/mergeability/logger.rb @@ -0,0 +1,103 @@ +# frozen_string_literal: true + +module MergeRequests + module Mergeability + class Logger + include Gitlab::Utils::StrongMemoize + + def initialize(merge_request:, destination: Gitlab::AppJsonLogger) + @destination = destination + @merge_request = merge_request + end + + def commit + return unless enabled? + + commit_logs + end + + def instrument(mergeability_name:) + raise ArgumentError, 'block not given' unless block_given? + + return yield unless enabled? + + op_start_db_counters = current_db_counter_payload + op_started_at = current_monotonic_time + + result = yield + + observe("mergeability.#{mergeability_name}.duration_s", current_monotonic_time - op_started_at) + + observe_sql_counters(mergeability_name, op_start_db_counters, current_db_counter_payload) + + result + end + + private + + attr_reader :destination, :merge_request + + def observe(name, value) + return unless enabled? + + observations[name.to_s].push(value) + end + + def commit_logs + attributes = Gitlab::ApplicationContext.current.merge({ + mergeability_project_id: merge_request.project.id + }) + + attributes[:mergeability_merge_request_id] = merge_request.id + attributes.merge!(observations_hash) + attributes.compact! + attributes.stringify_keys! + + destination.info(attributes) + end + + def observations_hash + transformed = observations.transform_values do |values| + next if values.empty? + + { + 'values' => values + } + end.compact + + transformed.each_with_object({}) do |key, hash| + key[1].each { |k, v| hash["#{key[0]}.#{k}"] = v } + end + end + + def observations + strong_memoize(:observations) do + Hash.new { |hash, key| hash[key] = [] } + end + end + + def observe_sql_counters(name, start_db_counters, end_db_counters) + end_db_counters.each do |key, value| + result = value - start_db_counters.fetch(key, 0) + next if result == 0 + + observe("mergeability.#{name}.#{key}", result) + end + end + + def current_db_counter_payload + ::Gitlab::Metrics::Subscribers::ActiveRecord.db_counter_payload + end + + def enabled? + strong_memoize(:enabled) do + ::Feature.enabled?(:mergeability_checks_logger, merge_request.project) + end + end + + def current_monotonic_time + ::Gitlab::Metrics::System.monotonic_time + end + end + end +end diff --git a/app/services/merge_requests/mergeability/run_checks_service.rb b/app/services/merge_requests/mergeability/run_checks_service.rb index 68f842b3322..7f205c8dd6c 100644 --- a/app/services/merge_requests/mergeability/run_checks_service.rb +++ b/app/services/merge_requests/mergeability/run_checks_service.rb @@ -15,12 +15,17 @@ module MergeRequests next if check.skip? - check_result = run_check(check) + check_result = logger.instrument(mergeability_name: check_class.to_s.demodulize.underscore) do + run_check(check) + end + result_hash << check_result break result_hash if check_result.failed? end + logger.commit + self end @@ -57,6 +62,12 @@ module MergeRequests Gitlab::MergeRequests::Mergeability::ResultsStore.new(merge_request: merge_request) end end + + def logger + strong_memoize(:logger) do + MergeRequests::Mergeability::Logger.new(merge_request: merge_request) + end + end end end end |