magick-feature-flags 0.9.22 → 0.9.23

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: 42699b3af1d542ac14b10f2b82ea4156da27b68e3e2a07e715cd528f0e3f936b
4
- data.tar.gz: 0c5235c6cb220201ed0443d1a4633009325d410620d8d28ecbb836cac01bdc25
3
+ metadata.gz: 0cae8aed5b88784ad2c7b2635b377cd17270044eea05e4f47a18bd3a209c9e3f
4
+ data.tar.gz: f2c69d41bd7a26010f25eee8e0088d660e90fdeb976de961192dcbc52c781c3d
5
5
  SHA512:
6
- metadata.gz: 10fb678c4aeaf5a17f721341e01ec937bf9a14042e4a90fa0bcc1aba13c5e91272aba144fa05913a020660ffd602ccf7481d216e92364338c7e4a111794e0896
7
- data.tar.gz: '079e2d11d6ab0339929f965c1eb05fc934ea88522f26e268df324c366ad28268f87131233fe0f2315a5494328ae5fa18790e8e512fa550372852c3c1d61c3faf'
6
+ metadata.gz: 7a184080f97332ecac03e71f3ae72791ed3a01389a05f84dfdab7d6588dca2a0e8da7c5c5f78b9dc7fee05c09ddbdb302bd8cc97e8a6af0b276e621a01331640
7
+ data.tar.gz: c51c09f333e04b46e980c8c6ff67d434611208d716757b53850d625630a48c73c6f4c961f091909ade1fd33d0170f0128111fefb1d1a181f9a012e77be2be1ab
@@ -40,23 +40,43 @@ module Magick
40
40
  @_rails_events_enabled = defined?(Magick::Rails::Events) && Magick::Rails::Events.rails8?
41
41
  @_adapter_available = nil # Will be cached on first check
42
42
  @_redis_available = nil # Will be cached on first check
43
+
44
+ # Async recording queue for non-blocking metrics
45
+ @async_queue = Queue.new
46
+ @async_thread = nil
47
+ @async_enabled = true # Enable async by default for performance
48
+ start_async_processor
43
49
  end
44
50
 
45
51
  # Public accessor for redis_enabled
46
52
  attr_reader :redis_enabled
47
53
 
48
54
  def record(feature_name, operation, duration, success: true)
49
- # Fast path: minimize work in mutex
50
- feature_name_str = feature_name.to_s
55
+ # Fast path: push to async queue (non-blocking, zero overhead in hot path)
56
+ # Queue#<< is thread-safe and lock-free - extremely fast!
57
+ return unless @async_enabled
58
+
59
+ # Push to async queue - this is lock-free and extremely fast
60
+ # Use non-blocking push (will raise if queue is full, but our queue is unbounded)
61
+ begin
62
+ @async_queue << [feature_name.to_s, operation.to_s, duration, success]
63
+ rescue ThreadError, ClosedQueueError
64
+ # Queue is closed or thread died, disable async
65
+ @async_enabled = false
66
+ end
67
+
68
+ nil
69
+ end
51
70
 
71
+ # Internal: Process metrics from async queue (runs in background thread)
72
+ def process_async_record(feature_name_str, operation_str, duration, success)
52
73
  # Minimize mutex lock time - only update counters
53
74
  pending_count = nil
54
75
  total_pending = nil
55
76
  @mutex.synchronize do
56
77
  # 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
78
  if @metrics.length < 1000
59
- metric = Metric.new(feature_name_str, operation, duration, success: success)
79
+ metric = Metric.new(feature_name_str, operation_str, duration, success: success)
60
80
  @metrics << metric
61
81
  end
62
82
  @usage_count[feature_name_str] += 1
@@ -69,14 +89,48 @@ module Magick
69
89
 
70
90
  # Rails 8+ event for usage tracking (cached check)
71
91
  if @_rails_events_enabled
72
- Magick::Rails::Events.usage_tracked(feature_name, operation: operation, duration: duration, success: success)
92
+ Magick::Rails::Events.usage_tracked(feature_name_str, operation: operation_str, duration: duration,
93
+ success: success)
73
94
  end
74
95
 
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
96
+ # Batch flush check - only if we're close to batch size
77
97
  flush_to_redis_if_needed if pending_count >= @batch_size || total_pending >= @batch_size
98
+ end
78
99
 
79
- nil # Don't return metric object to avoid allocation overhead
100
+ # Start background thread to process async metrics
101
+ def start_async_processor
102
+ return if @async_thread&.alive?
103
+
104
+ @async_thread = Thread.new do
105
+ last_flush_check = Time.now
106
+ loop do
107
+ # Wait for metrics with timeout to allow periodic flush checks
108
+ # Queue#pop with timeout returns nil on timeout, raises on closed queue
109
+ begin
110
+ data = @async_queue.pop(timeout: 1.0)
111
+ rescue ThreadError => e
112
+ # Queue closed or thread interrupted
113
+ break if e.message.include?('queue closed')
114
+
115
+ raise
116
+ end
117
+
118
+ if data
119
+ feature_name_str, operation_str, duration, success = data
120
+ process_async_record(feature_name_str, operation_str, duration, success)
121
+ last_flush_check = Time.now
122
+ elsif Time.now - last_flush_check >= 1.0
123
+ # Timeout - check if we need to flush based on time (every second)
124
+ flush_to_redis_if_needed
125
+ last_flush_check = Time.now
126
+ end
127
+ rescue StandardError => e
128
+ # Log error but continue processing
129
+ warn "Magick: Error in async metrics processor: #{e.message}" if defined?(Rails) && Rails.env.development?
130
+ sleep 0.1 # Brief pause on error
131
+ end
132
+ end
133
+ @async_thread.abort_on_exception = false
80
134
  end
81
135
 
82
136
  def flush_to_redis_if_needed
@@ -326,5 +380,13 @@ module Magick
326
380
  @flushed_counts.clear
327
381
  end
328
382
  end
383
+
384
+ # Stop async processor (for cleanup)
385
+ def stop_async_processor
386
+ @async_enabled = false
387
+ @async_queue.close if @async_queue.respond_to?(:close)
388
+ @async_thread&.kill
389
+ @async_thread = nil
390
+ end
329
391
  end
330
392
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Magick
4
- VERSION = '0.9.22'
4
+ VERSION = '0.9.23'
5
5
  end
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.22
4
+ version: 0.9.23
5
5
  platform: ruby
6
6
  authors:
7
7
  - Andrew Lobanov