magick-feature-flags 0.9.20 → 0.9.22

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: e4f9379b630e9db00b28614f347d523edb33d63c842d6b5c9fd747be02f1d75d
4
- data.tar.gz: ffa32ee13faa08781c68870813c15ea7b3dcc15fe65f8c1f2618952251fe870c
3
+ metadata.gz: 42699b3af1d542ac14b10f2b82ea4156da27b68e3e2a07e715cd528f0e3f936b
4
+ data.tar.gz: 0c5235c6cb220201ed0443d1a4633009325d410620d8d28ecbb836cac01bdc25
5
5
  SHA512:
6
- metadata.gz: 0f2651c1f8528b4eb385f82a629c7891e0a78ec4a796b574baa8228a2bfcaddf88ee6855af45f9fba9c12b4d7d475e1d8aa6ba6b1002706a1247a8ef81b540e4
7
- data.tar.gz: 418cc8c6909969581b147df052c72ffc9635a86b07726f69f0c6085bef3bbb804515d34a3d6ca782f8b4c03d4127722a5547a483ca2d0cddfc56f3bef4e6e864
6
+ metadata.gz: 10fb678c4aeaf5a17f721341e01ec937bf9a14042e4a90fa0bcc1aba13c5e91272aba144fa05913a020660ffd602ccf7481d216e92364338c7e4a111794e0896
7
+ data.tar.gz: '079e2d11d6ab0339929f965c1eb05fc934ea88522f26e268df324c366ad28268f87131233fe0f2315a5494328ae5fa18790e8e512fa550372852c3c1d61c3faf'
@@ -33,15 +33,55 @@ module Magick
33
33
  # Update targeting empty cache after loading
34
34
  @_targeting_empty = @targeting.empty?
35
35
  # Cache performance metrics availability (check once, not on every call)
36
- @_perf_metrics_enabled = Magick.performance_metrics != nil
36
+ # Only enable if performance_metrics exists AND is actually being used
37
+ @_perf_metrics_enabled = !Magick.performance_metrics.nil?
37
38
  # Save description and display_name to adapter if they were provided and not already in adapter
38
39
  save_metadata_if_new
39
40
  end
40
41
 
41
42
  def enabled?(context = {})
42
- # Fastest path: check enabled status directly (no overhead)
43
- check_enabled(context)
43
+ # Check performance metrics dynamically (in case enabled after feature creation)
44
+ # But cache the check result for performance
45
+ perf_metrics = Magick.performance_metrics
46
+ perf_metrics_enabled = !perf_metrics.nil?
47
+
48
+ # Update cached flag if it changed
49
+ @_perf_metrics_enabled = perf_metrics_enabled if @_perf_metrics_enabled != perf_metrics_enabled
50
+
51
+ # Fast path: if performance metrics disabled, skip all overhead
52
+ return check_enabled(context) unless perf_metrics_enabled
53
+
54
+ # Performance metrics enabled: measure and record
55
+ # Use inline timing to avoid function call overhead
56
+ start_time = Process.clock_gettime(Process::CLOCK_MONOTONIC)
57
+ result = check_enabled(context)
58
+ duration = (Process.clock_gettime(Process::CLOCK_MONOTONIC) - start_time) * 1000 # milliseconds
59
+
60
+ # Record metrics (fast path - minimal overhead)
61
+ perf_metrics.record(name, 'enabled?', duration, success: true)
62
+
63
+ # Rails 8+ events (only in development or when explicitly enabled)
64
+ if @_rails_events_enabled
65
+ if result
66
+ Magick::Rails::Events.feature_enabled(name, context: context)
67
+ else
68
+ Magick::Rails::Events.feature_disabled(name, context: context)
69
+ end
70
+ end
71
+
72
+ # Warn if deprecated (only if enabled)
73
+ if status == :deprecated && result && !context[:allow_deprecated] && Magick.warn_on_deprecated
74
+ warn "DEPRECATED: Feature '#{name}' is deprecated and will be removed."
75
+ Magick::Rails::Events.deprecated_warning(name) if @_rails_events_enabled
76
+ end
77
+
78
+ result
44
79
  rescue StandardError => e
80
+ # Record error metrics if enabled
81
+ if perf_metrics_enabled && perf_metrics
82
+ duration = defined?(start_time) && start_time ? (Process.clock_gettime(Process::CLOCK_MONOTONIC) - start_time) * 1000 : 0.0
83
+ perf_metrics.record(name, 'enabled?', duration, success: false)
84
+ end
45
85
  # Return false on any error (fail-safe)
46
86
  warn "Magick: Error checking feature '#{name}': #{e.message}" if defined?(Rails) && Rails.env.development?
47
87
  false
@@ -452,6 +492,10 @@ module Magick
452
492
  # Reload feature state from adapter (useful when feature is changed externally)
453
493
  def reload
454
494
  load_from_adapter
495
+ # Update targeting empty cache
496
+ @_targeting_empty = @targeting.empty?
497
+ # Update performance metrics flag (in case it was enabled after feature creation)
498
+ @_perf_metrics_enabled = !Magick.performance_metrics.nil?
455
499
  # Update registered feature instance if it exists
456
500
  if Magick.features.key?(name)
457
501
  registered = Magick.features[name]
@@ -461,6 +505,8 @@ module Magick
461
505
  registered.instance_variable_set(:@description, @description)
462
506
  registered.instance_variable_set(:@display_name, @display_name)
463
507
  registered.instance_variable_set(:@targeting, @targeting.dup)
508
+ registered.instance_variable_set(:@_targeting_empty, @_targeting_empty)
509
+ registered.instance_variable_set(:@_perf_metrics_enabled, @_perf_metrics_enabled)
464
510
  end
465
511
  true
466
512
  end
@@ -36,44 +36,59 @@ module Magick
36
36
  # If redis_enabled is explicitly set, use it; otherwise default to false
37
37
  # It will be enabled later via enable_redis_tracking if Redis adapter is available
38
38
  @redis_enabled = redis_enabled.nil? ? false : redis_enabled
39
+ # Cache expensive checks for performance
40
+ @_rails_events_enabled = defined?(Magick::Rails::Events) && Magick::Rails::Events.rails8?
41
+ @_adapter_available = nil # Will be cached on first check
42
+ @_redis_available = nil # Will be cached on first check
39
43
  end
40
44
 
41
45
  # Public accessor for redis_enabled
42
46
  attr_reader :redis_enabled
43
47
 
44
48
  def record(feature_name, operation, duration, success: true)
49
+ # Fast path: minimize work in mutex
45
50
  feature_name_str = feature_name.to_s
46
- metric = Metric.new(feature_name_str, operation, duration, success: success)
47
51
 
52
+ # Minimize mutex lock time - only update counters
53
+ pending_count = nil
54
+ total_pending = nil
48
55
  @mutex.synchronize do
49
- @metrics << metric
56
+ # Only create Metric object if we're keeping metrics in memory
57
+ # Most of the time we just need counters, not full metric objects
58
+ if @metrics.length < 1000
59
+ metric = Metric.new(feature_name_str, operation, duration, success: success)
60
+ @metrics << metric
61
+ end
50
62
  @usage_count[feature_name_str] += 1
51
63
  @pending_updates[feature_name_str] += 1
64
+ pending_count = @pending_updates[feature_name_str]
65
+ total_pending = @pending_updates.values.sum
52
66
  # Keep only last 1000 metrics (as a safety limit)
53
- # Note: Metrics are removed from memory after flushing to Redis to reduce memory usage
54
67
  @metrics.shift if @metrics.length > 1000
55
68
  end
56
69
 
57
- # Rails 8+ event for usage tracking
58
- if defined?(Magick::Rails::Events) && Magick::Rails::Events.rails8?
70
+ # Rails 8+ event for usage tracking (cached check)
71
+ if @_rails_events_enabled
59
72
  Magick::Rails::Events.usage_tracked(feature_name, operation: operation, duration: duration, success: success)
60
73
  end
61
74
 
62
- # Batch flush to Redis if needed
63
- flush_to_redis_if_needed
75
+ # Batch flush check - only if we're close to batch size (avoid expensive checks)
76
+ # Check pending count first before doing expensive adapter checks
77
+ flush_to_redis_if_needed if pending_count >= @batch_size || total_pending >= @batch_size
64
78
 
65
- metric
79
+ nil # Don't return metric object to avoid allocation overhead
66
80
  end
67
81
 
68
82
  def flush_to_redis_if_needed
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?
83
+ # Cache adapter availability check (expensive)
84
+ if @_adapter_available.nil?
85
+ adapter = Magick.adapter_registry || Magick.default_adapter_registry
86
+ @_adapter_available = adapter
87
+ @_redis_available = adapter.is_a?(Magick::Adapters::Registry) && adapter.redis_available? if adapter
88
+ end
75
89
 
76
- return unless redis_available || @redis_enabled
90
+ return unless @_adapter_available
91
+ return unless @_redis_available || @redis_enabled
77
92
  return if @pending_updates.empty?
78
93
 
79
94
  should_flush = false
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Magick
4
- VERSION = '0.9.20'
4
+ VERSION = '0.9.22'
5
5
  end
data/lib/magick.rb CHANGED
@@ -38,6 +38,12 @@ module Magick
38
38
  if value && adapter_registry.is_a?(Adapters::Registry) && adapter_registry.redis_available?
39
39
  value.enable_redis_tracking(enable: true)
40
40
  end
41
+ # Update all existing features to enable performance metrics tracking
42
+ if value
43
+ features.each_value do |feature|
44
+ feature.instance_variable_set(:@_perf_metrics_enabled, true)
45
+ end
46
+ end
41
47
  value
42
48
  end
43
49
 
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.20
4
+ version: 0.9.22
5
5
  platform: ruby
6
6
  authors:
7
7
  - Andrew Lobanov