2020-06-23 00:09:42 +05:30
# frozen_string_literal: true
require 'redis'
module Gitlab
module Instrumentation
class RedisBase
class << self
include :: Gitlab :: Utils :: StrongMemoize
include :: Gitlab :: Instrumentation :: RedisPayload
# TODO: To be used by https://gitlab.com/gitlab-com/gl-infra/scalability/-/issues/395
# as a 'label' alias.
def storage_key
self . name . demodulize . underscore
end
def add_duration ( duration )
:: RequestStore [ call_duration_key ] || = 0
:: RequestStore [ call_duration_key ] += duration
end
2022-10-11 01:57:18 +05:30
def add_call_details ( duration , commands )
2020-06-23 00:09:42 +05:30
return unless Gitlab :: PerformanceBar . enabled_for_request?
detail_store << {
2022-10-11 01:57:18 +05:30
commands : commands ,
2020-06-23 00:09:42 +05:30
duration : duration ,
backtrace : :: Gitlab :: BacktraceCleaner . clean_backtrace ( caller )
}
end
2022-10-11 01:57:18 +05:30
def increment_request_count ( amount = 1 )
2020-06-23 00:09:42 +05:30
:: RequestStore [ request_count_key ] || = 0
2022-10-11 01:57:18 +05:30
:: RequestStore [ request_count_key ] += amount
2020-06-23 00:09:42 +05:30
end
def increment_read_bytes ( num_bytes )
:: RequestStore [ read_bytes_key ] || = 0
:: RequestStore [ read_bytes_key ] += num_bytes
end
def increment_write_bytes ( num_bytes )
:: RequestStore [ write_bytes_key ] || = 0
:: RequestStore [ write_bytes_key ] += num_bytes
end
2023-03-04 22:38:38 +05:30
def increment_cross_slot_request_count ( amount = 1 )
:: RequestStore [ cross_slots_key ] || = 0
:: RequestStore [ cross_slots_key ] += amount
end
def increment_allowed_cross_slot_request_count ( amount = 1 )
:: RequestStore [ allowed_cross_slots_key ] || = 0
:: RequestStore [ allowed_cross_slots_key ] += amount
end
2020-06-23 00:09:42 +05:30
def get_request_count
:: RequestStore [ request_count_key ] || 0
end
def read_bytes
:: RequestStore [ read_bytes_key ] || 0
end
def write_bytes
:: RequestStore [ write_bytes_key ] || 0
end
def detail_store
:: RequestStore [ call_details_key ] || = [ ]
end
2023-03-04 22:38:38 +05:30
def get_cross_slot_request_count
:: RequestStore [ cross_slots_key ] || 0
end
def get_allowed_cross_slot_request_count
:: RequestStore [ allowed_cross_slots_key ] || 0
end
2020-06-23 00:09:42 +05:30
def query_time
query_time = :: RequestStore [ call_duration_key ] || 0
query_time . round ( :: Gitlab :: InstrumentationHelper :: DURATION_PRECISION )
end
2023-01-13 00:05:48 +05:30
def redis_cluster_validate! ( commands )
2023-03-04 22:38:38 +05:30
return true unless @redis_cluster_validation
result = :: Gitlab :: Instrumentation :: RedisClusterValidator . validate ( commands )
return true if result . nil?
if ! result [ :valid ] && ! result [ :allowed ] && ( Rails . env . development? || Rails . env . test? )
raise RedisClusterValidator :: CrossSlotError , " Redis command #{ result [ :command_name ] } arguments hash to different slots. See https://docs.gitlab.com/ee/development/redis.html # multi-key-commands "
end
increment_allowed_cross_slot_request_count if result [ :allowed ]
result [ :valid ]
2020-07-28 23:09:34 +05:30
end
def enable_redis_cluster_validation
@redis_cluster_validation = true
self
end
2022-10-11 01:57:18 +05:30
def instance_count_request ( amount = 1 )
2020-07-28 23:09:34 +05:30
@request_counter || = Gitlab :: Metrics . counter ( :gitlab_redis_client_requests_total , 'Client side Redis request count, per Redis server' )
2022-10-11 01:57:18 +05:30
@request_counter . increment ( { storage : storage_key } , amount )
2020-07-28 23:09:34 +05:30
end
def instance_count_exception ( ex )
# This metric is meant to give a client side view of how the Redis
# server is doing. Redis itself does not expose error counts. This
# metric can be used for Redis alerting and service health monitoring.
@exception_counter || = Gitlab :: Metrics . counter ( :gitlab_redis_client_exceptions_total , 'Client side Redis exception count, per Redis server, per exception class' )
@exception_counter . increment ( { storage : storage_key , exception : ex . class . to_s } )
end
def instance_observe_duration ( duration )
@request_latency_histogram || = Gitlab :: Metrics . histogram (
:gitlab_redis_client_requests_duration_seconds ,
'Client side Redis request latency, per Redis server, excluding blocking commands' ,
{ } ,
2020-10-24 23:57:45 +05:30
[ 0 . 1 , 0 . 5 , 0 . 75 , 1 ]
2020-07-28 23:09:34 +05:30
)
@request_latency_histogram . observe ( { storage : storage_key } , duration )
end
2020-06-23 00:09:42 +05:30
private
def request_count_key
strong_memoize ( :request_count_key ) { build_key ( :redis_request_count ) }
end
def read_bytes_key
strong_memoize ( :read_bytes_key ) { build_key ( :redis_read_bytes ) }
end
def write_bytes_key
strong_memoize ( :write_bytes_key ) { build_key ( :redis_write_bytes ) }
end
def call_duration_key
strong_memoize ( :call_duration_key ) { build_key ( :redis_call_duration ) }
end
def call_details_key
strong_memoize ( :call_details_key ) { build_key ( :redis_call_details ) }
end
2023-03-04 22:38:38 +05:30
def cross_slots_key
strong_memoize ( :cross_slots_key ) { build_key ( :redis_cross_slot_request_count ) }
end
def allowed_cross_slots_key
strong_memoize ( :allowed_cross_slots_key ) { build_key ( :redis_allowed_cross_slot_request_count ) }
end
2020-06-23 00:09:42 +05:30
def build_key ( namespace )
" #{ storage_key } _ #{ namespace } "
end
end
end
end
end