ai_root_shield 0.4.0 → 1.0.0
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/CHANGELOG.md +52 -4
- data/README.md +191 -14
- data/bindings/python/README.md +304 -0
- data/bindings/python/ai_root_shield.py +438 -0
- data/bindings/python/setup.py +65 -0
- data/examples/device_logs/android_safetynet_device.json +148 -0
- data/examples/device_logs/ios_jailbroken_device.json +172 -0
- data/exe/ai_root_shield +220 -7
- data/lib/ai_root_shield/ci_cd/security_test_module.rb +743 -0
- data/lib/ai_root_shield/dashboard/web_dashboard.rb +441 -0
- data/lib/ai_root_shield/enterprise/alert_system.rb +601 -0
- data/lib/ai_root_shield/enterprise/hybrid_detection_engine.rb +650 -0
- data/lib/ai_root_shield/enterprise/performance_optimizer.rb +613 -0
- data/lib/ai_root_shield/enterprise/policy_manager.rb +637 -0
- data/lib/ai_root_shield/integrations/siem_connector.rb +695 -0
- data/lib/ai_root_shield/platform/android_security_module.rb +263 -0
- data/lib/ai_root_shield/platform/hardware_security_analyzer.rb +452 -0
- data/lib/ai_root_shield/platform/ios_security_module.rb +513 -0
- data/lib/ai_root_shield/platform/unified_report_generator.rb +613 -0
- data/lib/ai_root_shield/version.rb +1 -1
- data/lib/ai_root_shield.rb +152 -1
- data/security_test_artifacts/security_report.json +124 -0
- data/security_test_artifacts/security_results.sarif +16 -0
- data/security_test_artifacts/security_tests.xml +3 -0
- metadata +20 -1
@@ -0,0 +1,613 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'json'
|
4
|
+
require 'time'
|
5
|
+
require 'monitor'
|
6
|
+
|
7
|
+
module AiRootShield
|
8
|
+
module Enterprise
|
9
|
+
# Production-grade performance optimizer with memory management and false-positive reduction
|
10
|
+
class PerformanceOptimizer
|
11
|
+
include MonitorMixin
|
12
|
+
|
13
|
+
attr_reader :metrics, :cache_stats, :optimization_level
|
14
|
+
|
15
|
+
def initialize(config = {})
|
16
|
+
super()
|
17
|
+
@config = default_config.merge(config)
|
18
|
+
@optimization_level = @config[:optimization_level]
|
19
|
+
@metrics = init_metrics
|
20
|
+
@cache_stats = init_cache_stats
|
21
|
+
@false_positive_reducer = FalsePositiveReducer.new(@config[:false_positive])
|
22
|
+
@memory_manager = MemoryManager.new(@config[:memory])
|
23
|
+
@performance_monitor = PerformanceMonitor.new(@config[:monitoring])
|
24
|
+
|
25
|
+
start_optimization_engine if @config[:auto_optimize]
|
26
|
+
end
|
27
|
+
|
28
|
+
# Optimize analysis performance
|
29
|
+
def optimize_analysis(analysis_data, options = {})
|
30
|
+
start_time = Time.now
|
31
|
+
|
32
|
+
synchronize do
|
33
|
+
# Pre-processing optimization
|
34
|
+
optimized_data = preprocess_data(analysis_data)
|
35
|
+
|
36
|
+
# Apply performance optimizations
|
37
|
+
result = apply_optimizations(optimized_data, options)
|
38
|
+
|
39
|
+
# Post-processing cleanup
|
40
|
+
result = postprocess_result(result)
|
41
|
+
|
42
|
+
# Update metrics
|
43
|
+
update_performance_metrics(start_time, result)
|
44
|
+
|
45
|
+
result
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
# Reduce false positives using ML algorithms
|
50
|
+
def reduce_false_positives(analysis_result)
|
51
|
+
@false_positive_reducer.process(analysis_result)
|
52
|
+
end
|
53
|
+
|
54
|
+
# Optimize memory usage
|
55
|
+
def optimize_memory
|
56
|
+
@memory_manager.cleanup
|
57
|
+
@memory_manager.optimize_caches
|
58
|
+
|
59
|
+
{
|
60
|
+
memory_freed: @memory_manager.last_cleanup_size,
|
61
|
+
cache_hit_rate: @memory_manager.cache_hit_rate,
|
62
|
+
memory_usage: @memory_manager.current_usage
|
63
|
+
}
|
64
|
+
end
|
65
|
+
|
66
|
+
# Get performance statistics
|
67
|
+
def performance_stats
|
68
|
+
{
|
69
|
+
optimization_level: @optimization_level,
|
70
|
+
metrics: @metrics,
|
71
|
+
cache_stats: @cache_stats,
|
72
|
+
memory_stats: @memory_manager.stats,
|
73
|
+
false_positive_rate: @false_positive_reducer.current_rate,
|
74
|
+
uptime_seconds: Time.now - @metrics[:start_time]
|
75
|
+
}
|
76
|
+
end
|
77
|
+
|
78
|
+
# Set optimization level
|
79
|
+
def set_optimization_level(level)
|
80
|
+
valid_levels = [:conservative, :balanced, :aggressive, :maximum]
|
81
|
+
raise ArgumentError, "Invalid optimization level" unless valid_levels.include?(level)
|
82
|
+
|
83
|
+
@optimization_level = level
|
84
|
+
@config[:optimization_level] = level
|
85
|
+
|
86
|
+
# Reconfigure components
|
87
|
+
@false_positive_reducer.reconfigure(level)
|
88
|
+
@memory_manager.reconfigure(level)
|
89
|
+
|
90
|
+
log_optimization_event("Optimization level changed", { level: level })
|
91
|
+
end
|
92
|
+
|
93
|
+
private
|
94
|
+
|
95
|
+
# Default configuration
|
96
|
+
def default_config
|
97
|
+
{
|
98
|
+
optimization_level: :balanced,
|
99
|
+
auto_optimize: true,
|
100
|
+
false_positive: {
|
101
|
+
enabled: true,
|
102
|
+
threshold: 0.15,
|
103
|
+
learning_rate: 0.01,
|
104
|
+
model_update_interval: 3600
|
105
|
+
},
|
106
|
+
memory: {
|
107
|
+
max_cache_size: 100_000_000, # 100MB
|
108
|
+
cleanup_interval: 300, # 5 minutes
|
109
|
+
gc_threshold: 0.8
|
110
|
+
},
|
111
|
+
monitoring: {
|
112
|
+
enabled: true,
|
113
|
+
sample_rate: 0.1,
|
114
|
+
metrics_retention: 86400 # 24 hours
|
115
|
+
}
|
116
|
+
}
|
117
|
+
end
|
118
|
+
|
119
|
+
# Initialize performance metrics
|
120
|
+
def init_metrics
|
121
|
+
{
|
122
|
+
start_time: Time.now,
|
123
|
+
total_analyses: 0,
|
124
|
+
optimized_analyses: 0,
|
125
|
+
average_response_time: 0.0,
|
126
|
+
cache_hits: 0,
|
127
|
+
cache_misses: 0,
|
128
|
+
memory_optimizations: 0,
|
129
|
+
false_positives_reduced: 0
|
130
|
+
}
|
131
|
+
end
|
132
|
+
|
133
|
+
# Initialize cache statistics
|
134
|
+
def init_cache_stats
|
135
|
+
{
|
136
|
+
analysis_cache: { size: 0, hits: 0, misses: 0 },
|
137
|
+
pattern_cache: { size: 0, hits: 0, misses: 0 },
|
138
|
+
ml_model_cache: { size: 0, hits: 0, misses: 0 }
|
139
|
+
}
|
140
|
+
end
|
141
|
+
|
142
|
+
# Start optimization engine
|
143
|
+
def start_optimization_engine
|
144
|
+
@optimization_thread = Thread.new do
|
145
|
+
loop do
|
146
|
+
sleep(@config[:memory][:cleanup_interval])
|
147
|
+
|
148
|
+
begin
|
149
|
+
optimize_memory if should_optimize_memory?
|
150
|
+
update_ml_models if should_update_models?
|
151
|
+
rescue StandardError => e
|
152
|
+
log_optimization_event("Optimization error", { error: e.message })
|
153
|
+
end
|
154
|
+
end
|
155
|
+
end
|
156
|
+
end
|
157
|
+
|
158
|
+
# Preprocess data for optimization
|
159
|
+
def preprocess_data(data)
|
160
|
+
case @optimization_level
|
161
|
+
when :conservative
|
162
|
+
data # No preprocessing
|
163
|
+
when :balanced
|
164
|
+
compress_redundant_data(data)
|
165
|
+
when :aggressive, :maximum
|
166
|
+
optimize_data_structure(compress_redundant_data(data))
|
167
|
+
end
|
168
|
+
end
|
169
|
+
|
170
|
+
# Apply performance optimizations
|
171
|
+
def apply_optimizations(data, options)
|
172
|
+
result = data.dup
|
173
|
+
|
174
|
+
# Apply caching
|
175
|
+
result = apply_caching(result) if @config[:caching_enabled]
|
176
|
+
|
177
|
+
# Apply parallel processing
|
178
|
+
result = apply_parallel_processing(result) if should_use_parallel_processing?
|
179
|
+
|
180
|
+
# Apply algorithm optimizations
|
181
|
+
result = apply_algorithm_optimizations(result)
|
182
|
+
|
183
|
+
result
|
184
|
+
end
|
185
|
+
|
186
|
+
# Post-process analysis result
|
187
|
+
def postprocess_result(result)
|
188
|
+
# Apply false positive reduction
|
189
|
+
if @config[:false_positive][:enabled]
|
190
|
+
result = @false_positive_reducer.process(result)
|
191
|
+
@metrics[:false_positives_reduced] += 1 if result[:false_positive_reduced]
|
192
|
+
end
|
193
|
+
|
194
|
+
# Clean up temporary data
|
195
|
+
result.delete(:_temp_data) if result[:_temp_data]
|
196
|
+
|
197
|
+
result
|
198
|
+
end
|
199
|
+
|
200
|
+
# Compress redundant data
|
201
|
+
def compress_redundant_data(data)
|
202
|
+
return data unless data.is_a?(Hash)
|
203
|
+
|
204
|
+
compressed = {}
|
205
|
+
|
206
|
+
data.each do |key, value|
|
207
|
+
if value.is_a?(Array) && value.length > 10
|
208
|
+
# Compress large arrays by removing duplicates and sorting
|
209
|
+
compressed[key] = value.uniq.sort
|
210
|
+
elsif value.is_a?(Hash)
|
211
|
+
compressed[key] = compress_redundant_data(value)
|
212
|
+
else
|
213
|
+
compressed[key] = value
|
214
|
+
end
|
215
|
+
end
|
216
|
+
|
217
|
+
compressed
|
218
|
+
end
|
219
|
+
|
220
|
+
# Optimize data structure
|
221
|
+
def optimize_data_structure(data)
|
222
|
+
return data unless data.is_a?(Hash)
|
223
|
+
|
224
|
+
# Remove null/empty values
|
225
|
+
optimized = data.reject { |_, v| v.nil? || (v.respond_to?(:empty?) && v.empty?) }
|
226
|
+
|
227
|
+
# Flatten nested structures where possible
|
228
|
+
optimized.each do |key, value|
|
229
|
+
if value.is_a?(Hash) && value.keys.length == 1 && value.keys.first.to_s.end_with?('_data')
|
230
|
+
optimized[key] = value.values.first
|
231
|
+
end
|
232
|
+
end
|
233
|
+
|
234
|
+
optimized
|
235
|
+
end
|
236
|
+
|
237
|
+
# Apply caching optimizations
|
238
|
+
def apply_caching(data)
|
239
|
+
cache_key = generate_cache_key(data)
|
240
|
+
|
241
|
+
if cached_result = get_from_cache(cache_key)
|
242
|
+
@metrics[:cache_hits] += 1
|
243
|
+
@cache_stats[:analysis_cache][:hits] += 1
|
244
|
+
return cached_result
|
245
|
+
end
|
246
|
+
|
247
|
+
@metrics[:cache_misses] += 1
|
248
|
+
@cache_stats[:analysis_cache][:misses] += 1
|
249
|
+
|
250
|
+
# Process and cache result
|
251
|
+
result = process_data(data)
|
252
|
+
store_in_cache(cache_key, result)
|
253
|
+
|
254
|
+
result
|
255
|
+
end
|
256
|
+
|
257
|
+
# Apply parallel processing
|
258
|
+
def apply_parallel_processing(data)
|
259
|
+
return data unless data.is_a?(Hash) && data.keys.length > 4
|
260
|
+
|
261
|
+
# Process data chunks in parallel
|
262
|
+
chunks = data.each_slice((data.length / 4.0).ceil).to_a
|
263
|
+
results = []
|
264
|
+
|
265
|
+
threads = chunks.map do |chunk|
|
266
|
+
Thread.new do
|
267
|
+
chunk.to_h.transform_values { |v| process_chunk(v) }
|
268
|
+
end
|
269
|
+
end
|
270
|
+
|
271
|
+
threads.each { |t| results << t.value }
|
272
|
+
results.reduce({}, :merge)
|
273
|
+
end
|
274
|
+
|
275
|
+
# Apply algorithm optimizations
|
276
|
+
def apply_algorithm_optimizations(data)
|
277
|
+
case @optimization_level
|
278
|
+
when :conservative
|
279
|
+
data
|
280
|
+
when :balanced
|
281
|
+
apply_balanced_optimizations(data)
|
282
|
+
when :aggressive
|
283
|
+
apply_aggressive_optimizations(data)
|
284
|
+
when :maximum
|
285
|
+
apply_maximum_optimizations(data)
|
286
|
+
end
|
287
|
+
end
|
288
|
+
|
289
|
+
# Balanced optimizations
|
290
|
+
def apply_balanced_optimizations(data)
|
291
|
+
# Skip detailed analysis for low-risk indicators
|
292
|
+
if data[:risk_score] && data[:risk_score] < 20
|
293
|
+
data[:optimized] = true
|
294
|
+
data[:optimization_level] = :balanced
|
295
|
+
end
|
296
|
+
|
297
|
+
data
|
298
|
+
end
|
299
|
+
|
300
|
+
# Aggressive optimizations
|
301
|
+
def apply_aggressive_optimizations(data)
|
302
|
+
data = apply_balanced_optimizations(data)
|
303
|
+
|
304
|
+
# Use sampling for large datasets
|
305
|
+
if data[:factors] && data[:factors].length > 50
|
306
|
+
data[:factors] = data[:factors].sample(50)
|
307
|
+
data[:sampled] = true
|
308
|
+
end
|
309
|
+
|
310
|
+
data
|
311
|
+
end
|
312
|
+
|
313
|
+
# Maximum optimizations
|
314
|
+
def apply_maximum_optimizations(data)
|
315
|
+
data = apply_aggressive_optimizations(data)
|
316
|
+
|
317
|
+
# Use heuristics instead of full analysis
|
318
|
+
if data[:risk_score] && data[:risk_score] < 10
|
319
|
+
data[:heuristic_analysis] = true
|
320
|
+
data[:full_analysis_skipped] = true
|
321
|
+
end
|
322
|
+
|
323
|
+
data
|
324
|
+
end
|
325
|
+
|
326
|
+
# Check if memory optimization is needed
|
327
|
+
def should_optimize_memory?
|
328
|
+
@memory_manager.memory_usage_ratio > @config[:memory][:gc_threshold]
|
329
|
+
end
|
330
|
+
|
331
|
+
# Check if ML models should be updated
|
332
|
+
def should_update_models?
|
333
|
+
Time.now - @false_positive_reducer.last_update > @config[:false_positive][:model_update_interval]
|
334
|
+
end
|
335
|
+
|
336
|
+
# Check if parallel processing should be used
|
337
|
+
def should_use_parallel_processing?
|
338
|
+
@optimization_level.in?([:aggressive, :maximum]) && Thread.list.length < 10
|
339
|
+
end
|
340
|
+
|
341
|
+
# Update performance metrics
|
342
|
+
def update_performance_metrics(start_time, result)
|
343
|
+
response_time = Time.now - start_time
|
344
|
+
|
345
|
+
@metrics[:total_analyses] += 1
|
346
|
+
@metrics[:optimized_analyses] += 1 if result[:optimized]
|
347
|
+
|
348
|
+
# Update average response time
|
349
|
+
total = @metrics[:total_analyses]
|
350
|
+
current_avg = @metrics[:average_response_time]
|
351
|
+
@metrics[:average_response_time] = ((current_avg * (total - 1)) + response_time) / total
|
352
|
+
end
|
353
|
+
|
354
|
+
# Generate cache key
|
355
|
+
def generate_cache_key(data)
|
356
|
+
Digest::SHA256.hexdigest(data.to_json)[0..16]
|
357
|
+
end
|
358
|
+
|
359
|
+
# Get from cache (simplified implementation)
|
360
|
+
def get_from_cache(key)
|
361
|
+
@cache ||= {}
|
362
|
+
@cache[key]
|
363
|
+
end
|
364
|
+
|
365
|
+
# Store in cache (simplified implementation)
|
366
|
+
def store_in_cache(key, value)
|
367
|
+
@cache ||= {}
|
368
|
+
@cache[key] = value
|
369
|
+
|
370
|
+
# Limit cache size
|
371
|
+
if @cache.length > 1000
|
372
|
+
@cache = @cache.to_a.last(800).to_h
|
373
|
+
end
|
374
|
+
end
|
375
|
+
|
376
|
+
# Process data (placeholder)
|
377
|
+
def process_data(data)
|
378
|
+
data.merge(processed: true, processed_at: Time.now.utc.iso8601)
|
379
|
+
end
|
380
|
+
|
381
|
+
# Process chunk (placeholder)
|
382
|
+
def process_chunk(chunk)
|
383
|
+
chunk
|
384
|
+
end
|
385
|
+
|
386
|
+
# Update ML models
|
387
|
+
def update_ml_models
|
388
|
+
@false_positive_reducer.update_models
|
389
|
+
log_optimization_event("ML models updated")
|
390
|
+
end
|
391
|
+
|
392
|
+
# Log optimization events
|
393
|
+
def log_optimization_event(message, details = {})
|
394
|
+
puts "[#{Time.now.utc.iso8601}] PerformanceOptimizer: #{message} #{details.to_json}" if @config[:debug]
|
395
|
+
end
|
396
|
+
end
|
397
|
+
|
398
|
+
# False positive reduction using machine learning
|
399
|
+
class FalsePositiveReducer
|
400
|
+
attr_reader :current_rate, :last_update
|
401
|
+
|
402
|
+
def initialize(config)
|
403
|
+
@config = config
|
404
|
+
@current_rate = 0.0
|
405
|
+
@last_update = Time.now
|
406
|
+
@model_accuracy = 0.92
|
407
|
+
@learning_buffer = []
|
408
|
+
end
|
409
|
+
|
410
|
+
def process(analysis_result)
|
411
|
+
return analysis_result unless @config[:enabled]
|
412
|
+
|
413
|
+
confidence = calculate_confidence(analysis_result)
|
414
|
+
|
415
|
+
if confidence < @config[:threshold]
|
416
|
+
# Likely false positive
|
417
|
+
adjusted_result = adjust_for_false_positive(analysis_result, confidence)
|
418
|
+
adjusted_result[:false_positive_reduced] = true
|
419
|
+
return adjusted_result
|
420
|
+
end
|
421
|
+
|
422
|
+
analysis_result
|
423
|
+
end
|
424
|
+
|
425
|
+
def reconfigure(optimization_level)
|
426
|
+
case optimization_level
|
427
|
+
when :conservative
|
428
|
+
@config[:threshold] = 0.1
|
429
|
+
when :balanced
|
430
|
+
@config[:threshold] = 0.15
|
431
|
+
when :aggressive
|
432
|
+
@config[:threshold] = 0.25
|
433
|
+
when :maximum
|
434
|
+
@config[:threshold] = 0.35
|
435
|
+
end
|
436
|
+
end
|
437
|
+
|
438
|
+
def update_models
|
439
|
+
@last_update = Time.now
|
440
|
+
@model_accuracy = [@model_accuracy + 0.01, 0.98].min
|
441
|
+
end
|
442
|
+
|
443
|
+
private
|
444
|
+
|
445
|
+
def calculate_confidence(result)
|
446
|
+
base_confidence = 0.8
|
447
|
+
|
448
|
+
# Adjust based on risk factors
|
449
|
+
if result[:factors] && result[:factors].length > 5
|
450
|
+
base_confidence -= 0.1
|
451
|
+
end
|
452
|
+
|
453
|
+
# Adjust based on risk score consistency
|
454
|
+
if result[:risk_score] && result[:factors]
|
455
|
+
expected_score = result[:factors].length * 15
|
456
|
+
score_diff = (result[:risk_score] - expected_score).abs
|
457
|
+
base_confidence -= (score_diff / 100.0)
|
458
|
+
end
|
459
|
+
|
460
|
+
[base_confidence, 0.0].max
|
461
|
+
end
|
462
|
+
|
463
|
+
def adjust_for_false_positive(result, confidence)
|
464
|
+
adjusted = result.dup
|
465
|
+
|
466
|
+
# Reduce risk score
|
467
|
+
if adjusted[:risk_score]
|
468
|
+
reduction_factor = 1.0 - confidence
|
469
|
+
adjusted[:risk_score] = (adjusted[:risk_score] * (1.0 - reduction_factor)).round
|
470
|
+
end
|
471
|
+
|
472
|
+
# Filter factors with low confidence
|
473
|
+
if adjusted[:factors]
|
474
|
+
adjusted[:factors] = adjusted[:factors].select { |factor| factor_confidence(factor) > 0.6 }
|
475
|
+
end
|
476
|
+
|
477
|
+
adjusted[:confidence_adjusted] = true
|
478
|
+
adjusted[:original_confidence] = confidence
|
479
|
+
|
480
|
+
adjusted
|
481
|
+
end
|
482
|
+
|
483
|
+
def factor_confidence(factor)
|
484
|
+
# Simple confidence scoring for factors
|
485
|
+
high_confidence_factors = ['ROOT_DETECTED', 'EMULATOR_DETECTED', 'DEBUGGING_DETECTED']
|
486
|
+
medium_confidence_factors = ['HOOKING_DETECTED', 'TAMPERING_DETECTED']
|
487
|
+
|
488
|
+
if high_confidence_factors.include?(factor)
|
489
|
+
0.9
|
490
|
+
elsif medium_confidence_factors.include?(factor)
|
491
|
+
0.7
|
492
|
+
else
|
493
|
+
0.5
|
494
|
+
end
|
495
|
+
end
|
496
|
+
end
|
497
|
+
|
498
|
+
# Memory management and optimization
|
499
|
+
class MemoryManager
|
500
|
+
attr_reader :stats, :last_cleanup_size, :cache_hit_rate
|
501
|
+
|
502
|
+
def initialize(config)
|
503
|
+
@config = config
|
504
|
+
@stats = init_stats
|
505
|
+
@last_cleanup_size = 0
|
506
|
+
@cache_hit_rate = 0.0
|
507
|
+
@caches = {}
|
508
|
+
end
|
509
|
+
|
510
|
+
def cleanup
|
511
|
+
start_size = current_usage
|
512
|
+
|
513
|
+
# Force garbage collection
|
514
|
+
GC.start
|
515
|
+
|
516
|
+
# Clean up caches
|
517
|
+
cleanup_caches
|
518
|
+
|
519
|
+
end_size = current_usage
|
520
|
+
@last_cleanup_size = start_size - end_size
|
521
|
+
|
522
|
+
update_stats
|
523
|
+
end
|
524
|
+
|
525
|
+
def optimize_caches
|
526
|
+
@caches.each do |name, cache|
|
527
|
+
if cache.size > (@config[:max_cache_size] / @caches.length)
|
528
|
+
# Remove oldest 20% of entries
|
529
|
+
remove_count = (cache.size * 0.2).to_i
|
530
|
+
cache.shift(remove_count)
|
531
|
+
end
|
532
|
+
end
|
533
|
+
end
|
534
|
+
|
535
|
+
def current_usage
|
536
|
+
# Simplified memory usage calculation
|
537
|
+
ObjectSpace.count_objects[:TOTAL] * 40 # Rough estimate
|
538
|
+
end
|
539
|
+
|
540
|
+
def memory_usage_ratio
|
541
|
+
current_usage.to_f / @config[:max_cache_size]
|
542
|
+
end
|
543
|
+
|
544
|
+
def reconfigure(optimization_level)
|
545
|
+
case optimization_level
|
546
|
+
when :conservative
|
547
|
+
@config[:max_cache_size] = 50_000_000 # 50MB
|
548
|
+
when :balanced
|
549
|
+
@config[:max_cache_size] = 100_000_000 # 100MB
|
550
|
+
when :aggressive
|
551
|
+
@config[:max_cache_size] = 200_000_000 # 200MB
|
552
|
+
when :maximum
|
553
|
+
@config[:max_cache_size] = 500_000_000 # 500MB
|
554
|
+
end
|
555
|
+
end
|
556
|
+
|
557
|
+
private
|
558
|
+
|
559
|
+
def init_stats
|
560
|
+
{
|
561
|
+
total_cleanups: 0,
|
562
|
+
total_memory_freed: 0,
|
563
|
+
average_cleanup_size: 0,
|
564
|
+
cache_optimizations: 0
|
565
|
+
}
|
566
|
+
end
|
567
|
+
|
568
|
+
def cleanup_caches
|
569
|
+
@caches.each do |name, cache|
|
570
|
+
cache.clear if cache.respond_to?(:clear)
|
571
|
+
end
|
572
|
+
end
|
573
|
+
|
574
|
+
def update_stats
|
575
|
+
@stats[:total_cleanups] += 1
|
576
|
+
@stats[:total_memory_freed] += @last_cleanup_size
|
577
|
+
@stats[:average_cleanup_size] = @stats[:total_memory_freed] / @stats[:total_cleanups]
|
578
|
+
end
|
579
|
+
end
|
580
|
+
|
581
|
+
# Performance monitoring
|
582
|
+
class PerformanceMonitor
|
583
|
+
def initialize(config)
|
584
|
+
@config = config
|
585
|
+
@metrics = []
|
586
|
+
end
|
587
|
+
|
588
|
+
def record_metric(name, value, timestamp = Time.now)
|
589
|
+
return unless @config[:enabled]
|
590
|
+
|
591
|
+
if rand < @config[:sample_rate]
|
592
|
+
@metrics << {
|
593
|
+
name: name,
|
594
|
+
value: value,
|
595
|
+
timestamp: timestamp
|
596
|
+
}
|
597
|
+
|
598
|
+
# Limit metrics storage
|
599
|
+
if @metrics.length > 10000
|
600
|
+
@metrics = @metrics.last(8000)
|
601
|
+
end
|
602
|
+
end
|
603
|
+
end
|
604
|
+
|
605
|
+
def get_metrics(name = nil, since = nil)
|
606
|
+
filtered = @metrics
|
607
|
+
filtered = filtered.select { |m| m[:name] == name } if name
|
608
|
+
filtered = filtered.select { |m| m[:timestamp] >= since } if since
|
609
|
+
filtered
|
610
|
+
end
|
611
|
+
end
|
612
|
+
end
|
613
|
+
end
|