magick-feature-flags 0.9.10 → 0.9.18
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/magick/config.rb +48 -10
- data/lib/magick/performance_metrics.rb +174 -39
- data/lib/magick/rails/railtie.rb +41 -2
- data/lib/magick/version.rb +1 -1
- data/lib/magick.rb +42 -3
- metadata +1 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 7df3e59dc8f0736e18fb93603f53038f48b5302769c42cc37ab87f2afcba0307
|
|
4
|
+
data.tar.gz: e0235e0c7518860061b7b67a27aee9c1be88346c8292733aae0a2c794c2288d7
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 0a76c637a3cd96cebd6fa91be4f10df8e1174713fabb82c4cdb8df1cecd2d4c6c6ea46e00949cc485e6c0a3d46a9180c718204361a2630c8a71a069a0c6f6bbd
|
|
7
|
+
data.tar.gz: f0682f004002eab91412ee9d948e69f3f84de41238ab2d5ca3f06abec1abf2616c86063a66198f8a596f32d8dc2cf545c9db0b62961e6dc98a4d97a39b01e5cc
|
data/lib/magick/config.rb
CHANGED
|
@@ -41,21 +41,50 @@ module Magick
|
|
|
41
41
|
def redis(url: nil, namespace: nil, **options)
|
|
42
42
|
@redis_url = url if url
|
|
43
43
|
@redis_namespace = namespace if namespace
|
|
44
|
-
configure_redis_adapter(url: url, namespace: namespace, **options)
|
|
44
|
+
redis_adapter = configure_redis_adapter(url: url, namespace: namespace, **options)
|
|
45
|
+
|
|
46
|
+
# Automatically create Registry adapter if it doesn't exist
|
|
47
|
+
# This allows users to just call `redis url: ...` without needing to call `adapter :registry`
|
|
48
|
+
if @adapter_registry
|
|
49
|
+
# If registry already exists, update it with the new Redis adapter
|
|
50
|
+
# This allows reconfiguring Redis without recreating the registry
|
|
51
|
+
if redis_adapter && @adapter_registry.is_a?(Adapters::Registry)
|
|
52
|
+
# Update the Redis adapter in the existing registry
|
|
53
|
+
@adapter_registry.instance_variable_set(:@redis_adapter, redis_adapter)
|
|
54
|
+
# Restart cache invalidation subscriber with new Redis adapter
|
|
55
|
+
@adapter_registry.send(:start_cache_invalidation_subscriber) if redis_adapter
|
|
56
|
+
end
|
|
57
|
+
else
|
|
58
|
+
memory_adapter = configure_memory_adapter
|
|
59
|
+
cb = Magick::CircuitBreaker.new(
|
|
60
|
+
failure_threshold: @circuit_breaker_threshold,
|
|
61
|
+
timeout: @circuit_breaker_timeout
|
|
62
|
+
)
|
|
63
|
+
@adapter_registry = Adapters::Registry.new(
|
|
64
|
+
memory_adapter,
|
|
65
|
+
redis_adapter,
|
|
66
|
+
circuit_breaker: cb,
|
|
67
|
+
async: @async_updates
|
|
68
|
+
)
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
redis_adapter
|
|
45
72
|
end
|
|
46
73
|
|
|
47
74
|
def performance_metrics(enabled: true, redis_tracking: nil, batch_size: 100, flush_interval: 60, **_options)
|
|
48
75
|
return unless enabled
|
|
49
76
|
|
|
50
|
-
|
|
51
|
-
# Store redis_tracking preference for later application
|
|
77
|
+
# Store redis_tracking preference before creating instance
|
|
52
78
|
@performance_metrics_redis_tracking = redis_tracking
|
|
53
|
-
#
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
79
|
+
# Create instance with redis_enabled set if explicitly provided
|
|
80
|
+
initial_redis_enabled = redis_tracking == true
|
|
81
|
+
@performance_metrics = PerformanceMetrics.new(
|
|
82
|
+
batch_size: batch_size,
|
|
83
|
+
flush_interval: flush_interval,
|
|
84
|
+
redis_enabled: initial_redis_enabled
|
|
85
|
+
)
|
|
86
|
+
# If explicitly set to false, disable it
|
|
87
|
+
@performance_metrics.enable_redis_tracking(enable: false) if redis_tracking == false
|
|
59
88
|
# If nil, will be auto-determined in apply! method
|
|
60
89
|
@performance_metrics
|
|
61
90
|
end
|
|
@@ -100,10 +129,19 @@ module Magick
|
|
|
100
129
|
Magick.performance_metrics = performance_metrics
|
|
101
130
|
# Re-apply redis_tracking setting after assignment (in case object was replaced)
|
|
102
131
|
if defined?(@performance_metrics_redis_tracking) && !@performance_metrics_redis_tracking.nil?
|
|
132
|
+
# Explicitly set value takes precedence
|
|
103
133
|
Magick.performance_metrics.enable_redis_tracking(enable: @performance_metrics_redis_tracking)
|
|
104
134
|
# Otherwise, auto-enable if Redis adapter is configured
|
|
105
135
|
# Check Magick.adapter_registry (after it's been set) instead of local instance variable
|
|
106
136
|
elsif Magick.adapter_registry.is_a?(Adapters::Registry) && Magick.adapter_registry.redis_available?
|
|
137
|
+
# Always enable if Redis adapter is available (unless explicitly disabled above)
|
|
138
|
+
Magick.performance_metrics.enable_redis_tracking(enable: true)
|
|
139
|
+
end
|
|
140
|
+
elsif Magick.performance_metrics
|
|
141
|
+
# If no new performance_metrics was configured, but one exists, still try to enable Redis tracking
|
|
142
|
+
# if Redis adapter is available and redis_tracking wasn't explicitly disabled
|
|
143
|
+
# Only auto-enable if not explicitly disabled
|
|
144
|
+
if Magick.adapter_registry.is_a?(Adapters::Registry) && Magick.adapter_registry.redis_available? && !(defined?(@performance_metrics_redis_tracking) && @performance_metrics_redis_tracking == false)
|
|
107
145
|
Magick.performance_metrics.enable_redis_tracking(enable: true)
|
|
108
146
|
end
|
|
109
147
|
end
|
|
@@ -150,7 +188,7 @@ module Magick
|
|
|
150
188
|
memory_adapter = memory || configure_memory_adapter
|
|
151
189
|
redis_adapter = redis || configure_redis_adapter
|
|
152
190
|
|
|
153
|
-
cb = circuit_breaker || CircuitBreaker.new(
|
|
191
|
+
cb = circuit_breaker || Magick::CircuitBreaker.new(
|
|
154
192
|
failure_threshold: @circuit_breaker_threshold,
|
|
155
193
|
timeout: @circuit_breaker_timeout
|
|
156
194
|
)
|
|
@@ -24,17 +24,23 @@ module Magick
|
|
|
24
24
|
end
|
|
25
25
|
end
|
|
26
26
|
|
|
27
|
-
def initialize(batch_size: 100, flush_interval: 60)
|
|
27
|
+
def initialize(batch_size: 100, flush_interval: 60, redis_enabled: nil)
|
|
28
28
|
@metrics = []
|
|
29
29
|
@mutex = Mutex.new
|
|
30
|
-
@usage_count = Hash.new(0)
|
|
31
|
-
@pending_updates = Hash.new(0) # For Redis batching
|
|
30
|
+
@usage_count = Hash.new(0) # Memory-only counts (reset on each process boot)
|
|
31
|
+
@pending_updates = Hash.new(0) # For Redis batching (reset on each process boot)
|
|
32
|
+
@flushed_counts = Hash.new(0) # Track counts that have been flushed to Redis (to avoid double-counting)
|
|
32
33
|
@batch_size = batch_size
|
|
33
34
|
@flush_interval = flush_interval
|
|
34
35
|
@last_flush = Time.now
|
|
35
|
-
|
|
36
|
+
# If redis_enabled is explicitly set, use it; otherwise default to false
|
|
37
|
+
# It will be enabled later via enable_redis_tracking if Redis adapter is available
|
|
38
|
+
@redis_enabled = redis_enabled.nil? ? false : redis_enabled
|
|
36
39
|
end
|
|
37
40
|
|
|
41
|
+
# Public accessor for redis_enabled
|
|
42
|
+
attr_reader :redis_enabled
|
|
43
|
+
|
|
38
44
|
def record(feature_name, operation, duration, success: true)
|
|
39
45
|
feature_name_str = feature_name.to_s
|
|
40
46
|
metric = Metric.new(feature_name_str, operation, duration, success: success)
|
|
@@ -43,7 +49,8 @@ module Magick
|
|
|
43
49
|
@metrics << metric
|
|
44
50
|
@usage_count[feature_name_str] += 1
|
|
45
51
|
@pending_updates[feature_name_str] += 1
|
|
46
|
-
# Keep only last 1000 metrics
|
|
52
|
+
# Keep only last 1000 metrics (as a safety limit)
|
|
53
|
+
# Note: Metrics are removed from memory after flushing to Redis to reduce memory usage
|
|
47
54
|
@metrics.shift if @metrics.length > 1000
|
|
48
55
|
end
|
|
49
56
|
|
|
@@ -59,41 +66,98 @@ module Magick
|
|
|
59
66
|
end
|
|
60
67
|
|
|
61
68
|
def flush_to_redis_if_needed
|
|
62
|
-
|
|
69
|
+
# Always try to flush if Redis adapter is available, even if @redis_enabled is false
|
|
70
|
+
# This ensures stats are collected even if redis_enabled wasn't set correctly
|
|
71
|
+
adapter = Magick.adapter_registry || Magick.default_adapter_registry
|
|
72
|
+
return unless adapter # No adapter at all, skip
|
|
73
|
+
|
|
74
|
+
redis_available = adapter.is_a?(Magick::Adapters::Registry) && adapter.redis_available?
|
|
75
|
+
|
|
76
|
+
return unless redis_available || @redis_enabled
|
|
63
77
|
return if @pending_updates.empty?
|
|
64
78
|
|
|
65
79
|
should_flush = false
|
|
66
80
|
@mutex.synchronize do
|
|
67
|
-
# Flush if we have enough pending updates or enough time has passed
|
|
68
|
-
|
|
81
|
+
# Flush if we have enough pending updates (sum of all counts) or enough time has passed
|
|
82
|
+
# Check total count of pending updates, not just number of keys
|
|
83
|
+
total_pending_count = @pending_updates.values.sum
|
|
84
|
+
should_flush = true if total_pending_count >= @batch_size || (Time.now - @last_flush) >= @flush_interval
|
|
69
85
|
end
|
|
70
86
|
|
|
71
87
|
flush_to_redis if should_flush
|
|
72
88
|
end
|
|
73
89
|
|
|
90
|
+
# Force flush pending updates to Redis immediately
|
|
91
|
+
# Useful when you need up-to-date stats across processes
|
|
92
|
+
def force_flush_to_redis
|
|
93
|
+
return if @pending_updates.empty?
|
|
94
|
+
|
|
95
|
+
flush_to_redis
|
|
96
|
+
end
|
|
97
|
+
|
|
74
98
|
def flush_to_redis
|
|
75
99
|
updates_to_flush = nil
|
|
100
|
+
duration_stats_to_flush = {}
|
|
76
101
|
@mutex.synchronize do
|
|
77
102
|
return if @pending_updates.empty?
|
|
78
103
|
|
|
79
104
|
updates_to_flush = @pending_updates.dup
|
|
105
|
+
flushed_feature_names = updates_to_flush.keys.to_set
|
|
80
106
|
@pending_updates.clear
|
|
107
|
+
# Track what we're flushing so we don't double-count in usage_count
|
|
108
|
+
updates_to_flush.each do |feature_name, count|
|
|
109
|
+
@flushed_counts[feature_name] += count
|
|
110
|
+
end
|
|
111
|
+
|
|
112
|
+
# Collect duration stats for flushed features
|
|
113
|
+
# Group metrics by feature_name and operation, sum durations and count occurrences
|
|
114
|
+
@metrics.each do |metric|
|
|
115
|
+
next unless flushed_feature_names.include?(metric.feature_name) && metric.success
|
|
116
|
+
|
|
117
|
+
key = "#{metric.feature_name}:#{metric.operation}"
|
|
118
|
+
duration_stats_to_flush[key] ||= { sum: 0.0, count: 0, feature_name: metric.feature_name,
|
|
119
|
+
operation: metric.operation }
|
|
120
|
+
duration_stats_to_flush[key][:sum] += metric.duration
|
|
121
|
+
duration_stats_to_flush[key][:count] += 1
|
|
122
|
+
end
|
|
123
|
+
|
|
124
|
+
# Remove metrics for flushed features from memory to reduce memory usage
|
|
125
|
+
# Metrics are already persisted in Redis, so we don't need to keep them in memory
|
|
126
|
+
@metrics.reject! { |m| flushed_feature_names.include?(m.feature_name) }
|
|
127
|
+
|
|
81
128
|
@last_flush = Time.now
|
|
82
129
|
end
|
|
83
130
|
|
|
84
131
|
return if updates_to_flush.nil? || updates_to_flush.empty?
|
|
85
132
|
|
|
86
133
|
# Update Redis in batch
|
|
134
|
+
# Always try to flush if Redis adapter is available, regardless of @redis_enabled flag
|
|
135
|
+
# This ensures stats are collected even if redis_enabled wasn't set correctly
|
|
87
136
|
begin
|
|
88
137
|
adapter = Magick.adapter_registry || Magick.default_adapter_registry
|
|
89
138
|
if adapter.is_a?(Magick::Adapters::Registry) && adapter.redis_available?
|
|
90
139
|
redis = adapter.redis_client
|
|
91
140
|
if redis
|
|
141
|
+
# Flush usage counts
|
|
92
142
|
updates_to_flush.each do |feature_name, count|
|
|
93
143
|
redis_key = "magick:stats:#{feature_name}"
|
|
94
144
|
redis.incrby(redis_key, count)
|
|
95
145
|
redis.expire(redis_key, 86_400 * 7) # Keep stats for 7 days
|
|
96
146
|
end
|
|
147
|
+
|
|
148
|
+
# Flush duration stats (sum and count for calculating averages)
|
|
149
|
+
duration_stats_to_flush.each do |_key, stats|
|
|
150
|
+
sum_key = "magick:duration:sum:#{stats[:feature_name]}:#{stats[:operation]}"
|
|
151
|
+
count_key = "magick:duration:count:#{stats[:feature_name]}:#{stats[:operation]}"
|
|
152
|
+
redis.incrbyfloat(sum_key, stats[:sum])
|
|
153
|
+
redis.incrby(count_key, stats[:count])
|
|
154
|
+
redis.expire(sum_key, 86_400 * 7) # Keep stats for 7 days
|
|
155
|
+
redis.expire(count_key, 86_400 * 7)
|
|
156
|
+
end
|
|
157
|
+
|
|
158
|
+
# Auto-enable redis tracking if we successfully flushed to Redis
|
|
159
|
+
# This ensures redis_enabled is set correctly even if config didn't work
|
|
160
|
+
@redis_enabled ||= true
|
|
97
161
|
end
|
|
98
162
|
end
|
|
99
163
|
rescue StandardError => e
|
|
@@ -103,67 +167,137 @@ module Magick
|
|
|
103
167
|
end
|
|
104
168
|
|
|
105
169
|
def enable_redis_tracking(enable: true)
|
|
170
|
+
old_value = @redis_enabled
|
|
106
171
|
@redis_enabled = enable
|
|
172
|
+
|
|
107
173
|
# Flush any pending updates when enabling
|
|
108
|
-
|
|
174
|
+
if enable && !@pending_updates.empty?
|
|
175
|
+
begin
|
|
176
|
+
flush_to_redis
|
|
177
|
+
rescue StandardError => e
|
|
178
|
+
# Don't fail if flush fails - the flag is still set
|
|
179
|
+
if defined?(Rails) && Rails.env.development?
|
|
180
|
+
warn "Magick: Failed to flush stats when enabling Redis tracking: #{e.message}"
|
|
181
|
+
end
|
|
182
|
+
end
|
|
183
|
+
end
|
|
184
|
+
|
|
185
|
+
# Verify the value was set (for debugging)
|
|
186
|
+
if !(@redis_enabled == enable) && defined?(Rails) && Rails.env.development?
|
|
187
|
+
warn "Magick: Failed to set redis_enabled to #{enable}, current value: #{@redis_enabled}"
|
|
188
|
+
end
|
|
189
|
+
|
|
190
|
+
true
|
|
109
191
|
end
|
|
110
192
|
|
|
111
193
|
def average_duration(feature_name: nil, operation: nil)
|
|
194
|
+
# Calculate from memory metrics (current process, not yet flushed)
|
|
112
195
|
filtered = @metrics.select do |m|
|
|
113
196
|
(feature_name.nil? || m.feature_name == feature_name.to_s) &&
|
|
114
197
|
(operation.nil? || m.operation == operation.to_s) &&
|
|
115
198
|
m.success
|
|
116
199
|
end
|
|
117
|
-
return 0.0 if filtered.empty?
|
|
118
200
|
|
|
119
|
-
filtered.sum(&:duration)
|
|
201
|
+
memory_sum = filtered.sum(&:duration)
|
|
202
|
+
memory_count = filtered.length
|
|
203
|
+
|
|
204
|
+
# Also get from Redis if available (persisted across processes)
|
|
205
|
+
redis_sum = 0.0
|
|
206
|
+
redis_count = 0
|
|
207
|
+
begin
|
|
208
|
+
adapter = Magick.adapter_registry || Magick.default_adapter_registry
|
|
209
|
+
if adapter.is_a?(Magick::Adapters::Registry) && adapter.redis_available?
|
|
210
|
+
redis = adapter.redis_client
|
|
211
|
+
if redis
|
|
212
|
+
if feature_name && operation
|
|
213
|
+
# Specific feature and operation
|
|
214
|
+
sum_key = "magick:duration:sum:#{feature_name}:#{operation}"
|
|
215
|
+
count_key = "magick:duration:count:#{feature_name}:#{operation}"
|
|
216
|
+
redis_sum = redis.get(sum_key).to_f
|
|
217
|
+
redis_count = redis.get(count_key).to_i
|
|
218
|
+
elsif feature_name
|
|
219
|
+
# All operations for this feature
|
|
220
|
+
pattern = "magick:duration:sum:#{feature_name}:*"
|
|
221
|
+
redis.keys(pattern).each do |sum_key|
|
|
222
|
+
op = sum_key.to_s.sub("magick:duration:sum:#{feature_name}:", '')
|
|
223
|
+
count_key = "magick:duration:count:#{feature_name}:#{op}"
|
|
224
|
+
redis_sum += redis.get(sum_key).to_f
|
|
225
|
+
redis_count += redis.get(count_key).to_i
|
|
226
|
+
end
|
|
227
|
+
else
|
|
228
|
+
# All features and operations (not recommended, but supported)
|
|
229
|
+
redis.keys('magick:duration:sum:*').each do |sum_key|
|
|
230
|
+
count_key = sum_key.to_s.sub(':sum:', ':count:')
|
|
231
|
+
redis_sum += redis.get(sum_key).to_f
|
|
232
|
+
redis_count += redis.get(count_key).to_i
|
|
233
|
+
end
|
|
234
|
+
end
|
|
235
|
+
end
|
|
236
|
+
end
|
|
237
|
+
rescue StandardError
|
|
238
|
+
# Silently fail
|
|
239
|
+
end
|
|
240
|
+
|
|
241
|
+
total_sum = memory_sum + redis_sum
|
|
242
|
+
total_count = memory_count + redis_count
|
|
243
|
+
|
|
244
|
+
return 0.0 if total_count == 0
|
|
245
|
+
|
|
246
|
+
total_sum / total_count.to_f
|
|
120
247
|
end
|
|
121
248
|
|
|
122
249
|
def usage_count(feature_name)
|
|
123
250
|
feature_name_str = feature_name.to_s
|
|
124
|
-
memory_count = @usage_count[feature_name_str] || 0
|
|
125
251
|
|
|
126
|
-
#
|
|
252
|
+
# Force flush any pending updates for this feature before reading to ensure accuracy
|
|
253
|
+
# This ensures stats are synced across processes immediately
|
|
254
|
+
force_flush_to_redis if @pending_updates[feature_name_str] && @pending_updates[feature_name_str] > 0
|
|
255
|
+
|
|
256
|
+
# Memory count = total counts in current process minus what we've already flushed
|
|
257
|
+
# This avoids double-counting with Redis
|
|
258
|
+
memory_count = (@usage_count[feature_name_str] || 0) - (@flushed_counts[feature_name_str] || 0)
|
|
259
|
+
memory_count = 0 if memory_count < 0 # Safety check
|
|
260
|
+
|
|
261
|
+
# Redis count = total counts from all processes (including this process's flushed counts)
|
|
127
262
|
redis_count = 0
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
redis_count = redis.get(redis_key).to_i
|
|
136
|
-
end
|
|
263
|
+
begin
|
|
264
|
+
adapter = Magick.adapter_registry || Magick.default_adapter_registry
|
|
265
|
+
if adapter.is_a?(Magick::Adapters::Registry) && adapter.redis_available?
|
|
266
|
+
redis = adapter.redis_client
|
|
267
|
+
if redis
|
|
268
|
+
redis_key = "magick:stats:#{feature_name_str}"
|
|
269
|
+
redis_count = redis.get(redis_key).to_i
|
|
137
270
|
end
|
|
138
|
-
rescue StandardError
|
|
139
|
-
# Silently fail
|
|
140
271
|
end
|
|
272
|
+
rescue StandardError
|
|
273
|
+
# Silently fail
|
|
141
274
|
end
|
|
142
275
|
|
|
143
|
-
|
|
276
|
+
# Total = Redis (all processes, all time) + memory (current process, not yet flushed)
|
|
277
|
+
redis_count + memory_count
|
|
144
278
|
end
|
|
145
279
|
|
|
146
280
|
def most_used_features(limit: 10)
|
|
147
281
|
# Combine memory and Redis counts
|
|
148
282
|
combined_counts = @usage_count.dup
|
|
149
283
|
|
|
150
|
-
if @redis_enabled
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
284
|
+
# Always check Redis if adapter is available (not just if @redis_enabled)
|
|
285
|
+
# This ensures we get the full count even if redis_enabled flag wasn't set correctly
|
|
286
|
+
begin
|
|
287
|
+
adapter = Magick.adapter_registry || Magick.default_adapter_registry
|
|
288
|
+
if adapter.is_a?(Magick::Adapters::Registry) && adapter.redis_available?
|
|
289
|
+
redis = adapter.redis_client
|
|
290
|
+
if redis
|
|
291
|
+
# Get all stats keys
|
|
292
|
+
redis.keys('magick:stats:*').each do |key|
|
|
293
|
+
feature_name = key.to_s.sub('magick:stats:', '')
|
|
294
|
+
redis_count = redis.get(key).to_i
|
|
295
|
+
combined_counts[feature_name] = (combined_counts[feature_name] || 0) + redis_count
|
|
162
296
|
end
|
|
163
297
|
end
|
|
164
|
-
rescue StandardError
|
|
165
|
-
# Silently fail
|
|
166
298
|
end
|
|
299
|
+
rescue StandardError
|
|
300
|
+
# Silently fail
|
|
167
301
|
end
|
|
168
302
|
|
|
169
303
|
combined_counts.sort_by { |_name, count| -count }.first(limit).to_h
|
|
@@ -174,6 +308,7 @@ module Magick
|
|
|
174
308
|
@metrics.clear
|
|
175
309
|
@usage_count.clear
|
|
176
310
|
@pending_updates.clear
|
|
311
|
+
@flushed_counts.clear
|
|
177
312
|
end
|
|
178
313
|
end
|
|
179
314
|
end
|
data/lib/magick/rails/railtie.rb
CHANGED
|
@@ -29,11 +29,44 @@ if defined?(Rails)
|
|
|
29
29
|
memory_adapter = Adapters::Memory.new
|
|
30
30
|
redis_adapter = Adapters::Redis.new(redis_client)
|
|
31
31
|
magick.adapter_registry = Adapters::Registry.new(memory_adapter, redis_adapter)
|
|
32
|
-
#
|
|
33
|
-
|
|
32
|
+
# Enable Redis tracking if performance metrics exists and Redis is available
|
|
33
|
+
if Magick.performance_metrics && redis_adapter
|
|
34
|
+
Magick.performance_metrics.enable_redis_tracking(enable: true)
|
|
35
|
+
end
|
|
34
36
|
rescue StandardError => e
|
|
35
37
|
Rails.logger&.warn "Magick: Failed to initialize Redis adapter: #{e.message}. Using memory-only adapter."
|
|
38
|
+
# Still set up memory adapter even if Redis fails
|
|
39
|
+
memory_adapter = Adapters::Memory.new
|
|
40
|
+
magick.adapter_registry = Adapters::Registry.new(memory_adapter, nil)
|
|
36
41
|
end
|
|
42
|
+
else
|
|
43
|
+
# No Redis gem, use memory-only adapter
|
|
44
|
+
memory_adapter = Adapters::Memory.new
|
|
45
|
+
magick.adapter_registry = Adapters::Registry.new(memory_adapter, nil)
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
# Ensure adapter_registry is always set (fallback to default if not configured)
|
|
51
|
+
unless Magick.adapter_registry
|
|
52
|
+
Magick.adapter_registry = Magick.default_adapter_registry
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
# Ensure adapter_registry is set and Redis tracking is enabled after all initializers have run
|
|
56
|
+
# This ensures user's config/initializers/magick.rb has been loaded
|
|
57
|
+
config.after_initialize do
|
|
58
|
+
# Ensure adapter_registry is set (fallback to default if not configured)
|
|
59
|
+
unless Magick.adapter_registry
|
|
60
|
+
Magick.adapter_registry = Magick.default_adapter_registry
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
# Force enable Redis tracking if Redis adapter is available
|
|
64
|
+
# This is a final safety net to ensure stats are collected
|
|
65
|
+
if Magick.performance_metrics && Magick.adapter_registry.is_a?(Adapters::Registry) && Magick.adapter_registry.redis_available?
|
|
66
|
+
Magick.performance_metrics.enable_redis_tracking(enable: true)
|
|
67
|
+
# Double-check it was enabled (for debugging)
|
|
68
|
+
unless Magick.performance_metrics.redis_enabled
|
|
69
|
+
Rails.logger&.warn 'Magick: Failed to enable Redis tracking despite Redis adapter being available'
|
|
37
70
|
end
|
|
38
71
|
end
|
|
39
72
|
end
|
|
@@ -64,6 +97,12 @@ if defined?(Rails)
|
|
|
64
97
|
# Preload features in request store
|
|
65
98
|
config.to_prepare do
|
|
66
99
|
RequestStore.store[:magick_features] ||= {} if defined?(RequestStore)
|
|
100
|
+
|
|
101
|
+
# Final check: ensure Redis tracking is enabled (runs on every request in development)
|
|
102
|
+
# This is the absolute last chance to enable it
|
|
103
|
+
if Magick.performance_metrics && Magick.adapter_registry.is_a?(Adapters::Registry) && Magick.adapter_registry.redis_available? && !Magick.performance_metrics.redis_enabled
|
|
104
|
+
Magick.performance_metrics.enable_redis_tracking(enable: true)
|
|
105
|
+
end
|
|
67
106
|
end
|
|
68
107
|
end
|
|
69
108
|
end
|
data/lib/magick/version.rb
CHANGED
data/lib/magick.rb
CHANGED
|
@@ -28,24 +28,63 @@ require_relative 'magick/dsl'
|
|
|
28
28
|
|
|
29
29
|
module Magick
|
|
30
30
|
class << self
|
|
31
|
-
attr_accessor :adapter_registry, :default_adapter, :
|
|
31
|
+
attr_accessor :adapter_registry, :default_adapter, :audit_log, :versioning,
|
|
32
32
|
:warn_on_deprecated
|
|
33
33
|
|
|
34
|
+
# Override performance_metrics setter to auto-enable Redis tracking
|
|
35
|
+
def performance_metrics=(value)
|
|
36
|
+
@performance_metrics = value
|
|
37
|
+
# Auto-enable Redis tracking if Redis adapter is available
|
|
38
|
+
if value && adapter_registry.is_a?(Adapters::Registry) && adapter_registry.redis_available?
|
|
39
|
+
value.enable_redis_tracking(enable: true)
|
|
40
|
+
end
|
|
41
|
+
value
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
# Override adapter_registry setter to auto-enable Redis tracking on existing performance_metrics
|
|
45
|
+
def adapter_registry=(value)
|
|
46
|
+
@adapter_registry = value
|
|
47
|
+
# Auto-enable Redis tracking if performance_metrics exists and Redis adapter is available
|
|
48
|
+
if performance_metrics && value.is_a?(Adapters::Registry) && value.redis_available?
|
|
49
|
+
performance_metrics.enable_redis_tracking(enable: true)
|
|
50
|
+
end
|
|
51
|
+
value
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
# Getter for performance_metrics
|
|
55
|
+
def performance_metrics
|
|
56
|
+
@performance_metrics
|
|
57
|
+
end
|
|
58
|
+
|
|
34
59
|
def configure(&block)
|
|
35
60
|
@performance_metrics ||= PerformanceMetrics.new
|
|
36
61
|
@audit_log ||= AuditLog.new
|
|
37
62
|
@warn_on_deprecated ||= false
|
|
63
|
+
# Ensure adapter_registry is set (fallback to default if not configured)
|
|
64
|
+
@adapter_registry ||= default_adapter_registry
|
|
38
65
|
|
|
39
66
|
# Support both old style and new DSL style
|
|
40
67
|
return unless block_given?
|
|
41
68
|
|
|
42
69
|
if block.arity.zero?
|
|
43
|
-
# New DSL style
|
|
70
|
+
# New DSL style - calls apply! automatically
|
|
44
71
|
ConfigDSL.configure(&block)
|
|
45
72
|
else
|
|
46
|
-
# Old style
|
|
73
|
+
# Old style - need to manually reapply Redis tracking after configuration
|
|
47
74
|
yield self
|
|
75
|
+
# Ensure adapter_registry is still set after configuration
|
|
76
|
+
@adapter_registry ||= default_adapter_registry
|
|
77
|
+
# Enable Redis tracking if adapter is available and performance_metrics exists
|
|
78
|
+
# Only enable if not already enabled (to avoid overriding explicit false setting)
|
|
79
|
+
if @performance_metrics && @adapter_registry.is_a?(Adapters::Registry) && @adapter_registry.redis_available?
|
|
80
|
+
unless @performance_metrics.instance_variable_get(:@redis_enabled)
|
|
81
|
+
@performance_metrics.enable_redis_tracking(enable: true)
|
|
82
|
+
end
|
|
83
|
+
end
|
|
48
84
|
end
|
|
85
|
+
|
|
86
|
+
# Final check: ensure adapter_registry is set
|
|
87
|
+
@adapter_registry ||= default_adapter_registry
|
|
49
88
|
end
|
|
50
89
|
|
|
51
90
|
def [](feature_name)
|