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 +4 -4
- data/lib/magick/performance_metrics.rb +70 -8
- data/lib/magick/version.rb +1 -1
- 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: 0cae8aed5b88784ad2c7b2635b377cd17270044eea05e4f47a18bd3a209c9e3f
|
|
4
|
+
data.tar.gz: f2c69d41bd7a26010f25eee8e0088d660e90fdeb976de961192dcbc52c781c3d
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
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:
|
|
50
|
-
|
|
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,
|
|
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(
|
|
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
|
|
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
|
-
|
|
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
|
data/lib/magick/version.rb
CHANGED