solid_observer 0.3.0 → 0.5.0

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.
Files changed (81) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +34 -0
  3. data/README.md +195 -82
  4. data/app/assets/javascripts/solid_observer/live_poll.js +3 -1
  5. data/app/controllers/solid_observer/application_controller.rb +1 -0
  6. data/app/controllers/solid_observer/cable_dashboard_controller.rb +52 -0
  7. data/app/controllers/solid_observer/cable_operations_controller.rb +16 -0
  8. data/app/controllers/solid_observer/cache_dashboard_controller.rb +52 -0
  9. data/app/controllers/solid_observer/cache_operations_controller.rb +24 -0
  10. data/app/controllers/solid_observer/dashboard_controller.rb +38 -1
  11. data/app/controllers/solid_observer/storages_controller.rb +1 -1
  12. data/app/helpers/solid_observer/application_helper.rb +268 -5
  13. data/app/helpers/solid_observer/dashboard_helper.rb +30 -11
  14. data/app/models/solid_observer/cable_event.rb +13 -0
  15. data/app/models/solid_observer/cable_metric.rb +12 -0
  16. data/app/models/solid_observer/cache_event.rb +15 -0
  17. data/app/models/solid_observer/cache_metric.rb +13 -0
  18. data/app/models/solid_observer/storage_info.rb +4 -1
  19. data/app/views/layouts/solid_observer/application.html.erb +157 -19
  20. data/app/views/solid_observer/cable_dashboard/_charts.html.erb +31 -0
  21. data/app/views/solid_observer/cable_dashboard/_recent_events.html.erb +34 -0
  22. data/app/views/solid_observer/cable_dashboard/_summary.html.erb +34 -0
  23. data/app/views/solid_observer/cable_dashboard/index.html.erb +118 -0
  24. data/app/views/solid_observer/cache_dashboard/_charts.html.erb +40 -0
  25. data/app/views/solid_observer/cache_dashboard/_recent_events.html.erb +34 -0
  26. data/app/views/solid_observer/cache_dashboard/_summary.html.erb +39 -0
  27. data/app/views/solid_observer/cache_dashboard/index.html.erb +62 -0
  28. data/app/views/solid_observer/cache_operations/_confirm_clear.html.erb +6 -0
  29. data/app/views/solid_observer/cache_operations/index.html.erb +60 -0
  30. data/app/views/solid_observer/dashboard/_queue_table.html.erb +1 -0
  31. data/app/views/solid_observer/dashboard/index.html.erb +32 -5
  32. data/app/views/solid_observer/events/index.html.erb +1 -0
  33. data/app/views/solid_observer/jobs/index.html.erb +1 -0
  34. data/app/views/solid_observer/jobs/show.html.erb +3 -3
  35. data/app/views/solid_observer/storages/show.html.erb +90 -32
  36. data/config/routes.rb +7 -0
  37. data/db/migrate/20260601000001_create_solid_observer_cache_events.rb +22 -0
  38. data/db/migrate/20260601000002_create_solid_observer_cache_metrics.rb +18 -0
  39. data/db/migrate/20260602000001_add_component_to_solid_observer_storage_infos.rb +8 -0
  40. data/db/migrate/20260612000001_add_event_type_recorded_at_index_to_cache_events.rb +21 -0
  41. data/db/migrate/20260619000001_create_solid_observer_cable_events.rb +22 -0
  42. data/db/migrate/20260619000002_create_solid_observer_cable_metrics.rb +17 -0
  43. data/lib/generators/solid_observer/install_generator.rb +8 -1
  44. data/lib/generators/solid_observer/templates/initializer.rb.tt +20 -4
  45. data/lib/solid_observer/base_event.rb +1 -1
  46. data/lib/solid_observer/base_metric.rb +1 -1
  47. data/lib/solid_observer/base_record.rb +8 -0
  48. data/lib/solid_observer/cable_event_buffer.rb +28 -0
  49. data/lib/solid_observer/cable_metric_buffer.rb +230 -0
  50. data/lib/solid_observer/cable_subscriber.rb +57 -0
  51. data/lib/solid_observer/cache_event_buffer.rb +28 -0
  52. data/lib/solid_observer/cache_metric_buffer.rb +229 -0
  53. data/lib/solid_observer/cache_subscriber.rb +47 -0
  54. data/lib/solid_observer/chart_buffer.rb +84 -27
  55. data/lib/solid_observer/cli/storage.rb +16 -13
  56. data/lib/solid_observer/configuration.rb +67 -5
  57. data/lib/solid_observer/engine.rb +70 -15
  58. data/lib/solid_observer/event_buffer_core.rb +218 -0
  59. data/lib/solid_observer/queue_event_buffer.rb +9 -201
  60. data/lib/solid_observer/services/cable_operations.rb +74 -0
  61. data/lib/solid_observer/services/cable_stats.rb +385 -0
  62. data/lib/solid_observer/services/cache_operations.rb +115 -0
  63. data/lib/solid_observer/services/cache_stats.rb +346 -0
  64. data/lib/solid_observer/services/cleanup_storage.rb +98 -47
  65. data/lib/solid_observer/services/database_size.rb +13 -8
  66. data/lib/solid_observer/services/flush_cable_event_buffer.rb +54 -0
  67. data/lib/solid_observer/services/flush_cable_metrics.rb +54 -0
  68. data/lib/solid_observer/services/flush_cache_event_buffer.rb +54 -0
  69. data/lib/solid_observer/services/flush_cache_metrics.rb +56 -0
  70. data/lib/solid_observer/services/record_cable_event.rb +114 -0
  71. data/lib/solid_observer/services/record_cable_metric.rb +73 -0
  72. data/lib/solid_observer/services/record_cache_event.rb +165 -0
  73. data/lib/solid_observer/services/record_cache_metric.rb +66 -0
  74. data/lib/solid_observer/services/storage_info_snapshot.rb +216 -0
  75. data/lib/solid_observer/version.rb +1 -1
  76. data/lib/solid_observer.rb +36 -11
  77. data/lib/tasks/solid_observer.rake +111 -21
  78. metadata +47 -5
  79. data/bin/console +0 -11
  80. data/bin/quality_gate +0 -95
  81. data/bin/setup +0 -8
@@ -0,0 +1,230 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "singleton"
4
+ require "concurrent/timer_task"
5
+
6
+ require_relative "services/flush_cable_metrics"
7
+
8
+ module SolidObserver
9
+ class CableMetricBuffer
10
+ include Singleton
11
+
12
+ INITIAL_METRICS = {
13
+ flush_failures_count: 0,
14
+ drops_count: 0,
15
+ last_flush_at: nil,
16
+ last_flush_duration_ms: nil,
17
+ last_flush_error: nil
18
+ }.freeze
19
+
20
+ def initialize
21
+ @store = MetricStore.new
22
+ @timer_mutex = Mutex.new
23
+ @timer_task = nil
24
+ end
25
+
26
+ def increment(metric_data)
27
+ config = SolidObserver.config
28
+ return unless config.persistence_mode?
29
+
30
+ @store.add(metric_data)
31
+ ensure_timer_running
32
+ flush! if size >= config.buffer_size
33
+ end
34
+
35
+ def flush!
36
+ metrics_to_flush = @store.drain
37
+ return if metrics_to_flush.empty?
38
+
39
+ flush_metrics(metrics_to_flush, monotonic_ms)
40
+ end
41
+
42
+ def flush
43
+ flush!
44
+ end
45
+
46
+ def size
47
+ @store.size
48
+ end
49
+
50
+ def clear
51
+ @store.clear
52
+ end
53
+
54
+ def metrics
55
+ @store.metrics
56
+ end
57
+
58
+ def shutdown
59
+ stop_timer
60
+ flush!
61
+ end
62
+
63
+ private
64
+
65
+ def ensure_timer_running
66
+ timer_to_start, timer_to_stop = replace_timer_if_stopped
67
+ return unless timer_to_start
68
+
69
+ timer_to_stop&.shutdown
70
+ timer_to_start.execute
71
+ end
72
+
73
+ def replace_timer_if_stopped
74
+ @timer_mutex.synchronize do
75
+ current_timer_task = @timer_task
76
+ return [nil, nil] if current_timer_task && !current_timer_task.shuttingdown?
77
+
78
+ [build_timer_task, current_timer_task]
79
+ end
80
+ end
81
+
82
+ def stop_timer
83
+ timer_to_stop = @timer_mutex.synchronize do
84
+ current_timer = @timer_task
85
+ @timer_task = nil
86
+ current_timer
87
+ end
88
+
89
+ timer_to_stop&.shutdown
90
+ end
91
+
92
+ def build_timer_task
93
+ @timer_task = Concurrent::TimerTask.new(
94
+ execution_interval: SolidObserver.config.flush_interval,
95
+ run_now: false
96
+ ) { flush! }
97
+ end
98
+
99
+ def monotonic_ms
100
+ Process.clock_gettime(Process::CLOCK_MONOTONIC, :millisecond)
101
+ end
102
+
103
+ def flush_metrics(metrics_to_flush, started_at_ms)
104
+ Services::FlushCableMetrics.call(metrics_to_flush)
105
+ @store.record_flush_success(monotonic_ms - started_at_ms)
106
+ rescue => error
107
+ handle_flush_error(error, metrics_to_flush)
108
+ end
109
+
110
+ def handle_flush_error(error, metrics_to_flush)
111
+ @store.requeue(metrics_to_flush)
112
+ @store.record_flush_failure(error)
113
+ Rails.logger&.error("[SolidObserver] Cable metric buffer flush failed: #{error.message}") if defined?(Rails)
114
+ end
115
+
116
+ class MetricStore
117
+ COUNTERS = %i[
118
+ broadcasts_count
119
+ transmissions_count
120
+ confirmations_count
121
+ rejections_count
122
+ perform_actions_count
123
+ errors_count
124
+ ].freeze
125
+
126
+ def initialize
127
+ @mutex = Mutex.new
128
+ @buffer = {}
129
+ @metrics = INITIAL_METRICS.dup
130
+ end
131
+
132
+ def add(metric_data)
133
+ @mutex.synchronize { add_metric(metric_data) }
134
+ end
135
+
136
+ def drain
137
+ @mutex.synchronize do
138
+ drained = @buffer.values.map(&:dup)
139
+ @buffer.clear
140
+ drained
141
+ end
142
+ end
143
+
144
+ def requeue(metrics_to_flush)
145
+ @mutex.synchronize { add_metrics_with_capacity(metrics_to_flush + @buffer.values) }
146
+ end
147
+
148
+ def clear
149
+ @mutex.synchronize { @buffer.clear }
150
+ end
151
+
152
+ def size
153
+ @mutex.synchronize { @buffer.size }
154
+ end
155
+
156
+ def metrics
157
+ @mutex.synchronize do
158
+ {
159
+ size: @buffer.size,
160
+ max_buffer_size: SolidObserver.config.max_buffer_size
161
+ }.merge(@metrics.dup)
162
+ end
163
+ end
164
+
165
+ def record_flush_success(duration_ms)
166
+ @mutex.synchronize do
167
+ @metrics.merge!(
168
+ last_flush_at: Time.current,
169
+ last_flush_duration_ms: duration_ms,
170
+ last_flush_error: nil
171
+ )
172
+ end
173
+ end
174
+
175
+ def record_flush_failure(error)
176
+ @mutex.synchronize do
177
+ @metrics[:flush_failures_count] += 1
178
+ @metrics[:last_flush_error] = error.message
179
+ end
180
+ end
181
+
182
+ private
183
+
184
+ def add_metric(metric_data)
185
+ config = SolidObserver.config
186
+ key = metric_data.fetch(:period_start)
187
+ if @buffer.key?(key)
188
+ merge_metric(@buffer.fetch(key), metric_data)
189
+ elsif @buffer.size < config.max_buffer_size
190
+ @buffer[key] = metric_hash(metric_data)
191
+ else
192
+ handle_overflow(key, metric_data)
193
+ end
194
+ end
195
+
196
+ def add_metrics_with_capacity(metrics)
197
+ @buffer.clear
198
+ metrics.each { |metric| add_metric(metric) }
199
+ end
200
+
201
+ def handle_overflow(key, metric_data)
202
+ drop_count = if SolidObserver.config.buffer_overflow_strategy == :drop_old
203
+ replace_old_metric(key, metric_data)
204
+ else
205
+ COUNTERS.sum { |counter| metric_data.fetch(counter, 0).to_i }
206
+ end
207
+
208
+ @metrics[:drops_count] += drop_count
209
+ end
210
+
211
+ def replace_old_metric(key, metric_data)
212
+ dropped = @buffer.shift&.last
213
+ @buffer[key] = metric_hash(metric_data)
214
+ dropped ? COUNTERS.sum { |counter| dropped.fetch(counter, 0).to_i } : 0
215
+ end
216
+
217
+ def merge_metric(target, metric_data)
218
+ COUNTERS.each do |counter|
219
+ target[counter] += metric_data.fetch(counter, 0).to_i
220
+ end
221
+ end
222
+
223
+ def metric_hash(metric_data)
224
+ COUNTERS.each_with_object({period_start: metric_data.fetch(:period_start)}) do |counter, hash|
225
+ hash[counter] = metric_data.fetch(counter, 0).to_i
226
+ end
227
+ end
228
+ end
229
+ end
230
+ end
@@ -0,0 +1,57 @@
1
+ # frozen_string_literal: true
2
+
3
+ module SolidObserver
4
+ class CableSubscriber
5
+ EVENTS = %w[
6
+ broadcast.action_cable
7
+ transmit.action_cable
8
+ transmit_subscription_confirmation.action_cable
9
+ transmit_subscription_rejection.action_cable
10
+ perform_action.action_cable
11
+ ].freeze
12
+
13
+ class << self
14
+ attr_reader :subscriptions
15
+
16
+ def subscribe
17
+ return unless subscription_allowed?
18
+
19
+ self.subscriptions = EVENTS.map { |event_name| subscribe_to(event_name) }
20
+ end
21
+
22
+ def unsubscribe
23
+ return unless subscriptions
24
+
25
+ subscriptions.each { |subscription| ActiveSupport::Notifications.unsubscribe(subscription) }
26
+ self.subscriptions = []
27
+ end
28
+
29
+ def subscribe!
30
+ subscribe
31
+ end
32
+
33
+ def unsubscribe!
34
+ unsubscribe
35
+ end
36
+
37
+ def subscribed?
38
+ !!subscriptions&.any?
39
+ end
40
+
41
+ private
42
+
43
+ attr_writer :subscriptions
44
+
45
+ def subscription_allowed?
46
+ SolidObserver.config.solid_cable_enabled? && !subscribed? && defined?(ActiveSupport::Notifications)
47
+ end
48
+
49
+ def subscribe_to(event_name)
50
+ ActiveSupport::Notifications.subscribe(event_name) do |*args|
51
+ event = ActiveSupport::Notifications::Event.new(*args)
52
+ Services::RecordCableEvent.call(event: event, buffer: CableEventBuffer.instance)
53
+ end
54
+ end
55
+ end
56
+ end
57
+ end
@@ -0,0 +1,28 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "singleton"
4
+
5
+ require_relative "event_buffer_core"
6
+
7
+ module SolidObserver
8
+ class CacheEventBuffer
9
+ include Singleton
10
+ include EventBufferCore
11
+
12
+ INITIAL_METRICS = EventBufferCore::INITIAL_METRICS
13
+
14
+ def initialize
15
+ initialize_event_buffer
16
+ end
17
+
18
+ private
19
+
20
+ def flush_service
21
+ Services::FlushCacheEventBuffer
22
+ end
23
+
24
+ def log_label
25
+ "Cache buffer"
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,229 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "singleton"
4
+ require "concurrent/timer_task"
5
+
6
+ require_relative "services/flush_cache_metrics"
7
+
8
+ module SolidObserver
9
+ class CacheMetricBuffer
10
+ include Singleton
11
+
12
+ INITIAL_METRICS = {
13
+ flush_failures_count: 0,
14
+ drops_count: 0,
15
+ last_flush_at: nil,
16
+ last_flush_duration_ms: nil,
17
+ last_flush_error: nil
18
+ }.freeze
19
+
20
+ def initialize
21
+ @store = MetricStore.new
22
+ @timer_mutex = Mutex.new
23
+ @timer_task = nil
24
+ end
25
+
26
+ def increment(metric_data)
27
+ config = SolidObserver.config
28
+ return unless config.persistence_mode?
29
+
30
+ @store.add(metric_data)
31
+ ensure_timer_running
32
+ flush! if size >= config.buffer_size
33
+ end
34
+
35
+ def flush!
36
+ metrics_to_flush = @store.drain
37
+ return if metrics_to_flush.empty?
38
+
39
+ flush_metrics(metrics_to_flush, monotonic_ms)
40
+ end
41
+
42
+ def flush
43
+ flush!
44
+ end
45
+
46
+ def size
47
+ @store.size
48
+ end
49
+
50
+ def clear
51
+ @store.clear
52
+ end
53
+
54
+ def metrics
55
+ @store.metrics
56
+ end
57
+
58
+ def shutdown
59
+ stop_timer
60
+ flush!
61
+ end
62
+
63
+ private
64
+
65
+ def ensure_timer_running
66
+ timer_to_start, timer_to_stop = replace_timer_if_stopped
67
+ return unless timer_to_start
68
+
69
+ timer_to_stop&.shutdown
70
+ timer_to_start.execute
71
+ end
72
+
73
+ def replace_timer_if_stopped
74
+ @timer_mutex.synchronize do
75
+ current_timer_task = @timer_task
76
+ return [nil, nil] if current_timer_task && !current_timer_task.shuttingdown?
77
+
78
+ [build_timer_task, current_timer_task]
79
+ end
80
+ end
81
+
82
+ def stop_timer
83
+ timer_to_stop = @timer_mutex.synchronize do
84
+ current_timer = @timer_task
85
+ @timer_task = nil
86
+ current_timer
87
+ end
88
+
89
+ timer_to_stop&.shutdown
90
+ end
91
+
92
+ def build_timer_task
93
+ @timer_task = Concurrent::TimerTask.new(
94
+ execution_interval: SolidObserver.config.flush_interval,
95
+ run_now: false
96
+ ) { flush! }
97
+ end
98
+
99
+ def monotonic_ms
100
+ Process.clock_gettime(Process::CLOCK_MONOTONIC, :millisecond)
101
+ end
102
+
103
+ def flush_metrics(metrics_to_flush, started_at_ms)
104
+ Services::FlushCacheMetrics.call(metrics_to_flush)
105
+ @store.record_flush_success(monotonic_ms - started_at_ms)
106
+ rescue => error
107
+ handle_flush_error(error, metrics_to_flush)
108
+ end
109
+
110
+ def handle_flush_error(error, metrics_to_flush)
111
+ @store.requeue(metrics_to_flush)
112
+ @store.record_flush_failure(error)
113
+ Rails.logger&.error("[SolidObserver] Cache metric buffer flush failed: #{error.message}") if defined?(Rails)
114
+ end
115
+
116
+ class MetricStore
117
+ def initialize
118
+ @mutex = Mutex.new
119
+ @buffer = {}
120
+ @metrics = INITIAL_METRICS.dup
121
+ end
122
+
123
+ def add(metric_data)
124
+ @mutex.synchronize { add_metric(metric_data) }
125
+ end
126
+
127
+ def drain
128
+ @mutex.synchronize do
129
+ drained = @buffer.values.map(&:dup)
130
+ @buffer.clear
131
+ drained
132
+ end
133
+ end
134
+
135
+ def requeue(metrics_to_flush)
136
+ @mutex.synchronize { add_metrics_with_capacity(metrics_to_flush + @buffer.values) }
137
+ end
138
+
139
+ def clear
140
+ @mutex.synchronize { @buffer.clear }
141
+ end
142
+
143
+ def size
144
+ @mutex.synchronize { @buffer.size }
145
+ end
146
+
147
+ def metrics
148
+ @mutex.synchronize do
149
+ {
150
+ size: @buffer.size,
151
+ max_buffer_size: SolidObserver.config.max_buffer_size
152
+ }.merge(@metrics.dup)
153
+ end
154
+ end
155
+
156
+ def record_flush_success(duration_ms)
157
+ @mutex.synchronize do
158
+ @metrics.merge!(
159
+ last_flush_at: Time.current,
160
+ last_flush_duration_ms: duration_ms,
161
+ last_flush_error: nil
162
+ )
163
+ end
164
+ end
165
+
166
+ def record_flush_failure(error)
167
+ @mutex.synchronize do
168
+ @metrics[:flush_failures_count] += 1
169
+ @metrics[:last_flush_error] = error.message
170
+ end
171
+ end
172
+
173
+ private
174
+
175
+ def add_metric(metric_data)
176
+ config = SolidObserver.config
177
+ key = [metric_data.fetch(:event_type), metric_data.fetch(:period_start)]
178
+ if @buffer.key?(key)
179
+ merge_metric(@buffer.fetch(key), metric_data)
180
+ elsif @buffer.size < config.max_buffer_size
181
+ @buffer[key] = metric_hash(metric_data)
182
+ else
183
+ handle_overflow(key, metric_data)
184
+ end
185
+ end
186
+
187
+ def add_metrics_with_capacity(metrics)
188
+ @buffer.clear
189
+ metrics.each { |metric| add_metric(metric) }
190
+ end
191
+
192
+ def handle_overflow(key, metric_data)
193
+ drop_count = if SolidObserver.config.buffer_overflow_strategy == :drop_old
194
+ replace_old_metric(key, metric_data)
195
+ else
196
+ metric_data.fetch(:operations_count, 1).to_i
197
+ end
198
+
199
+ @metrics[:drops_count] += drop_count
200
+ end
201
+
202
+ def replace_old_metric(key, metric_data)
203
+ dropped = @buffer.shift&.last
204
+ @buffer[key] = metric_hash(metric_data)
205
+ dropped ? dropped.fetch(:operations_count, 1).to_i : 0
206
+ end
207
+
208
+ def merge_metric(target, metric_data)
209
+ target[:operations_count] += metric_data.fetch(:operations_count, 1).to_i
210
+ target[:hits_count] += metric_data.fetch(:hits_count, 0).to_i
211
+ target[:misses_count] += metric_data.fetch(:misses_count, 0).to_i
212
+ target[:errors_count] += metric_data.fetch(:errors_count, 0).to_i
213
+ target[:duration_total] += metric_data.fetch(:duration_total, 0.0).to_f
214
+ end
215
+
216
+ def metric_hash(metric_data)
217
+ {
218
+ event_type: metric_data.fetch(:event_type),
219
+ period_start: metric_data.fetch(:period_start),
220
+ operations_count: metric_data.fetch(:operations_count, 1).to_i,
221
+ hits_count: metric_data.fetch(:hits_count, 0).to_i,
222
+ misses_count: metric_data.fetch(:misses_count, 0).to_i,
223
+ errors_count: metric_data.fetch(:errors_count, 0).to_i,
224
+ duration_total: metric_data.fetch(:duration_total, 0.0).to_f
225
+ }
226
+ end
227
+ end
228
+ end
229
+ end
@@ -0,0 +1,47 @@
1
+ # frozen_string_literal: true
2
+
3
+ module SolidObserver
4
+ class CacheSubscriber
5
+ EVENTS = %w[
6
+ cache_read.active_support
7
+ cache_write.active_support
8
+ cache_delete.active_support
9
+ cache_exist?.active_support
10
+ cache_read_multi.active_support
11
+ cache_write_multi.active_support
12
+ cache_delete_multi.active_support
13
+ ].freeze
14
+
15
+ class << self
16
+ def subscribe!
17
+ return unless subscription_allowed?
18
+
19
+ @subscriptions = EVENTS.map { |event_name| subscribe_to(event_name) }
20
+ end
21
+
22
+ def unsubscribe!
23
+ return unless @subscriptions
24
+
25
+ @subscriptions.each { |subscription| ActiveSupport::Notifications.unsubscribe(subscription) }
26
+ @subscriptions = []
27
+ end
28
+
29
+ def subscribed?
30
+ !!@subscriptions&.any?
31
+ end
32
+
33
+ private
34
+
35
+ def subscription_allowed?
36
+ SolidObserver.config.solid_cache_enabled? && !subscribed? && defined?(ActiveSupport::Notifications)
37
+ end
38
+
39
+ def subscribe_to(event_name)
40
+ ActiveSupport::Notifications.subscribe(event_name) do |*args|
41
+ event = ActiveSupport::Notifications::Event.new(*args)
42
+ Services::RecordCacheEvent.call(event: event, buffer: CacheEventBuffer.instance)
43
+ end
44
+ end
45
+ end
46
+ end
47
+ end