magick-feature-flags 0.9.5 → 0.9.7

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: db6e3bc7c41ca6efc8b8d1e0c3a37bb12ee979f012bb76482b7e45e65b89d2c9
4
- data.tar.gz: fb85c3f115744839d1fcc909e4bb495c3182bddd70977f4a6db525efe4d67e81
3
+ metadata.gz: 015b9568cac92b03d1776240c7236cb395d15189eb496e03ac803979fef2d77d
4
+ data.tar.gz: 3219b3349bb202775c883d7b977ca0dd8d64846625a524abbe858c6d51d9db43
5
5
  SHA512:
6
- metadata.gz: 895c93c5620618068c67643a1d5d989e385dd8c311714cf7af1e0b910a1b32f79739c55f3b7a8f987e73ea58dc7795b381fa2590dcd5bc73a0f4767578cbdb87
7
- data.tar.gz: 677a4ec77be3a13ed1c18ddd37c7609aff6c151dfff0be9a975d40742bb26b6adc86ed9e9c10e57490d665e3e3ec0fc685e36c7f272d16a4688a2829faa26419
6
+ metadata.gz: d93760f46cd383bc5b97f032a992fe4e0fc6e567d50985ab2544475805ffbb4545b105c8033a7aafb5a48149c1169ab7f87e6e70da58bcbfd2554a1935608bb3
7
+ data.tar.gz: 19424a6172af8421a2041a4259f5474a4cffa7cf62cd03582b23f479374b7ca3a9aa56ca57f69e7facae4e372662d70abf80384456247d801ffd6eee141acd56
data/README.md CHANGED
@@ -82,7 +82,12 @@ Magick.configure do
82
82
  async_updates enabled: true
83
83
 
84
84
  # Enable services
85
- performance_metrics enabled: true
85
+ performance_metrics(
86
+ enabled: true,
87
+ redis_tracking: true, # Auto-enabled if Redis is configured
88
+ batch_size: 100, # Flush after 100 updates
89
+ flush_interval: 60 # Or flush every 60 seconds
90
+ )
86
91
  audit_log enabled: true
87
92
  versioning enabled: true
88
93
  warn_on_deprecated enabled: true
@@ -295,7 +300,16 @@ variant = feature.get_variant
295
300
  ```ruby
296
301
  feature = Magick[:advanced_feature]
297
302
  feature.add_dependency(:base_feature)
298
- # advanced_feature will only be enabled if base_feature is also enabled
303
+ # advanced_feature can be enabled independently
304
+ # However, base_feature (dependency) cannot be enabled if advanced_feature (main feature) is disabled
305
+ # This ensures dependencies are only enabled when their parent features are enabled
306
+
307
+ # Example:
308
+ Magick[:advanced_feature].disable # => true
309
+ Magick[:base_feature].enable # => false (cannot enable dependency when main feature is disabled)
310
+
311
+ Magick[:advanced_feature].enable # => true
312
+ Magick[:base_feature].enable # => true (now can enable dependency)
299
313
  ```
300
314
 
301
315
  #### Export/Import
@@ -322,16 +336,55 @@ Magick.versioning.rollback(:my_feature, version: 2)
322
336
  #### Performance Metrics
323
337
 
324
338
  ```ruby
325
- # Get average duration for feature checks
326
- avg_duration = Magick.performance_metrics.average_duration(feature_name: :my_feature)
339
+ # Get comprehensive stats for a feature
340
+ Magick.feature_stats(:my_feature)
341
+ # => {
342
+ # usage_count: 1250,
343
+ # average_duration: 0.032,
344
+ # average_duration_by_operation: {
345
+ # enabled: 0.032,
346
+ # value: 0.0,
347
+ # get_value: 0.0
348
+ # }
349
+ # }
350
+
351
+ # Get just the usage count
352
+ Magick.feature_usage_count(:my_feature)
353
+ # => 1250
354
+
355
+ # Get average duration (optionally filtered by operation)
356
+ Magick.feature_average_duration(:my_feature)
357
+ Magick.feature_average_duration(:my_feature, operation: 'enabled?')
327
358
 
328
359
  # Get most used features
329
- most_used = Magick.performance_metrics.most_used_features(limit: 10)
360
+ Magick.most_used_features(limit: 10)
361
+ # => {
362
+ # "my_feature" => 1250,
363
+ # "another_feature" => 890,
364
+ # ...
365
+ # }
366
+
367
+ # Direct access to performance metrics (for advanced usage)
368
+ Magick.performance_metrics.average_duration(feature_name: :my_feature)
369
+ Magick.performance_metrics.usage_count(:my_feature)
370
+ Magick.performance_metrics.most_used_features(limit: 10)
371
+ ```
372
+
373
+ **Configuration:**
330
374
 
331
- # Get usage count
332
- count = Magick.performance_metrics.usage_count(:my_feature)
375
+ ```ruby
376
+ Magick.configure do
377
+ performance_metrics(
378
+ enabled: true,
379
+ redis_tracking: true, # Auto-enabled if Redis is configured
380
+ batch_size: 100, # Flush after 100 updates
381
+ flush_interval: 60 # Or flush every 60 seconds
382
+ )
383
+ end
333
384
  ```
334
385
 
386
+ **Note:** When `redis_tracking: true` is set, usage counts are persisted to Redis and aggregated across all processes, giving you total usage statistics.
387
+
335
388
  #### Audit Logging
336
389
 
337
390
  ```ruby
@@ -351,7 +404,10 @@ Magick uses a dual-adapter strategy:
351
404
  1. **Memory Adapter**: Fast, in-memory storage with TTL support
352
405
  2. **Redis Adapter**: Persistent storage for distributed systems (optional)
353
406
 
354
- The registry automatically falls back from memory to Redis if a feature isn't found in memory. When features are updated, both adapters are updated simultaneously.
407
+ The registry automatically falls back from memory to Redis if a feature isn't found in memory. When features are updated:
408
+ - Both adapters are updated simultaneously
409
+ - Cache invalidation messages are published via Redis Pub/Sub to notify other processes
410
+ - Targeting updates trigger immediate cache invalidation to ensure consistency
355
411
 
356
412
  #### Memory-Only Mode
357
413
 
@@ -86,6 +86,12 @@ module Magick
86
86
  (memory_features + redis_features).uniq
87
87
  end
88
88
 
89
+ # Explicitly trigger cache invalidation for a feature
90
+ # This is useful for targeting updates that need immediate cache invalidation
91
+ def invalidate_cache(feature_name)
92
+ publish_cache_invalidation(feature_name)
93
+ end
94
+
89
95
  private
90
96
 
91
97
  attr_reader :memory_adapter, :redis_adapter, :circuit_breaker
data/lib/magick/config.rb CHANGED
@@ -48,12 +48,15 @@ module Magick
48
48
  return unless enabled
49
49
 
50
50
  @performance_metrics = PerformanceMetrics.new(batch_size: batch_size, flush_interval: flush_interval)
51
- # Enable Redis tracking if Redis adapter is configured (or explicitly set)
52
- if redis_tracking.nil?
53
- # Auto-enable if Redis adapter exists
54
- redis_tracking = !@redis_url.nil? || configure_redis_adapter != nil
51
+ # Store redis_tracking preference for later application
52
+ @performance_metrics_redis_tracking = redis_tracking
53
+ # Enable Redis tracking if explicitly set to true, otherwise defer to apply! method
54
+ if redis_tracking == true
55
+ @performance_metrics.enable_redis_tracking(enable: true)
56
+ elsif redis_tracking == false
57
+ @performance_metrics.enable_redis_tracking(enable: false)
55
58
  end
56
- @performance_metrics.enable_redis_tracking(enable: redis_tracking) if @performance_metrics
59
+ # If nil, will be auto-determined in apply! method
57
60
  end
58
61
 
59
62
  def audit_log(enabled: true, adapter: nil)
@@ -95,9 +98,15 @@ module Magick
95
98
  Magick.versioning = versioning if versioning
96
99
  Magick.warn_on_deprecated = warn_on_deprecated
97
100
 
98
- # Enable Redis tracking for performance metrics if Redis adapter is configured
99
- if Magick.performance_metrics && adapter_registry.is_a?(Adapters::Registry) && adapter_registry.redis_adapter
100
- Magick.performance_metrics.enable_redis_tracking(enable: true)
101
+ # Enable Redis tracking for performance metrics
102
+ if Magick.performance_metrics
103
+ # If redis_tracking was explicitly set, use that value
104
+ if defined?(@performance_metrics_redis_tracking) && !@performance_metrics_redis_tracking.nil?
105
+ Magick.performance_metrics.enable_redis_tracking(enable: @performance_metrics_redis_tracking)
106
+ # Otherwise, auto-enable if Redis adapter is configured
107
+ elsif adapter_registry.is_a?(Adapters::Registry) && adapter_registry.redis_adapter
108
+ Magick.performance_metrics.enable_redis_tracking(enable: true)
109
+ end
101
110
  end
102
111
  end
103
112
 
@@ -74,7 +74,7 @@ module Magick
74
74
  return false if status == :inactive
75
75
  return false if status == :deprecated && !context[:allow_deprecated]
76
76
 
77
- # Note: We don't check dependencies here because:
77
+ # NOTE: We don't check dependencies here because:
78
78
  # - Main features can be enabled independently
79
79
  # - Dependencies are only prevented from being enabled if the main feature is disabled
80
80
  # - The dependency check is handled in the enable() method, not in enabled?()
@@ -463,6 +463,7 @@ module Magick
463
463
  def delete
464
464
  adapter_registry.delete(name)
465
465
  @stored_value = nil
466
+ @stored_value_initialized = false # Reset initialization flag so get_value returns default_value
466
467
  @targeting = {}
467
468
  # Also remove from Magick.features if registered
468
469
  Magick.features.delete(name.to_s)
@@ -822,11 +823,20 @@ module Magick
822
823
  end
823
824
 
824
825
  def save_targeting
826
+ # Save targeting to adapter (this triggers cache invalidation via Pub/Sub)
825
827
  adapter_registry.set(name, 'targeting', targeting)
828
+
826
829
  # Update the feature in Magick.features if it's registered
827
830
  return unless Magick.features.key?(name)
828
831
 
829
832
  Magick.features[name].instance_variable_set(:@targeting, targeting.dup)
833
+
834
+ # Explicitly trigger cache invalidation for targeting updates
835
+ # Targeting changes affect enabled? checks, so we need immediate cache invalidation
836
+ # even if async updates are enabled
837
+ return unless adapter_registry.respond_to?(:invalidate_cache)
838
+
839
+ adapter_registry.invalidate_cache(name)
830
840
  end
831
841
 
832
842
  def default_for_type
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Magick
4
- VERSION = '0.9.5'
4
+ VERSION = '0.9.7'
5
5
  end
data/lib/magick.rb CHANGED
@@ -128,6 +128,38 @@ module Magick
128
128
  @versioning ||= Versioning.new(adapter_registry || default_adapter_registry)
129
129
  end
130
130
 
131
+ # Get total usage count for a feature (combines memory and Redis counts)
132
+ def feature_stats(feature_name)
133
+ return {} unless performance_metrics
134
+
135
+ {
136
+ usage_count: performance_metrics.usage_count(feature_name),
137
+ average_duration: performance_metrics.average_duration(feature_name: feature_name),
138
+ average_duration_by_operation: {
139
+ enabled: performance_metrics.average_duration(feature_name: feature_name, operation: 'enabled?'),
140
+ value: performance_metrics.average_duration(feature_name: feature_name, operation: 'value'),
141
+ get_value: performance_metrics.average_duration(feature_name: feature_name, operation: 'get_value')
142
+ }
143
+ }
144
+ end
145
+
146
+ # Get usage count for a feature
147
+ def feature_usage_count(feature_name)
148
+ performance_metrics&.usage_count(feature_name) || 0
149
+ end
150
+
151
+ # Get average duration for a feature (optionally filtered by operation)
152
+ def feature_average_duration(feature_name, operation: nil)
153
+ return 0.0 unless performance_metrics
154
+
155
+ performance_metrics.average_duration(feature_name: feature_name, operation: operation)
156
+ end
157
+
158
+ # Get most used features
159
+ def most_used_features(limit: 10)
160
+ performance_metrics&.most_used_features(limit: limit) || {}
161
+ end
162
+
131
163
  def reset!
132
164
  @features = {}
133
165
  @adapter_registry = nil
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: magick-feature-flags
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.9.5
4
+ version: 0.9.7
5
5
  platform: ruby
6
6
  authors:
7
7
  - Andrew Lobanov