diff options
Diffstat (limited to 'lib/gitlab/metrics')
-rw-r--r-- | lib/gitlab/metrics/background_transaction.rb | 49 | ||||
-rw-r--r-- | lib/gitlab/metrics/samplers/ruby_sampler.rb | 2 | ||||
-rw-r--r-- | lib/gitlab/metrics/subscribers/action_cable.rb | 72 | ||||
-rw-r--r-- | lib/gitlab/metrics/subscribers/active_record.rb | 56 |
4 files changed, 158 insertions, 21 deletions
diff --git a/lib/gitlab/metrics/background_transaction.rb b/lib/gitlab/metrics/background_transaction.rb new file mode 100644 index 00000000000..3dda68bf93f --- /dev/null +++ b/lib/gitlab/metrics/background_transaction.rb @@ -0,0 +1,49 @@ +# frozen_string_literal: true + +module Gitlab + module Metrics + class BackgroundTransaction < Transaction + # Separate web transaction instance and background transaction instance + BACKGROUND_THREAD_KEY = :_gitlab_metrics_background_transaction + BACKGROUND_BASE_LABEL_KEYS = %i(endpoint_id feature_category).freeze + + class << self + def current + Thread.current[BACKGROUND_THREAD_KEY] + end + + def prometheus_metric(name, type, &block) + fetch_metric(type, name) do + # set default metric options + docstring "#{name.to_s.humanize} #{type}" + + evaluate(&block) + # always filter sensitive labels and merge with base ones + label_keys BACKGROUND_BASE_LABEL_KEYS | (label_keys - ::Gitlab::Metrics::Transaction::FILTERED_LABEL_KEYS) + end + end + end + + def run + Thread.current[BACKGROUND_THREAD_KEY] = self + + yield + ensure + Thread.current[BACKGROUND_THREAD_KEY] = nil + end + + def labels + @labels ||= { + endpoint_id: current_context&.get_attribute(:caller_id), + feature_category: current_context&.get_attribute(:feature_category) + } + end + + private + + def current_context + Labkit::Context.current + end + end + end +end diff --git a/lib/gitlab/metrics/samplers/ruby_sampler.rb b/lib/gitlab/metrics/samplers/ruby_sampler.rb index 76175b465e4..3d29d38fa1f 100644 --- a/lib/gitlab/metrics/samplers/ruby_sampler.rb +++ b/lib/gitlab/metrics/samplers/ruby_sampler.rb @@ -7,7 +7,7 @@ module Gitlab module Samplers class RubySampler < BaseSampler DEFAULT_SAMPLING_INTERVAL_SECONDS = 60 - GC_REPORT_BUCKETS = [0.005, 0.01, 0.02, 0.04, 0.07, 0.1, 0.5].freeze + GC_REPORT_BUCKETS = [0.01, 0.05, 0.1, 0.2, 0.3, 0.5, 1].freeze def initialize(*) GC::Profiler.clear diff --git a/lib/gitlab/metrics/subscribers/action_cable.rb b/lib/gitlab/metrics/subscribers/action_cable.rb new file mode 100644 index 00000000000..a9355eeae40 --- /dev/null +++ b/lib/gitlab/metrics/subscribers/action_cable.rb @@ -0,0 +1,72 @@ +# frozen_string_literal: true + +module Gitlab + module Metrics + module Subscribers + class ActionCable < ActiveSupport::Subscriber + include Gitlab::Utils::StrongMemoize + + attach_to :action_cable + + SINGLE_CLIENT_TRANSMISSION = :action_cable_single_client_transmissions_total + TRANSMIT_SUBSCRIPTION_CONFIRMATION = :action_cable_subscription_confirmations_total + TRANSMIT_SUBSCRIPTION_REJECTION = :action_cable_subscription_rejections_total + BROADCAST = :action_cable_broadcasts_total + + def transmit_subscription_confirmation(event) + confirm_subscription_counter.increment + end + + def transmit_subscription_rejection(event) + reject_subscription_counter.increment + end + + def transmit(event) + transmit_counter.increment + end + + def broadcast(event) + broadcast_counter.increment + end + + private + + def transmit_counter + strong_memoize("transmission_counter") do + ::Gitlab::Metrics.counter( + SINGLE_CLIENT_TRANSMISSION, + 'The number of ActionCable messages transmitted to any client in any channel' + ) + end + end + + def broadcast_counter + strong_memoize("broadcast_counter") do + ::Gitlab::Metrics.counter( + BROADCAST, + 'The number of ActionCable broadcasts emitted' + ) + end + end + + def confirm_subscription_counter + strong_memoize("confirm_subscription_counter") do + ::Gitlab::Metrics.counter( + TRANSMIT_SUBSCRIPTION_CONFIRMATION, + 'The number of ActionCable subscriptions from clients confirmed' + ) + end + end + + def reject_subscription_counter + strong_memoize("reject_subscription_counter") do + ::Gitlab::Metrics.counter( + TRANSMIT_SUBSCRIPTION_REJECTION, + 'The number of ActionCable subscriptions from clients rejected' + ) + end + end + end + end + end +end diff --git a/lib/gitlab/metrics/subscribers/active_record.rb b/lib/gitlab/metrics/subscribers/active_record.rb index d725d8d7b29..5eefef02507 100644 --- a/lib/gitlab/metrics/subscribers/active_record.rb +++ b/lib/gitlab/metrics/subscribers/active_record.rb @@ -9,6 +9,16 @@ module Gitlab IGNORABLE_SQL = %w{BEGIN COMMIT}.freeze DB_COUNTERS = %i{db_count db_write_count db_cached_count}.freeze + SQL_COMMANDS_WITH_COMMENTS_REGEX = /\A(\/\*.*\*\/\s)?((?!(.*[^\w'"](DELETE|UPDATE|INSERT INTO)[^\w'"])))(WITH.*)?(SELECT)((?!(FOR UPDATE|FOR SHARE)).)*$/i.freeze + + DURATION_BUCKET = [0.05, 0.1, 0.25].freeze + + # This event is published from ActiveRecordBaseTransactionMetrics and + # used to record a database transaction duration when calling + # ActiveRecord::Base.transaction {} block. + def transaction(event) + observe(:gitlab_database_transaction_seconds, event) + end def sql(event) # Mark this thread as requiring a database connection. This is used @@ -17,51 +27,57 @@ module Gitlab Thread.current[:uses_db_connection] = true payload = event.payload - return if payload[:name] == 'SCHEMA' || IGNORABLE_SQL.include?(payload[:sql]) + return if ignored_query?(payload) - increment_db_counters(payload) + increment(:db_count) + increment(:db_cached_count) if cached_query?(payload) + increment(:db_write_count) unless select_sql_command?(payload) - current_transaction&.observe(:gitlab_sql_duration_seconds, event.duration / 1000.0) do - buckets [0.05, 0.1, 0.25] - end + observe(:gitlab_sql_duration_seconds, event) end def self.db_counter_payload return {} unless Gitlab::SafeRequestStore.active? - DB_COUNTERS.map do |counter| - [counter, Gitlab::SafeRequestStore[counter].to_i] - end.to_h + payload = {} + DB_COUNTERS.each do |counter| + payload[counter] = Gitlab::SafeRequestStore[counter].to_i + end + payload end private - def select_sql_command?(payload) - payload[:sql].match(/\A((?!(.*[^\w'"](DELETE|UPDATE|INSERT INTO)[^\w'"])))(WITH.*)?(SELECT)((?!(FOR UPDATE|FOR SHARE)).)*$/i) + def ignored_query?(payload) + payload[:name] == 'SCHEMA' || IGNORABLE_SQL.include?(payload[:sql]) end - def increment_db_counters(payload) - increment(:db_count) - - if payload.fetch(:cached, payload[:name] == 'CACHE') - increment(:db_cached_count) - end + def cached_query?(payload) + payload.fetch(:cached, payload[:name] == 'CACHE') + end - increment(:db_write_count) unless select_sql_command?(payload) + def select_sql_command?(payload) + payload[:sql].match(SQL_COMMANDS_WITH_COMMENTS_REGEX) end def increment(counter) current_transaction&.increment("gitlab_transaction_#{counter}_total".to_sym, 1) - if Gitlab::SafeRequestStore.active? - Gitlab::SafeRequestStore[counter] = Gitlab::SafeRequestStore[counter].to_i + 1 + Gitlab::SafeRequestStore[counter] = Gitlab::SafeRequestStore[counter].to_i + 1 + end + + def observe(histogram, event) + current_transaction&.observe(histogram, event.duration / 1000.0) do + buckets DURATION_BUCKET end end def current_transaction - ::Gitlab::Metrics::Transaction.current + ::Gitlab::Metrics::WebTransaction.current || ::Gitlab::Metrics::BackgroundTransaction.current end end end end end + +Gitlab::Metrics::Subscribers::ActiveRecord.prepend_if_ee('EE::Gitlab::Metrics::Subscribers::ActiveRecord') |