brainzlab 0.1.3 → 0.1.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/lib/brainzlab/configuration.rb +20 -0
- data/lib/brainzlab/instrumentation/action_cable.rb +351 -0
- data/lib/brainzlab/instrumentation/action_controller.rb +649 -0
- data/lib/brainzlab/instrumentation/action_dispatch.rb +259 -0
- data/lib/brainzlab/instrumentation/action_mailbox.rb +197 -0
- data/lib/brainzlab/instrumentation/action_view.rb +380 -0
- data/lib/brainzlab/instrumentation/active_job.rb +569 -0
- data/lib/brainzlab/instrumentation/active_record.rb +458 -25
- data/lib/brainzlab/instrumentation/active_storage.rb +541 -0
- data/lib/brainzlab/instrumentation/active_support_cache.rb +700 -0
- data/lib/brainzlab/instrumentation/rails_deprecation.rb +139 -0
- data/lib/brainzlab/instrumentation/railties.rb +134 -0
- data/lib/brainzlab/instrumentation.rb +91 -1
- data/lib/brainzlab/version.rb +1 -1
- metadata +11 -1
|
@@ -0,0 +1,700 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module BrainzLab
|
|
4
|
+
module Instrumentation
|
|
5
|
+
class ActiveSupportCache
|
|
6
|
+
# Thresholds for slow cache operations (in milliseconds)
|
|
7
|
+
SLOW_CACHE_THRESHOLD = 10
|
|
8
|
+
VERY_SLOW_CACHE_THRESHOLD = 50
|
|
9
|
+
|
|
10
|
+
class << self
|
|
11
|
+
def install!
|
|
12
|
+
return unless defined?(::ActiveSupport::Cache)
|
|
13
|
+
return if @installed
|
|
14
|
+
|
|
15
|
+
install_cache_read_subscriber!
|
|
16
|
+
install_cache_read_multi_subscriber!
|
|
17
|
+
install_cache_write_subscriber!
|
|
18
|
+
install_cache_write_multi_subscriber!
|
|
19
|
+
install_cache_delete_subscriber!
|
|
20
|
+
install_cache_exist_subscriber!
|
|
21
|
+
install_cache_fetch_hit_subscriber!
|
|
22
|
+
install_cache_generate_subscriber!
|
|
23
|
+
install_cache_increment_subscriber!
|
|
24
|
+
install_cache_decrement_subscriber!
|
|
25
|
+
install_cache_delete_multi_subscriber!
|
|
26
|
+
install_cache_delete_matched_subscriber!
|
|
27
|
+
install_cache_cleanup_subscriber!
|
|
28
|
+
install_cache_prune_subscriber!
|
|
29
|
+
install_message_serializer_fallback_subscriber!
|
|
30
|
+
|
|
31
|
+
@installed = true
|
|
32
|
+
BrainzLab.debug_log('ActiveSupport::Cache instrumentation installed')
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def installed?
|
|
36
|
+
@installed == true
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
private
|
|
40
|
+
|
|
41
|
+
# ============================================
|
|
42
|
+
# Cache Read
|
|
43
|
+
# ============================================
|
|
44
|
+
def install_cache_read_subscriber!
|
|
45
|
+
ActiveSupport::Notifications.subscribe('cache_read.active_support') do |*args|
|
|
46
|
+
event = ActiveSupport::Notifications::Event.new(*args)
|
|
47
|
+
handle_cache_read(event)
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
def handle_cache_read(event)
|
|
52
|
+
payload = event.payload
|
|
53
|
+
duration = event.duration.round(2)
|
|
54
|
+
|
|
55
|
+
key = payload[:key]
|
|
56
|
+
hit = payload[:hit]
|
|
57
|
+
super_operation = payload[:super_operation]
|
|
58
|
+
|
|
59
|
+
# Skip if this is part of a fetch operation (will be tracked separately)
|
|
60
|
+
return if super_operation == :fetch
|
|
61
|
+
|
|
62
|
+
# Record breadcrumb
|
|
63
|
+
record_cache_breadcrumb('read', key, duration, hit: hit)
|
|
64
|
+
|
|
65
|
+
# Add Pulse span
|
|
66
|
+
record_cache_span(event, 'read', key, duration, hit: hit)
|
|
67
|
+
|
|
68
|
+
# Track cache metrics
|
|
69
|
+
track_cache_metrics('read', hit, duration)
|
|
70
|
+
rescue StandardError => e
|
|
71
|
+
BrainzLab.debug_log("ActiveSupport::Cache read instrumentation failed: #{e.message}")
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
# ============================================
|
|
75
|
+
# Cache Read Multi
|
|
76
|
+
# ============================================
|
|
77
|
+
def install_cache_read_multi_subscriber!
|
|
78
|
+
ActiveSupport::Notifications.subscribe('cache_read_multi.active_support') do |*args|
|
|
79
|
+
event = ActiveSupport::Notifications::Event.new(*args)
|
|
80
|
+
handle_cache_read_multi(event)
|
|
81
|
+
end
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
def handle_cache_read_multi(event)
|
|
85
|
+
payload = event.payload
|
|
86
|
+
duration = event.duration.round(2)
|
|
87
|
+
|
|
88
|
+
key = payload[:key] # Array of keys
|
|
89
|
+
hits = payload[:hits] # Keys that were found
|
|
90
|
+
super_operation = payload[:super_operation]
|
|
91
|
+
|
|
92
|
+
return if super_operation == :fetch_multi
|
|
93
|
+
|
|
94
|
+
key_count = Array(key).size
|
|
95
|
+
hit_count = Array(hits).size
|
|
96
|
+
|
|
97
|
+
# Record breadcrumb
|
|
98
|
+
if BrainzLab.configuration.reflex_effectively_enabled?
|
|
99
|
+
BrainzLab::Reflex.add_breadcrumb(
|
|
100
|
+
"Cache read_multi: #{hit_count}/#{key_count} hits (#{duration}ms)",
|
|
101
|
+
category: 'cache.read_multi',
|
|
102
|
+
level: duration >= SLOW_CACHE_THRESHOLD ? :warning : :info,
|
|
103
|
+
data: {
|
|
104
|
+
key_count: key_count,
|
|
105
|
+
hit_count: hit_count,
|
|
106
|
+
miss_count: key_count - hit_count,
|
|
107
|
+
duration_ms: duration,
|
|
108
|
+
hit_rate: key_count > 0 ? (hit_count.to_f / key_count * 100).round(1) : 0
|
|
109
|
+
}
|
|
110
|
+
)
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
# Add Pulse span
|
|
114
|
+
record_cache_span(event, 'read_multi', "#{key_count} keys", duration,
|
|
115
|
+
key_count: key_count, hit_count: hit_count)
|
|
116
|
+
rescue StandardError => e
|
|
117
|
+
BrainzLab.debug_log("ActiveSupport::Cache read_multi instrumentation failed: #{e.message}")
|
|
118
|
+
end
|
|
119
|
+
|
|
120
|
+
# ============================================
|
|
121
|
+
# Cache Write
|
|
122
|
+
# ============================================
|
|
123
|
+
def install_cache_write_subscriber!
|
|
124
|
+
ActiveSupport::Notifications.subscribe('cache_write.active_support') do |*args|
|
|
125
|
+
event = ActiveSupport::Notifications::Event.new(*args)
|
|
126
|
+
handle_cache_write(event)
|
|
127
|
+
end
|
|
128
|
+
end
|
|
129
|
+
|
|
130
|
+
def handle_cache_write(event)
|
|
131
|
+
payload = event.payload
|
|
132
|
+
duration = event.duration.round(2)
|
|
133
|
+
|
|
134
|
+
key = payload[:key]
|
|
135
|
+
|
|
136
|
+
# Record breadcrumb
|
|
137
|
+
record_cache_breadcrumb('write', key, duration)
|
|
138
|
+
|
|
139
|
+
# Add Pulse span
|
|
140
|
+
record_cache_span(event, 'write', key, duration)
|
|
141
|
+
|
|
142
|
+
# Log slow writes
|
|
143
|
+
log_slow_cache_operation('write', key, duration) if duration >= SLOW_CACHE_THRESHOLD
|
|
144
|
+
rescue StandardError => e
|
|
145
|
+
BrainzLab.debug_log("ActiveSupport::Cache write instrumentation failed: #{e.message}")
|
|
146
|
+
end
|
|
147
|
+
|
|
148
|
+
# ============================================
|
|
149
|
+
# Cache Write Multi
|
|
150
|
+
# ============================================
|
|
151
|
+
def install_cache_write_multi_subscriber!
|
|
152
|
+
ActiveSupport::Notifications.subscribe('cache_write_multi.active_support') do |*args|
|
|
153
|
+
event = ActiveSupport::Notifications::Event.new(*args)
|
|
154
|
+
handle_cache_write_multi(event)
|
|
155
|
+
end
|
|
156
|
+
end
|
|
157
|
+
|
|
158
|
+
def handle_cache_write_multi(event)
|
|
159
|
+
payload = event.payload
|
|
160
|
+
duration = event.duration.round(2)
|
|
161
|
+
|
|
162
|
+
key = payload[:key] # Hash of key => value pairs
|
|
163
|
+
key_count = key.is_a?(Hash) ? key.size : 1
|
|
164
|
+
|
|
165
|
+
# Record breadcrumb
|
|
166
|
+
if BrainzLab.configuration.reflex_effectively_enabled?
|
|
167
|
+
BrainzLab::Reflex.add_breadcrumb(
|
|
168
|
+
"Cache write_multi: #{key_count} keys (#{duration}ms)",
|
|
169
|
+
category: 'cache.write_multi',
|
|
170
|
+
level: duration >= SLOW_CACHE_THRESHOLD ? :warning : :info,
|
|
171
|
+
data: {
|
|
172
|
+
key_count: key_count,
|
|
173
|
+
duration_ms: duration
|
|
174
|
+
}
|
|
175
|
+
)
|
|
176
|
+
end
|
|
177
|
+
|
|
178
|
+
# Add Pulse span
|
|
179
|
+
record_cache_span(event, 'write_multi', "#{key_count} keys", duration, key_count: key_count)
|
|
180
|
+
rescue StandardError => e
|
|
181
|
+
BrainzLab.debug_log("ActiveSupport::Cache write_multi instrumentation failed: #{e.message}")
|
|
182
|
+
end
|
|
183
|
+
|
|
184
|
+
# ============================================
|
|
185
|
+
# Cache Delete
|
|
186
|
+
# ============================================
|
|
187
|
+
def install_cache_delete_subscriber!
|
|
188
|
+
ActiveSupport::Notifications.subscribe('cache_delete.active_support') do |*args|
|
|
189
|
+
event = ActiveSupport::Notifications::Event.new(*args)
|
|
190
|
+
handle_cache_delete(event)
|
|
191
|
+
end
|
|
192
|
+
end
|
|
193
|
+
|
|
194
|
+
def handle_cache_delete(event)
|
|
195
|
+
payload = event.payload
|
|
196
|
+
duration = event.duration.round(2)
|
|
197
|
+
|
|
198
|
+
key = payload[:key]
|
|
199
|
+
|
|
200
|
+
# Record breadcrumb
|
|
201
|
+
record_cache_breadcrumb('delete', key, duration)
|
|
202
|
+
|
|
203
|
+
# Add Pulse span
|
|
204
|
+
record_cache_span(event, 'delete', key, duration)
|
|
205
|
+
rescue StandardError => e
|
|
206
|
+
BrainzLab.debug_log("ActiveSupport::Cache delete instrumentation failed: #{e.message}")
|
|
207
|
+
end
|
|
208
|
+
|
|
209
|
+
# ============================================
|
|
210
|
+
# Cache Exist?
|
|
211
|
+
# ============================================
|
|
212
|
+
def install_cache_exist_subscriber!
|
|
213
|
+
ActiveSupport::Notifications.subscribe('cache_exist?.active_support') do |*args|
|
|
214
|
+
event = ActiveSupport::Notifications::Event.new(*args)
|
|
215
|
+
handle_cache_exist(event)
|
|
216
|
+
end
|
|
217
|
+
end
|
|
218
|
+
|
|
219
|
+
def handle_cache_exist(event)
|
|
220
|
+
payload = event.payload
|
|
221
|
+
duration = event.duration.round(2)
|
|
222
|
+
|
|
223
|
+
key = payload[:key]
|
|
224
|
+
|
|
225
|
+
# Only track if slow or significant
|
|
226
|
+
return if duration < 1
|
|
227
|
+
|
|
228
|
+
# Add Pulse span (skip breadcrumb for exist? as it's noisy)
|
|
229
|
+
record_cache_span(event, 'exist', key, duration)
|
|
230
|
+
rescue StandardError => e
|
|
231
|
+
BrainzLab.debug_log("ActiveSupport::Cache exist? instrumentation failed: #{e.message}")
|
|
232
|
+
end
|
|
233
|
+
|
|
234
|
+
# ============================================
|
|
235
|
+
# Cache Fetch Hit (successful fetch from cache)
|
|
236
|
+
# ============================================
|
|
237
|
+
def install_cache_fetch_hit_subscriber!
|
|
238
|
+
ActiveSupport::Notifications.subscribe('cache_fetch_hit.active_support') do |*args|
|
|
239
|
+
event = ActiveSupport::Notifications::Event.new(*args)
|
|
240
|
+
handle_cache_fetch_hit(event)
|
|
241
|
+
end
|
|
242
|
+
end
|
|
243
|
+
|
|
244
|
+
def handle_cache_fetch_hit(event)
|
|
245
|
+
payload = event.payload
|
|
246
|
+
duration = event.duration.round(2)
|
|
247
|
+
|
|
248
|
+
key = payload[:key]
|
|
249
|
+
|
|
250
|
+
# Record breadcrumb
|
|
251
|
+
record_cache_breadcrumb('fetch', key, duration, hit: true)
|
|
252
|
+
|
|
253
|
+
# Add Pulse span
|
|
254
|
+
record_cache_span(event, 'fetch', key, duration, hit: true)
|
|
255
|
+
|
|
256
|
+
# Track cache metrics
|
|
257
|
+
track_cache_metrics('fetch', true, duration)
|
|
258
|
+
rescue StandardError => e
|
|
259
|
+
BrainzLab.debug_log("ActiveSupport::Cache fetch_hit instrumentation failed: #{e.message}")
|
|
260
|
+
end
|
|
261
|
+
|
|
262
|
+
# ============================================
|
|
263
|
+
# Cache Generate (cache miss, value computed)
|
|
264
|
+
# ============================================
|
|
265
|
+
def install_cache_generate_subscriber!
|
|
266
|
+
ActiveSupport::Notifications.subscribe('cache_generate.active_support') do |*args|
|
|
267
|
+
event = ActiveSupport::Notifications::Event.new(*args)
|
|
268
|
+
handle_cache_generate(event)
|
|
269
|
+
end
|
|
270
|
+
end
|
|
271
|
+
|
|
272
|
+
def handle_cache_generate(event)
|
|
273
|
+
payload = event.payload
|
|
274
|
+
duration = event.duration.round(2)
|
|
275
|
+
|
|
276
|
+
key = payload[:key]
|
|
277
|
+
|
|
278
|
+
# Record breadcrumb - this is a cache miss that triggered computation
|
|
279
|
+
if BrainzLab.configuration.reflex_effectively_enabled?
|
|
280
|
+
level = case duration
|
|
281
|
+
when 0...SLOW_CACHE_THRESHOLD then :info
|
|
282
|
+
when SLOW_CACHE_THRESHOLD...VERY_SLOW_CACHE_THRESHOLD then :warning
|
|
283
|
+
else :error
|
|
284
|
+
end
|
|
285
|
+
|
|
286
|
+
BrainzLab::Reflex.add_breadcrumb(
|
|
287
|
+
"Cache miss + generate: #{truncate_key(key)} (#{duration}ms)",
|
|
288
|
+
category: 'cache.generate',
|
|
289
|
+
level: level,
|
|
290
|
+
data: {
|
|
291
|
+
key: truncate_key(key),
|
|
292
|
+
duration_ms: duration,
|
|
293
|
+
hit: false
|
|
294
|
+
}
|
|
295
|
+
)
|
|
296
|
+
end
|
|
297
|
+
|
|
298
|
+
# Add Pulse span
|
|
299
|
+
record_cache_span(event, 'generate', key, duration, hit: false)
|
|
300
|
+
|
|
301
|
+
# Track cache metrics
|
|
302
|
+
track_cache_metrics('fetch', false, duration)
|
|
303
|
+
|
|
304
|
+
# Log slow cache generations
|
|
305
|
+
log_slow_cache_operation('generate', key, duration) if duration >= SLOW_CACHE_THRESHOLD
|
|
306
|
+
rescue StandardError => e
|
|
307
|
+
BrainzLab.debug_log("ActiveSupport::Cache generate instrumentation failed: #{e.message}")
|
|
308
|
+
end
|
|
309
|
+
|
|
310
|
+
# ============================================
|
|
311
|
+
# Cache Increment
|
|
312
|
+
# ============================================
|
|
313
|
+
def install_cache_increment_subscriber!
|
|
314
|
+
ActiveSupport::Notifications.subscribe('cache_increment.active_support') do |*args|
|
|
315
|
+
event = ActiveSupport::Notifications::Event.new(*args)
|
|
316
|
+
handle_cache_increment(event)
|
|
317
|
+
end
|
|
318
|
+
end
|
|
319
|
+
|
|
320
|
+
def handle_cache_increment(event)
|
|
321
|
+
payload = event.payload
|
|
322
|
+
duration = event.duration.round(2)
|
|
323
|
+
|
|
324
|
+
key = payload[:key]
|
|
325
|
+
amount = payload[:amount]
|
|
326
|
+
|
|
327
|
+
# Record breadcrumb
|
|
328
|
+
if BrainzLab.configuration.reflex_effectively_enabled?
|
|
329
|
+
BrainzLab::Reflex.add_breadcrumb(
|
|
330
|
+
"Cache increment: #{truncate_key(key)} by #{amount} (#{duration}ms)",
|
|
331
|
+
category: 'cache.increment',
|
|
332
|
+
level: :info,
|
|
333
|
+
data: {
|
|
334
|
+
key: truncate_key(key),
|
|
335
|
+
amount: amount,
|
|
336
|
+
duration_ms: duration
|
|
337
|
+
}.compact
|
|
338
|
+
)
|
|
339
|
+
end
|
|
340
|
+
|
|
341
|
+
# Add Pulse span
|
|
342
|
+
record_cache_span(event, 'increment', key, duration, amount: amount)
|
|
343
|
+
rescue StandardError => e
|
|
344
|
+
BrainzLab.debug_log("ActiveSupport::Cache increment instrumentation failed: #{e.message}")
|
|
345
|
+
end
|
|
346
|
+
|
|
347
|
+
# ============================================
|
|
348
|
+
# Cache Decrement
|
|
349
|
+
# ============================================
|
|
350
|
+
def install_cache_decrement_subscriber!
|
|
351
|
+
ActiveSupport::Notifications.subscribe('cache_decrement.active_support') do |*args|
|
|
352
|
+
event = ActiveSupport::Notifications::Event.new(*args)
|
|
353
|
+
handle_cache_decrement(event)
|
|
354
|
+
end
|
|
355
|
+
end
|
|
356
|
+
|
|
357
|
+
def handle_cache_decrement(event)
|
|
358
|
+
payload = event.payload
|
|
359
|
+
duration = event.duration.round(2)
|
|
360
|
+
|
|
361
|
+
key = payload[:key]
|
|
362
|
+
amount = payload[:amount]
|
|
363
|
+
|
|
364
|
+
# Record breadcrumb
|
|
365
|
+
if BrainzLab.configuration.reflex_effectively_enabled?
|
|
366
|
+
BrainzLab::Reflex.add_breadcrumb(
|
|
367
|
+
"Cache decrement: #{truncate_key(key)} by #{amount} (#{duration}ms)",
|
|
368
|
+
category: 'cache.decrement',
|
|
369
|
+
level: :info,
|
|
370
|
+
data: {
|
|
371
|
+
key: truncate_key(key),
|
|
372
|
+
amount: amount,
|
|
373
|
+
duration_ms: duration
|
|
374
|
+
}.compact
|
|
375
|
+
)
|
|
376
|
+
end
|
|
377
|
+
|
|
378
|
+
# Add Pulse span
|
|
379
|
+
record_cache_span(event, 'decrement', key, duration, amount: amount)
|
|
380
|
+
rescue StandardError => e
|
|
381
|
+
BrainzLab.debug_log("ActiveSupport::Cache decrement instrumentation failed: #{e.message}")
|
|
382
|
+
end
|
|
383
|
+
|
|
384
|
+
# ============================================
|
|
385
|
+
# Cache Delete Multi
|
|
386
|
+
# ============================================
|
|
387
|
+
def install_cache_delete_multi_subscriber!
|
|
388
|
+
ActiveSupport::Notifications.subscribe('cache_delete_multi.active_support') do |*args|
|
|
389
|
+
event = ActiveSupport::Notifications::Event.new(*args)
|
|
390
|
+
handle_cache_delete_multi(event)
|
|
391
|
+
end
|
|
392
|
+
end
|
|
393
|
+
|
|
394
|
+
def handle_cache_delete_multi(event)
|
|
395
|
+
payload = event.payload
|
|
396
|
+
duration = event.duration.round(2)
|
|
397
|
+
|
|
398
|
+
key = payload[:key] # Array of keys
|
|
399
|
+
key_count = Array(key).size
|
|
400
|
+
|
|
401
|
+
# Record breadcrumb
|
|
402
|
+
if BrainzLab.configuration.reflex_effectively_enabled?
|
|
403
|
+
BrainzLab::Reflex.add_breadcrumb(
|
|
404
|
+
"Cache delete_multi: #{key_count} keys (#{duration}ms)",
|
|
405
|
+
category: 'cache.delete_multi',
|
|
406
|
+
level: :info,
|
|
407
|
+
data: {
|
|
408
|
+
key_count: key_count,
|
|
409
|
+
duration_ms: duration
|
|
410
|
+
}
|
|
411
|
+
)
|
|
412
|
+
end
|
|
413
|
+
|
|
414
|
+
# Add Pulse span
|
|
415
|
+
record_cache_span(event, 'delete_multi', "#{key_count} keys", duration, key_count: key_count)
|
|
416
|
+
rescue StandardError => e
|
|
417
|
+
BrainzLab.debug_log("ActiveSupport::Cache delete_multi instrumentation failed: #{e.message}")
|
|
418
|
+
end
|
|
419
|
+
|
|
420
|
+
# ============================================
|
|
421
|
+
# Cache Delete Matched (pattern-based delete)
|
|
422
|
+
# ============================================
|
|
423
|
+
def install_cache_delete_matched_subscriber!
|
|
424
|
+
ActiveSupport::Notifications.subscribe('cache_delete_matched.active_support') do |*args|
|
|
425
|
+
event = ActiveSupport::Notifications::Event.new(*args)
|
|
426
|
+
handle_cache_delete_matched(event)
|
|
427
|
+
end
|
|
428
|
+
end
|
|
429
|
+
|
|
430
|
+
def handle_cache_delete_matched(event)
|
|
431
|
+
payload = event.payload
|
|
432
|
+
duration = event.duration.round(2)
|
|
433
|
+
|
|
434
|
+
key = payload[:key] # Pattern
|
|
435
|
+
|
|
436
|
+
# Record breadcrumb - pattern deletes are significant
|
|
437
|
+
if BrainzLab.configuration.reflex_effectively_enabled?
|
|
438
|
+
BrainzLab::Reflex.add_breadcrumb(
|
|
439
|
+
"Cache delete_matched: #{truncate_key(key)} (#{duration}ms)",
|
|
440
|
+
category: 'cache.delete_matched',
|
|
441
|
+
level: :warning,
|
|
442
|
+
data: {
|
|
443
|
+
pattern: truncate_key(key),
|
|
444
|
+
duration_ms: duration
|
|
445
|
+
}
|
|
446
|
+
)
|
|
447
|
+
end
|
|
448
|
+
|
|
449
|
+
# Add Pulse span
|
|
450
|
+
record_cache_span(event, 'delete_matched', key, duration)
|
|
451
|
+
|
|
452
|
+
# Log pattern deletes
|
|
453
|
+
if BrainzLab.configuration.recall_effectively_enabled?
|
|
454
|
+
BrainzLab::Recall.info(
|
|
455
|
+
"Cache pattern delete",
|
|
456
|
+
pattern: truncate_key(key),
|
|
457
|
+
duration_ms: duration
|
|
458
|
+
)
|
|
459
|
+
end
|
|
460
|
+
rescue StandardError => e
|
|
461
|
+
BrainzLab.debug_log("ActiveSupport::Cache delete_matched instrumentation failed: #{e.message}")
|
|
462
|
+
end
|
|
463
|
+
|
|
464
|
+
# ============================================
|
|
465
|
+
# Cache Cleanup (remove expired entries)
|
|
466
|
+
# ============================================
|
|
467
|
+
def install_cache_cleanup_subscriber!
|
|
468
|
+
ActiveSupport::Notifications.subscribe('cache_cleanup.active_support') do |*args|
|
|
469
|
+
event = ActiveSupport::Notifications::Event.new(*args)
|
|
470
|
+
handle_cache_cleanup(event)
|
|
471
|
+
end
|
|
472
|
+
end
|
|
473
|
+
|
|
474
|
+
def handle_cache_cleanup(event)
|
|
475
|
+
payload = event.payload
|
|
476
|
+
duration = event.duration.round(2)
|
|
477
|
+
|
|
478
|
+
size = payload[:size]
|
|
479
|
+
key = payload[:key]
|
|
480
|
+
|
|
481
|
+
# Record breadcrumb
|
|
482
|
+
if BrainzLab.configuration.reflex_effectively_enabled?
|
|
483
|
+
BrainzLab::Reflex.add_breadcrumb(
|
|
484
|
+
"Cache cleanup: size=#{size} (#{duration}ms)",
|
|
485
|
+
category: 'cache.cleanup',
|
|
486
|
+
level: :info,
|
|
487
|
+
data: {
|
|
488
|
+
size: size,
|
|
489
|
+
duration_ms: duration
|
|
490
|
+
}.compact
|
|
491
|
+
)
|
|
492
|
+
end
|
|
493
|
+
|
|
494
|
+
# Add Pulse span
|
|
495
|
+
record_cache_span(event, 'cleanup', 'cleanup', duration, size: size)
|
|
496
|
+
rescue StandardError => e
|
|
497
|
+
BrainzLab.debug_log("ActiveSupport::Cache cleanup instrumentation failed: #{e.message}")
|
|
498
|
+
end
|
|
499
|
+
|
|
500
|
+
# ============================================
|
|
501
|
+
# Cache Prune (reduce cache size)
|
|
502
|
+
# ============================================
|
|
503
|
+
def install_cache_prune_subscriber!
|
|
504
|
+
ActiveSupport::Notifications.subscribe('cache_prune.active_support') do |*args|
|
|
505
|
+
event = ActiveSupport::Notifications::Event.new(*args)
|
|
506
|
+
handle_cache_prune(event)
|
|
507
|
+
end
|
|
508
|
+
end
|
|
509
|
+
|
|
510
|
+
def handle_cache_prune(event)
|
|
511
|
+
payload = event.payload
|
|
512
|
+
duration = event.duration.round(2)
|
|
513
|
+
|
|
514
|
+
key = payload[:key]
|
|
515
|
+
from = payload[:from]
|
|
516
|
+
to = payload[:to]
|
|
517
|
+
|
|
518
|
+
# Record breadcrumb
|
|
519
|
+
if BrainzLab.configuration.reflex_effectively_enabled?
|
|
520
|
+
BrainzLab::Reflex.add_breadcrumb(
|
|
521
|
+
"Cache prune: #{from} -> #{to} (#{duration}ms)",
|
|
522
|
+
category: 'cache.prune',
|
|
523
|
+
level: :info,
|
|
524
|
+
data: {
|
|
525
|
+
from: from,
|
|
526
|
+
to: to,
|
|
527
|
+
duration_ms: duration
|
|
528
|
+
}.compact
|
|
529
|
+
)
|
|
530
|
+
end
|
|
531
|
+
|
|
532
|
+
# Add Pulse span
|
|
533
|
+
record_cache_span(event, 'prune', 'prune', duration, from: from, to: to)
|
|
534
|
+
|
|
535
|
+
# Log prune operations
|
|
536
|
+
if BrainzLab.configuration.recall_effectively_enabled?
|
|
537
|
+
BrainzLab::Recall.info(
|
|
538
|
+
"Cache pruned",
|
|
539
|
+
from: from,
|
|
540
|
+
to: to,
|
|
541
|
+
duration_ms: duration
|
|
542
|
+
)
|
|
543
|
+
end
|
|
544
|
+
rescue StandardError => e
|
|
545
|
+
BrainzLab.debug_log("ActiveSupport::Cache prune instrumentation failed: #{e.message}")
|
|
546
|
+
end
|
|
547
|
+
|
|
548
|
+
# ============================================
|
|
549
|
+
# Message Serializer Fallback
|
|
550
|
+
# Fired when a message is deserialized using a fallback serializer
|
|
551
|
+
# This typically indicates a migration between serialization formats
|
|
552
|
+
# ============================================
|
|
553
|
+
def install_message_serializer_fallback_subscriber!
|
|
554
|
+
ActiveSupport::Notifications.subscribe('message_serializer_fallback.active_support') do |*args|
|
|
555
|
+
event = ActiveSupport::Notifications::Event.new(*args)
|
|
556
|
+
handle_message_serializer_fallback(event)
|
|
557
|
+
end
|
|
558
|
+
end
|
|
559
|
+
|
|
560
|
+
def handle_message_serializer_fallback(event)
|
|
561
|
+
payload = event.payload
|
|
562
|
+
duration = event.duration.round(2)
|
|
563
|
+
|
|
564
|
+
serializer = payload[:serializer]
|
|
565
|
+
fallback = payload[:fallback]
|
|
566
|
+
serialized = payload[:serialized]
|
|
567
|
+
deserialized = payload[:deserialized]
|
|
568
|
+
|
|
569
|
+
# Record breadcrumb - this is a warning as it indicates a format mismatch
|
|
570
|
+
if BrainzLab.configuration.reflex_effectively_enabled?
|
|
571
|
+
BrainzLab::Reflex.add_breadcrumb(
|
|
572
|
+
"Message serializer fallback: #{serializer} -> #{fallback}",
|
|
573
|
+
category: 'serializer.fallback',
|
|
574
|
+
level: :warning,
|
|
575
|
+
data: {
|
|
576
|
+
serializer: serializer.to_s,
|
|
577
|
+
fallback: fallback.to_s,
|
|
578
|
+
duration_ms: duration
|
|
579
|
+
}.compact
|
|
580
|
+
)
|
|
581
|
+
end
|
|
582
|
+
|
|
583
|
+
# Log to Recall - this is significant for debugging serialization issues
|
|
584
|
+
if BrainzLab.configuration.recall_effectively_enabled?
|
|
585
|
+
BrainzLab::Recall.warn(
|
|
586
|
+
"Message serializer fallback used",
|
|
587
|
+
serializer: serializer.to_s,
|
|
588
|
+
fallback: fallback.to_s,
|
|
589
|
+
duration_ms: duration
|
|
590
|
+
)
|
|
591
|
+
end
|
|
592
|
+
rescue StandardError => e
|
|
593
|
+
BrainzLab.debug_log("ActiveSupport message_serializer_fallback instrumentation failed: #{e.message}")
|
|
594
|
+
end
|
|
595
|
+
|
|
596
|
+
# ============================================
|
|
597
|
+
# Recording Helpers
|
|
598
|
+
# ============================================
|
|
599
|
+
def record_cache_breadcrumb(operation, key, duration, hit: nil)
|
|
600
|
+
return unless BrainzLab.configuration.reflex_effectively_enabled?
|
|
601
|
+
|
|
602
|
+
level = duration >= SLOW_CACHE_THRESHOLD ? :warning : :info
|
|
603
|
+
|
|
604
|
+
message = if hit.nil?
|
|
605
|
+
"Cache #{operation}: #{truncate_key(key)} (#{duration}ms)"
|
|
606
|
+
elsif hit
|
|
607
|
+
"Cache #{operation} hit: #{truncate_key(key)} (#{duration}ms)"
|
|
608
|
+
else
|
|
609
|
+
"Cache #{operation} miss: #{truncate_key(key)} (#{duration}ms)"
|
|
610
|
+
end
|
|
611
|
+
|
|
612
|
+
data = {
|
|
613
|
+
key: truncate_key(key),
|
|
614
|
+
operation: operation,
|
|
615
|
+
duration_ms: duration
|
|
616
|
+
}
|
|
617
|
+
data[:hit] = hit unless hit.nil?
|
|
618
|
+
|
|
619
|
+
BrainzLab::Reflex.add_breadcrumb(
|
|
620
|
+
message,
|
|
621
|
+
category: "cache.#{operation}",
|
|
622
|
+
level: level,
|
|
623
|
+
data: data.compact
|
|
624
|
+
)
|
|
625
|
+
end
|
|
626
|
+
|
|
627
|
+
def record_cache_span(event, operation, key, duration, hit: nil, **extra)
|
|
628
|
+
return unless BrainzLab.configuration.pulse_effectively_enabled?
|
|
629
|
+
|
|
630
|
+
tracer = BrainzLab::Pulse.tracer
|
|
631
|
+
return unless tracer.current_trace
|
|
632
|
+
|
|
633
|
+
span_data = {
|
|
634
|
+
span_id: SecureRandom.uuid,
|
|
635
|
+
name: "cache.#{operation}",
|
|
636
|
+
kind: 'cache',
|
|
637
|
+
started_at: event.time,
|
|
638
|
+
ended_at: event.end,
|
|
639
|
+
duration_ms: duration,
|
|
640
|
+
error: false,
|
|
641
|
+
data: {
|
|
642
|
+
'cache.operation' => operation,
|
|
643
|
+
'cache.key' => truncate_key(key),
|
|
644
|
+
'cache.hit' => hit
|
|
645
|
+
}.merge(extra.transform_keys { |k| "cache.#{k}" }).compact
|
|
646
|
+
}
|
|
647
|
+
|
|
648
|
+
tracer.current_spans << span_data
|
|
649
|
+
end
|
|
650
|
+
|
|
651
|
+
def track_cache_metrics(operation, hit, duration)
|
|
652
|
+
return unless BrainzLab.configuration.pulse_effectively_enabled?
|
|
653
|
+
|
|
654
|
+
# Increment cache operation counter
|
|
655
|
+
BrainzLab::Pulse.counter(
|
|
656
|
+
"cache.#{operation}.total",
|
|
657
|
+
1,
|
|
658
|
+
tags: { hit: hit.to_s }
|
|
659
|
+
)
|
|
660
|
+
|
|
661
|
+
# Record cache operation duration
|
|
662
|
+
BrainzLab::Pulse.histogram(
|
|
663
|
+
"cache.#{operation}.duration_ms",
|
|
664
|
+
duration,
|
|
665
|
+
tags: { hit: hit.to_s }
|
|
666
|
+
)
|
|
667
|
+
end
|
|
668
|
+
|
|
669
|
+
def log_slow_cache_operation(operation, key, duration)
|
|
670
|
+
return unless BrainzLab.configuration.recall_effectively_enabled?
|
|
671
|
+
|
|
672
|
+
level = duration >= VERY_SLOW_CACHE_THRESHOLD ? :error : :warn
|
|
673
|
+
|
|
674
|
+
BrainzLab::Recall.send(
|
|
675
|
+
level,
|
|
676
|
+
"Slow cache #{operation}: #{truncate_key(key)} (#{duration}ms)",
|
|
677
|
+
operation: operation,
|
|
678
|
+
key: truncate_key(key),
|
|
679
|
+
duration_ms: duration,
|
|
680
|
+
threshold_exceeded: duration >= VERY_SLOW_CACHE_THRESHOLD ? 'critical' : 'warning'
|
|
681
|
+
)
|
|
682
|
+
end
|
|
683
|
+
|
|
684
|
+
# ============================================
|
|
685
|
+
# Helper Methods
|
|
686
|
+
# ============================================
|
|
687
|
+
def truncate_key(key, max_length = 100)
|
|
688
|
+
return 'unknown' unless key
|
|
689
|
+
|
|
690
|
+
key_str = key.to_s
|
|
691
|
+
if key_str.length > max_length
|
|
692
|
+
"#{key_str[0, max_length - 3]}..."
|
|
693
|
+
else
|
|
694
|
+
key_str
|
|
695
|
+
end
|
|
696
|
+
end
|
|
697
|
+
end
|
|
698
|
+
end
|
|
699
|
+
end
|
|
700
|
+
end
|