Class: Gitlab::Instrumentation::RedisBase

Inherits:
Object
  • Object
show all
Extended by:
RedisPayload, Utils::StrongMemoize
Defined in:
lib/gitlab/instrumentation/redis_base.rb

Constant Summary collapse

DEFAULT_SHARD_KEY =
'default'

Class Method Summary collapse

Methods included from RedisPayload

payload

Class Method Details

.add_call_details(duration, commands) ⇒ Object



38
39
40
41
42
43
44
45
46
# File 'lib/gitlab/instrumentation/redis_base.rb', line 38

def add_call_details(duration, commands)
  return unless Gitlab::PerformanceBar.enabled_for_request?

  detail_store << {
    commands: commands,
    duration: duration,
    backtrace: ::Gitlab::BacktraceCleaner.clean_backtrace(caller)
  }
end

.add_duration(duration) ⇒ Object



33
34
35
36
# File 'lib/gitlab/instrumentation/redis_base.rb', line 33

def add_duration(duration)
  ::RequestStore[call_duration_key] ||= 0
  ::RequestStore[call_duration_key] += duration
end

.detail_storeObject



85
86
87
# File 'lib/gitlab/instrumentation/redis_base.rb', line 85

def detail_store
  ::RequestStore[call_details_key] ||= []
end

.enable_redis_cluster_validationObject



117
118
119
120
121
# File 'lib/gitlab/instrumentation/redis_base.rb', line 117

def enable_redis_cluster_validation
  @redis_cluster_validation = true

  self
end

.get_allowed_cross_slot_request_countObject



93
94
95
# File 'lib/gitlab/instrumentation/redis_base.rb', line 93

def get_allowed_cross_slot_request_count
  ::RequestStore[allowed_cross_slots_key] || 0
end

.get_cross_slot_request_countObject



89
90
91
# File 'lib/gitlab/instrumentation/redis_base.rb', line 89

def get_cross_slot_request_count
  ::RequestStore[cross_slots_key] || 0
end

.get_request_countObject



73
74
75
# File 'lib/gitlab/instrumentation/redis_base.rb', line 73

def get_request_count
  ::RequestStore[request_count_key] || 0
end

.increment_allowed_cross_slot_request_count(amount = 1) ⇒ Object



68
69
70
71
# File 'lib/gitlab/instrumentation/redis_base.rb', line 68

def increment_allowed_cross_slot_request_count(amount = 1)
  ::RequestStore[allowed_cross_slots_key] ||= 0
  ::RequestStore[allowed_cross_slots_key] += amount
end

.increment_cross_slot_request_count(amount = 1) ⇒ Object



63
64
65
66
# File 'lib/gitlab/instrumentation/redis_base.rb', line 63

def increment_cross_slot_request_count(amount = 1)
  ::RequestStore[cross_slots_key] ||= 0
  ::RequestStore[cross_slots_key] += amount
end

.increment_read_bytes(num_bytes) ⇒ Object



53
54
55
56
# File 'lib/gitlab/instrumentation/redis_base.rb', line 53

def increment_read_bytes(num_bytes)
  ::RequestStore[read_bytes_key] ||= 0
  ::RequestStore[read_bytes_key] += num_bytes
end

.increment_request_count(amount = 1) ⇒ Object



48
49
50
51
# File 'lib/gitlab/instrumentation/redis_base.rb', line 48

def increment_request_count(amount = 1)
  ::RequestStore[request_count_key] ||= 0
  ::RequestStore[request_count_key] += amount
end

.increment_write_bytes(num_bytes) ⇒ Object



58
59
60
61
# File 'lib/gitlab/instrumentation/redis_base.rb', line 58

def increment_write_bytes(num_bytes)
  ::RequestStore[write_bytes_key] ||= 0
  ::RequestStore[write_bytes_key] += num_bytes
end

.instance_count_cluster_pipeline_redirection(ex) ⇒ Object



159
160
161
162
163
164
165
166
167
168
169
170
# File 'lib/gitlab/instrumentation/redis_base.rb', line 159

def instance_count_cluster_pipeline_redirection(ex)
  @pipeline_redirection_histogram ||= Gitlab::Metrics.histogram(
    :gitlab_redis_client_pipeline_redirections_count,
    'Client side Redis Cluster pipeline redirection counts per pipeline',
    {},
    [10, 100, 250, 500]
  )

  # RedisClient::Cluster::Pipeline::RedirectionNeeded has `replies` and `indices`.
  # The latter is a list of redirection indices which indicates the volume of redirections per pipeline.
  @pipeline_redirection_histogram.observe(storage_labels, (ex.indices && ex.indices.size).to_i)
end

.instance_count_cluster_redirection(ex) ⇒ Object



151
152
153
154
155
156
157
# File 'lib/gitlab/instrumentation/redis_base.rb', line 151

def instance_count_cluster_redirection(ex)
  # This metric is meant to give a client side view of how often are commands
  # redirected to the right node, especially during resharding..
  # This metric can be used for Redis alerting and service health monitoring.
  @redirection_counter ||= Gitlab::Metrics.counter(:gitlab_redis_client_redirections_total, 'Client side Redis Cluster redirection count, per Redis node, per slot')
  @redirection_counter.increment(decompose_redirection_message(ex.message).merge(storage_labels))
end

.instance_count_connection_exception(ex) ⇒ Object



146
147
148
149
# File 'lib/gitlab/instrumentation/redis_base.rb', line 146

def instance_count_connection_exception(ex)
  @connection_exception_counter ||= Gitlab::Metrics.counter(:gitlab_redis_client_connection_exceptions_total, 'Client side Redis connection exception count, per Redis server, per exception class')
  @connection_exception_counter.increment(storage_labels.merge(exception: ex.class.to_s))
end

.instance_count_exception(ex) ⇒ Object



138
139
140
141
142
143
144
# File 'lib/gitlab/instrumentation/redis_base.rb', line 138

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_labels.merge(exception: ex.class.to_s))
end

.instance_count_pipelined_request(size) ⇒ Object



128
129
130
131
132
133
134
135
136
# File 'lib/gitlab/instrumentation/redis_base.rb', line 128

def instance_count_pipelined_request(size)
  @pipeline_size_histogram ||= Gitlab::Metrics.histogram(
    :gitlab_redis_client_requests_pipelined_commands,
    'Client side Redis request pipeline size, per Redis server',
    {},
    [10, 100, 1000, 10_000]
  )
  @pipeline_size_histogram.observe(storage_labels, size)
end

.instance_count_request(amount = 1) ⇒ Object



123
124
125
126
# File 'lib/gitlab/instrumentation/redis_base.rb', line 123

def instance_count_request(amount = 1)
  @request_counter ||= Gitlab::Metrics.counter(:gitlab_redis_client_requests_total, 'Client side Redis request count, per Redis server')
  @request_counter.increment(storage_labels, amount)
end

.instance_observe_duration(duration) ⇒ Object



172
173
174
175
176
177
178
179
180
181
# File 'lib/gitlab/instrumentation/redis_base.rb', line 172

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',
    {},
    [0.1, 0.5, 0.75, 1]
  )

  @request_latency_histogram.observe(storage_labels, duration)
end

.log_exception(ex) ⇒ Object



183
184
185
# File 'lib/gitlab/instrumentation/redis_base.rb', line 183

def log_exception(ex)
  ::Gitlab::ErrorTracking.log_exception(ex, **storage_labels)
end

.query_timeObject



97
98
99
100
# File 'lib/gitlab/instrumentation/redis_base.rb', line 97

def query_time
  query_time = ::RequestStore[call_duration_key] || 0
  query_time.round(::Gitlab::InstrumentationHelper::DURATION_PRECISION)
end

.read_bytesObject



77
78
79
# File 'lib/gitlab/instrumentation/redis_base.rb', line 77

def read_bytes
  ::RequestStore[read_bytes_key] || 0
end

.redis_cluster_validate!(commands) ⇒ Object



102
103
104
105
106
107
108
109
110
111
112
113
114
115
# File 'lib/gitlab/instrumentation/redis_base.rb', line 102

def redis_cluster_validate!(commands)
  return true unless @redis_cluster_validation

  result = ::Gitlab::Instrumentation::RedisClusterValidator.validate(commands)
  return true if result.nil?

  if !result[:valid] && !result[:allowed] && raise_cross_slot_validation_errors?
    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]

  result[:valid] || result[:allowed]
end

.shard_keyObject



25
26
27
28
29
30
31
# File 'lib/gitlab/instrumentation/redis_base.rb', line 25

def shard_key
  strong_memoize(:shard_key) do
    re = /.+_shard_(?<shard>.+)/
    md = re.match(self.name.demodulize.underscore)
    (md && md[:shard]) || DEFAULT_SHARD_KEY
  end
end

.storage_keyObject

TODO: To be used by gitlab.com/gitlab-com/gl-infra/scalability/-/issues/395 as a ‘label’ alias. The 2 acceptable formats for a demodulized name are: <storage>shard<shard> or <storage>.



17
18
19
20
21
22
23
# File 'lib/gitlab/instrumentation/redis_base.rb', line 17

def storage_key
  strong_memoize(:storage_key) do
    re = /(?<storage>.+)_shard_.+/
    md = re.match(self.name.demodulize.underscore)
    (md && md[:storage]) || self.name.demodulize.underscore
  end
end

.write_bytesObject



81
82
83
# File 'lib/gitlab/instrumentation/redis_base.rb', line 81

def write_bytes
  ::RequestStore[write_bytes_key] || 0
end