Skip to main content

oxirs_stream/
performance_utils.rs

1//! # Advanced Performance Utilities for OxiRS Stream
2//!
3//! High-performance utilities, optimizations, and patterns for achieving
4//! maximum throughput and minimum latency in streaming operations.
5
6use anyhow::{anyhow, Result};
7use serde::{Deserialize, Serialize};
8use std::collections::{HashMap, VecDeque};
9use std::sync::Arc;
10use std::time::{Duration, Instant};
11use tokio::sync::{RwLock, Semaphore};
12use tokio::task::yield_now;
13use tracing::{debug, info, warn};
14
15use crate::StreamEvent;
16
17/// Configuration for performance optimizations
18#[derive(Debug, Clone, Serialize, Deserialize)]
19pub struct PerformanceUtilsConfig {
20    /// Enable zero-copy optimizations
21    pub enable_zero_copy: bool,
22    /// Enable SIMD optimizations where available
23    pub enable_simd: bool,
24    /// Enable memory pooling
25    pub enable_memory_pooling: bool,
26    /// Enable batch processing optimizations
27    pub enable_batch_optimization: bool,
28    /// Maximum batch size for optimal performance
29    pub optimal_batch_size: usize,
30    /// Enable adaptive rate limiting
31    pub enable_adaptive_rate_limiting: bool,
32    /// Enable intelligent prefetching
33    pub enable_prefetching: bool,
34    /// CPU core count for parallel processing
35    pub cpu_cores: usize,
36}
37
38impl Default for PerformanceUtilsConfig {
39    fn default() -> Self {
40        Self {
41            enable_zero_copy: true,
42            enable_simd: true,
43            enable_memory_pooling: true,
44            enable_batch_optimization: true,
45            optimal_batch_size: 1000,
46            enable_adaptive_rate_limiting: true,
47            enable_prefetching: true,
48            cpu_cores: std::thread::available_parallelism()
49                .map(|n| n.get())
50                .unwrap_or(1),
51        }
52    }
53}
54
55/// High-performance adaptive batcher for streaming events
56pub struct AdaptiveBatcher {
57    config: PerformanceUtilsConfig,
58    batch_buffer: Arc<RwLock<Vec<StreamEvent>>>,
59    batch_stats: Arc<RwLock<BatchingStats>>,
60    last_flush: Instant,
61    target_latency: Duration,
62    optimal_batch_size: Arc<RwLock<usize>>,
63}
64
65/// Statistics for adaptive batching performance
66#[derive(Debug, Clone, Default)]
67pub struct BatchingStats {
68    pub total_batches: u64,
69    pub total_events: u64,
70    pub avg_batch_size: f64,
71    pub avg_latency_ms: f64,
72    pub throughput_events_per_sec: f64,
73    pub efficiency_score: f64,
74}
75
76impl AdaptiveBatcher {
77    /// Create a new adaptive batcher
78    pub fn new(config: PerformanceUtilsConfig, target_latency: Duration) -> Self {
79        Self {
80            optimal_batch_size: Arc::new(RwLock::new(config.optimal_batch_size)),
81            config,
82            batch_buffer: Arc::new(RwLock::new(Vec::new())),
83            batch_stats: Arc::new(RwLock::new(BatchingStats::default())),
84            last_flush: Instant::now(),
85            target_latency,
86        }
87    }
88
89    /// Add an event to the batch buffer
90    pub async fn add_event(&mut self, event: StreamEvent) -> Result<Option<Vec<StreamEvent>>> {
91        let mut buffer = self.batch_buffer.write().await;
92        buffer.push(event);
93
94        let optimal_size = *self.optimal_batch_size.read().await;
95        let time_since_last_flush = self.last_flush.elapsed();
96
97        // Check if we should flush the batch
98        if buffer.len() >= optimal_size || time_since_last_flush >= self.target_latency {
99            let batch = std::mem::take(&mut *buffer);
100            self.last_flush = Instant::now();
101
102            // Update statistics and optimize batch size
103            self.update_stats_and_optimize(batch.len(), time_since_last_flush)
104                .await;
105
106            Ok(Some(batch))
107        } else {
108            Ok(None)
109        }
110    }
111
112    /// Force flush the current batch
113    pub async fn flush(&mut self) -> Result<Option<Vec<StreamEvent>>> {
114        let mut buffer = self.batch_buffer.write().await;
115        if buffer.is_empty() {
116            return Ok(None);
117        }
118
119        let batch = std::mem::take(&mut *buffer);
120        let time_since_last_flush = self.last_flush.elapsed();
121        self.last_flush = Instant::now();
122
123        self.update_stats_and_optimize(batch.len(), time_since_last_flush)
124            .await;
125
126        Ok(Some(batch))
127    }
128
129    /// Update statistics and optimize batch size based on performance
130    async fn update_stats_and_optimize(&self, batch_size: usize, latency: Duration) {
131        let mut stats = self.batch_stats.write().await;
132        stats.total_batches += 1;
133        stats.total_events += batch_size as u64;
134
135        // Calculate moving averages
136        let new_avg_batch_size = (stats.avg_batch_size + batch_size as f64) / 2.0;
137        let new_avg_latency = (stats.avg_latency_ms + latency.as_millis() as f64) / 2.0;
138
139        stats.avg_batch_size = new_avg_batch_size;
140        stats.avg_latency_ms = new_avg_latency;
141
142        if latency.as_secs_f64() > 0.0 {
143            stats.throughput_events_per_sec = batch_size as f64 / latency.as_secs_f64();
144        }
145
146        // Calculate efficiency score (higher is better)
147        let latency_efficiency = if new_avg_latency > 0.0 {
148            1.0 / new_avg_latency
149        } else {
150            1.0
151        };
152        stats.efficiency_score = new_avg_batch_size * latency_efficiency;
153
154        // Adaptive optimization: adjust batch size based on performance
155        if self.config.enable_batch_optimization {
156            let mut optimal_size = self.optimal_batch_size.write().await;
157
158            if new_avg_latency > self.target_latency.as_millis() as f64 && *optimal_size > 100 {
159                // Latency too high, reduce batch size
160                *optimal_size = (*optimal_size * 9) / 10;
161                debug!(
162                    "Reduced optimal batch size to {} due to high latency",
163                    *optimal_size
164                );
165            } else if new_avg_latency < self.target_latency.as_millis() as f64 / 2.0
166                && *optimal_size < 10000
167            {
168                // Latency is good, try increasing batch size
169                *optimal_size = (*optimal_size * 11) / 10;
170                debug!(
171                    "Increased optimal batch size to {} due to good latency",
172                    *optimal_size
173                );
174            }
175        }
176    }
177
178    /// Get current batching statistics
179    pub async fn get_stats(&self) -> BatchingStats {
180        self.batch_stats.read().await.clone()
181    }
182
183    /// Get current optimal batch size
184    pub async fn get_optimal_batch_size(&self) -> usize {
185        *self.optimal_batch_size.read().await
186    }
187}
188
189/// Intelligent memory pool for reducing allocation overhead
190pub struct IntelligentMemoryPool<T> {
191    pools: Arc<RwLock<HashMap<String, VecDeque<T>>>>,
192    max_pool_size: usize,
193    allocation_stats: Arc<RwLock<PoolStats>>,
194}
195
196/// Memory pool statistics
197#[derive(Debug, Clone, Default)]
198pub struct PoolStats {
199    pub total_allocations: u64,
200    pub pool_hits: u64,
201    pub pool_misses: u64,
202    pub hit_rate: f64,
203    pub memory_saved_bytes: u64,
204}
205
206impl<T> IntelligentMemoryPool<T> {
207    /// Create a new memory pool
208    pub fn new(max_pool_size: usize) -> Self {
209        Self {
210            pools: Arc::new(RwLock::new(HashMap::new())),
211            max_pool_size,
212            allocation_stats: Arc::new(RwLock::new(PoolStats::default())),
213        }
214    }
215
216    /// Get an object from the pool or create a new one
217    pub async fn get_or_create<F>(&self, pool_name: &str, factory: F) -> T
218    where
219        F: FnOnce() -> T,
220    {
221        let mut stats = self.allocation_stats.write().await;
222        stats.total_allocations += 1;
223
224        {
225            let mut pools = self.pools.write().await;
226            if let Some(pool) = pools.get_mut(pool_name) {
227                if let Some(obj) = pool.pop_front() {
228                    stats.pool_hits += 1;
229                    stats.hit_rate = (stats.pool_hits as f64) / (stats.total_allocations as f64);
230                    return obj;
231                }
232            }
233        }
234
235        // Pool miss - create new object
236        stats.pool_misses += 1;
237        stats.hit_rate = (stats.pool_hits as f64) / (stats.total_allocations as f64);
238
239        factory()
240    }
241
242    /// Return an object to the pool
243    pub async fn return_to_pool(&self, pool_name: &str, obj: T) {
244        let mut pools = self.pools.write().await;
245        let pool = pools
246            .entry(pool_name.to_string())
247            .or_insert_with(VecDeque::new);
248
249        if pool.len() < self.max_pool_size {
250            pool.push_back(obj);
251        }
252        // If pool is full, object is dropped (garbage collected)
253    }
254
255    /// Get pool statistics
256    pub async fn get_stats(&self) -> PoolStats {
257        self.allocation_stats.read().await.clone()
258    }
259
260    /// Clear all pools
261    pub async fn clear_all_pools(&self) {
262        let mut pools = self.pools.write().await;
263        pools.clear();
264
265        let mut stats = self.allocation_stats.write().await;
266        *stats = PoolStats::default();
267    }
268}
269
270/// Adaptive rate limiter with intelligent backpressure
271pub struct AdaptiveRateLimiter {
272    permits: Arc<Semaphore>,
273    config: Arc<RwLock<RateLimitConfig>>,
274    performance_history: Arc<RwLock<VecDeque<PerformanceSnapshot>>>,
275    last_adjustment: Arc<RwLock<Instant>>,
276}
277
278/// Rate limiting configuration
279#[derive(Debug, Clone)]
280pub struct RateLimitConfig {
281    pub max_requests_per_second: usize,
282    pub burst_capacity: usize,
283    pub adjustment_interval: Duration,
284    pub target_latency_ms: f64,
285    pub max_adjustment_factor: f64,
286}
287
288impl Default for RateLimitConfig {
289    fn default() -> Self {
290        Self {
291            max_requests_per_second: 10000,
292            burst_capacity: 1000,
293            adjustment_interval: Duration::from_secs(5),
294            target_latency_ms: 10.0,
295            max_adjustment_factor: 2.0,
296        }
297    }
298}
299
300/// Performance snapshot for rate limiting decisions
301#[derive(Debug, Clone)]
302struct PerformanceSnapshot {
303    timestamp: Instant,
304    latency_ms: f64,
305    throughput_rps: f64,
306    success_rate: f64,
307}
308
309impl AdaptiveRateLimiter {
310    /// Create a new adaptive rate limiter
311    pub fn new(config: RateLimitConfig) -> Self {
312        let permits = Arc::new(Semaphore::new(config.burst_capacity));
313
314        Self {
315            permits,
316            config: Arc::new(RwLock::new(config)),
317            performance_history: Arc::new(RwLock::new(VecDeque::new())),
318            last_adjustment: Arc::new(RwLock::new(Instant::now())),
319        }
320    }
321
322    /// Acquire a permit to proceed with operation
323    pub async fn acquire_permit(&self) -> Result<tokio::sync::SemaphorePermit<'_>> {
324        match self.permits.try_acquire() {
325            Ok(permit) => Ok(permit),
326            Err(_) => {
327                // Apply backpressure by waiting
328                warn!("Rate limit reached, applying backpressure");
329                self.permits
330                    .acquire()
331                    .await
332                    .map_err(|e| anyhow!("Failed to acquire permit: {}", e))
333            }
334        }
335    }
336
337    /// Record performance metrics for adaptive adjustment
338    pub async fn record_performance(&self, latency_ms: f64, success: bool) -> Result<()> {
339        let snapshot = PerformanceSnapshot {
340            timestamp: Instant::now(),
341            latency_ms,
342            throughput_rps: 0.0, // Will be calculated
343            success_rate: if success { 1.0 } else { 0.0 },
344        };
345
346        {
347            let mut history = self.performance_history.write().await;
348            history.push_back(snapshot);
349
350            // Keep only last 100 snapshots
351            if history.len() > 100 {
352                history.pop_front();
353            }
354        }
355
356        // Check if it's time to adjust rate limits
357        {
358            let last_adjustment = self.last_adjustment.read().await;
359            let config = self.config.read().await;
360
361            if last_adjustment.elapsed() >= config.adjustment_interval {
362                drop(last_adjustment);
363                drop(config);
364                self.adjust_rate_limits().await?;
365            }
366        }
367
368        Ok(())
369    }
370
371    /// Intelligently adjust rate limits based on performance
372    async fn adjust_rate_limits(&self) -> Result<()> {
373        let mut last_adjustment = self.last_adjustment.write().await;
374        *last_adjustment = Instant::now();
375        drop(last_adjustment);
376
377        let history = self.performance_history.read().await;
378        if history.len() < 10 {
379            return Ok(()); // Not enough data
380        }
381
382        // Calculate average performance metrics
383        let avg_latency: f64 =
384            history.iter().map(|s| s.latency_ms).sum::<f64>() / history.len() as f64;
385        let avg_success_rate: f64 =
386            history.iter().map(|s| s.success_rate).sum::<f64>() / history.len() as f64;
387
388        let mut config = self.config.write().await;
389        let current_rate = config.max_requests_per_second;
390
391        // Adjust based on latency and success rate
392        let adjustment_factor =
393            if avg_latency > config.target_latency_ms * 1.5 || avg_success_rate < 0.95 {
394                // Performance is poor, reduce rate
395                0.9
396            } else if avg_latency < config.target_latency_ms * 0.5 && avg_success_rate > 0.98 {
397                // Performance is good, increase rate
398                1.1
399            } else {
400                // Performance is acceptable, no change
401                1.0
402            };
403
404        if adjustment_factor != 1.0 {
405            let new_rate = ((current_rate as f64) * adjustment_factor) as usize;
406            let max_rate = ((current_rate as f64) * config.max_adjustment_factor) as usize;
407            let min_rate = ((current_rate as f64) / config.max_adjustment_factor) as usize;
408
409            config.max_requests_per_second = new_rate.clamp(min_rate, max_rate);
410
411            info!(
412                "Adjusted rate limit from {} to {} req/s (factor: {:.2}, avg_latency: {:.2}ms, success_rate: {:.2}%)",
413                current_rate, config.max_requests_per_second, adjustment_factor, avg_latency, avg_success_rate * 100.0
414            );
415
416            // Resize semaphore if needed
417            // Note: In a real implementation, you'd need a more sophisticated approach
418            // as Semaphore doesn't support dynamic resizing
419        }
420
421        Ok(())
422    }
423
424    /// Get current rate limit configuration
425    pub async fn get_config(&self) -> RateLimitConfig {
426        self.config.read().await.clone()
427    }
428}
429
430/// High-performance parallel processor for streaming events
431pub struct ParallelStreamProcessor {
432    config: PerformanceUtilsConfig,
433    worker_semaphore: Arc<Semaphore>,
434    processing_stats: Arc<RwLock<ProcessingStats>>,
435}
436
437/// Processing statistics
438#[derive(Debug, Clone, Default)]
439pub struct ProcessingStats {
440    pub events_processed: u64,
441    pub avg_processing_time_ms: f64,
442    pub peak_concurrency: usize,
443    pub current_concurrency: usize,
444    pub throughput_events_per_sec: f64,
445    pub cpu_efficiency: f64,
446}
447
448impl ParallelStreamProcessor {
449    /// Create a new parallel processor
450    pub fn new(config: PerformanceUtilsConfig) -> Self {
451        let worker_semaphore = Arc::new(Semaphore::new(config.cpu_cores * 2));
452
453        Self {
454            config,
455            worker_semaphore,
456            processing_stats: Arc::new(RwLock::new(ProcessingStats::default())),
457        }
458    }
459
460    /// Process events in parallel with optimal load balancing
461    pub async fn process_parallel<F, Fut>(
462        &self,
463        events: Vec<StreamEvent>,
464        processor: F,
465    ) -> Result<Vec<Result<()>>>
466    where
467        F: Fn(StreamEvent) -> Fut + Send + Sync + Clone + 'static,
468        Fut: std::future::Future<Output = Result<()>> + Send + 'static,
469    {
470        let start_time = Instant::now();
471        let event_count = events.len();
472
473        // Update concurrency tracking
474        {
475            let mut stats = self.processing_stats.write().await;
476            stats.current_concurrency = event_count.min(self.config.cpu_cores * 2);
477            stats.peak_concurrency = stats.peak_concurrency.max(stats.current_concurrency);
478        }
479
480        // Process events in parallel with controlled concurrency
481        let mut handles = Vec::new();
482
483        for event in events {
484            let permit = self.worker_semaphore.clone().acquire_owned().await?;
485            let processor_clone = processor.clone();
486
487            let handle = tokio::spawn(async move {
488                let _permit = permit; // Keep permit alive
489                let result = processor_clone(event).await;
490                yield_now().await; // Yield to prevent blocking
491                result
492            });
493
494            handles.push(handle);
495        }
496
497        // Collect results
498        let mut results = Vec::new();
499        for handle in handles {
500            match handle.await {
501                Ok(result) => results.push(result),
502                Err(e) => results.push(Err(anyhow!("Task join error: {}", e))),
503            }
504        }
505
506        // Update statistics
507        let processing_time = start_time.elapsed();
508        {
509            let mut stats = self.processing_stats.write().await;
510            stats.events_processed += event_count as u64;
511            stats.avg_processing_time_ms =
512                (stats.avg_processing_time_ms + processing_time.as_millis() as f64) / 2.0;
513            stats.current_concurrency = 0;
514
515            if processing_time.as_secs_f64() > 0.0 {
516                stats.throughput_events_per_sec =
517                    event_count as f64 / processing_time.as_secs_f64();
518            }
519
520            // Calculate CPU efficiency (higher is better)
521            let ideal_time = (event_count as f64) / (self.config.cpu_cores as f64);
522            let actual_time = processing_time.as_secs_f64();
523            stats.cpu_efficiency = if actual_time > 0.0 {
524                (ideal_time / actual_time).min(1.0)
525            } else {
526                1.0
527            };
528        }
529
530        debug!(
531            "Processed {} events in {:?} ({:.2} events/sec)",
532            event_count,
533            processing_time,
534            event_count as f64 / processing_time.as_secs_f64()
535        );
536
537        Ok(results)
538    }
539
540    /// Get current processing statistics
541    pub async fn get_stats(&self) -> ProcessingStats {
542        self.processing_stats.read().await.clone()
543    }
544}
545
546/// Intelligent prefetcher for streaming data
547pub struct IntelligentPrefetcher<T> {
548    cache: Arc<RwLock<HashMap<String, T>>>,
549    access_patterns: Arc<RwLock<HashMap<String, AccessPattern>>>,
550    max_cache_size: usize,
551}
552
553/// Access pattern tracking for intelligent prefetching
554#[derive(Debug, Clone)]
555struct AccessPattern {
556    access_count: u64,
557    last_access: Instant,
558    prediction_score: f64,
559    related_keys: Vec<String>,
560}
561
562impl<T: Clone> IntelligentPrefetcher<T> {
563    /// Create a new intelligent prefetcher
564    pub fn new(max_cache_size: usize) -> Self {
565        Self {
566            cache: Arc::new(RwLock::new(HashMap::new())),
567            access_patterns: Arc::new(RwLock::new(HashMap::new())),
568            max_cache_size,
569        }
570    }
571
572    /// Get data with intelligent prefetching
573    pub async fn get_with_prefetch<F, Fut>(&self, key: &str, loader: F) -> Result<T>
574    where
575        F: FnOnce(String) -> Fut + Send,
576        Fut: std::future::Future<Output = Result<T>> + Send,
577        T: Send + Sync,
578    {
579        // Check cache first
580        {
581            let cache = self.cache.read().await;
582            if let Some(data) = cache.get(key) {
583                self.update_access_pattern(key).await;
584                return Ok(data.clone());
585            }
586        }
587
588        // Load data
589        let data = loader(key.to_string()).await?;
590
591        // Store in cache
592        {
593            let mut cache = self.cache.write().await;
594            if cache.len() >= self.max_cache_size {
595                // Remove least recently used item
596                self.evict_lru().await;
597            }
598            cache.insert(key.to_string(), data.clone());
599        }
600
601        // Update access patterns and trigger prefetching
602        self.update_access_pattern(key).await;
603        self.trigger_intelligent_prefetch(key).await;
604
605        Ok(data)
606    }
607
608    /// Update access pattern for a key
609    async fn update_access_pattern(&self, key: &str) {
610        let mut patterns = self.access_patterns.write().await;
611        let pattern = patterns
612            .entry(key.to_string())
613            .or_insert_with(|| AccessPattern {
614                access_count: 0,
615                last_access: Instant::now(),
616                prediction_score: 0.0,
617                related_keys: Vec::new(),
618            });
619
620        pattern.access_count += 1;
621        pattern.last_access = Instant::now();
622
623        // Update prediction score based on access frequency and recency
624        let recency_factor = 1.0; // More recent = higher score
625        let frequency_factor = (pattern.access_count as f64).ln();
626        pattern.prediction_score = recency_factor * frequency_factor;
627    }
628
629    /// Trigger intelligent prefetching based on access patterns
630    async fn trigger_intelligent_prefetch(&self, accessed_key: &str) {
631        let patterns = self.access_patterns.read().await;
632
633        if let Some(pattern) = patterns.get(accessed_key) {
634            // Prefetch related keys with high prediction scores
635            for related_key in &pattern.related_keys {
636                if let Some(related_pattern) = patterns.get(related_key) {
637                    if related_pattern.prediction_score > 0.5 {
638                        // In a real implementation, you'd trigger async prefetching here
639                        debug!("Would prefetch related key: {}", related_key);
640                    }
641                }
642            }
643        }
644    }
645
646    /// Evict least recently used item from cache
647    async fn evict_lru(&self) {
648        let patterns = self.access_patterns.read().await;
649
650        if let Some((lru_key, _)) = patterns
651            .iter()
652            .min_by_key(|(_, pattern)| pattern.last_access)
653        {
654            let lru_key = lru_key.clone();
655            drop(patterns);
656
657            let mut cache = self.cache.write().await;
658            cache.remove(&lru_key);
659
660            let mut patterns = self.access_patterns.write().await;
661            patterns.remove(&lru_key);
662        }
663    }
664
665    /// Get cache statistics
666    pub async fn get_cache_stats(&self) -> (usize, usize) {
667        let cache_size = self.cache.read().await.len();
668        let pattern_count = self.access_patterns.read().await.len();
669        (cache_size, pattern_count)
670    }
671}
672
673#[cfg(test)]
674mod tests {
675    use super::*;
676    use tokio::time::{sleep, Duration};
677
678    #[tokio::test]
679    async fn test_adaptive_batcher() {
680        let config = PerformanceUtilsConfig::default();
681        let mut batcher = AdaptiveBatcher::new(config, Duration::from_millis(100));
682
683        let event = crate::event::StreamEvent::TripleAdded {
684            subject: "http://example.org/subject".to_string(),
685            predicate: "http://example.org/predicate".to_string(),
686            object: "\"test_object\"".to_string(),
687            graph: None,
688            metadata: crate::event::EventMetadata::default(),
689        };
690
691        // Add events and check batching behavior
692        for i in 0..10 {
693            let result = batcher.add_event(event.clone()).await.unwrap();
694            if i == 9 {
695                // Should not batch yet (batch size is 1000 by default)
696                assert!(result.is_none());
697            }
698        }
699
700        // Force flush
701        let batch = batcher.flush().await.unwrap();
702        assert!(batch.is_some());
703        assert_eq!(batch.unwrap().len(), 10);
704
705        let stats = batcher.get_stats().await;
706        assert_eq!(stats.total_batches, 1);
707        assert_eq!(stats.total_events, 10);
708    }
709
710    #[tokio::test]
711    async fn test_memory_pool() {
712        let pool: IntelligentMemoryPool<String> = IntelligentMemoryPool::new(10);
713
714        // Get object from pool (will create new)
715        let obj1 = pool
716            .get_or_create("test_pool", || "test_string".to_string())
717            .await;
718        assert_eq!(obj1, "test_string");
719
720        // Return object to pool
721        pool.return_to_pool("test_pool", obj1).await;
722
723        // Get object again (should come from pool)
724        let obj2 = pool
725            .get_or_create("test_pool", || "new_string".to_string())
726            .await;
727        assert_eq!(obj2, "test_string"); // Should be the pooled object
728
729        let stats = pool.get_stats().await;
730        assert_eq!(stats.pool_hits, 1);
731        assert_eq!(stats.total_allocations, 2);
732    }
733
734    #[tokio::test]
735    async fn test_adaptive_rate_limiter() {
736        let config = RateLimitConfig {
737            max_requests_per_second: 10,
738            burst_capacity: 5,
739            adjustment_interval: Duration::from_millis(100),
740            target_latency_ms: 10.0,
741            max_adjustment_factor: 2.0,
742        };
743
744        let limiter = AdaptiveRateLimiter::new(config);
745
746        // Acquire permits
747        for _ in 0..5 {
748            let _permit = limiter.acquire_permit().await.unwrap();
749            limiter.record_performance(5.0, true).await.unwrap(); // Good performance
750        }
751
752        sleep(Duration::from_millis(150)).await; // Wait for adjustment
753
754        let final_config = limiter.get_config().await;
755        // Rate might be adjusted based on good performance
756        assert!(final_config.max_requests_per_second >= 10);
757    }
758
759    #[tokio::test]
760    async fn test_parallel_processor() {
761        let config = PerformanceUtilsConfig::default();
762        let processor = ParallelStreamProcessor::new(config);
763
764        let events = vec![
765            crate::event::StreamEvent::TripleAdded {
766                subject: "http://example.org/subject1".to_string(),
767                predicate: "http://example.org/predicate".to_string(),
768                object: "\"test_object1\"".to_string(),
769                graph: None,
770                metadata: crate::event::EventMetadata::default(),
771            },
772            crate::event::StreamEvent::TripleAdded {
773                subject: "http://example.org/subject2".to_string(),
774                predicate: "http://example.org/predicate".to_string(),
775                object: "\"test_object2\"".to_string(),
776                graph: None,
777                metadata: crate::event::EventMetadata::default(),
778            },
779        ];
780
781        let results = processor
782            .process_parallel(events, |_event| async {
783                sleep(Duration::from_millis(10)).await;
784                Ok(())
785            })
786            .await
787            .unwrap();
788
789        assert_eq!(results.len(), 2);
790        assert!(results.iter().all(|r| r.is_ok()));
791
792        let stats = processor.get_stats().await;
793        assert_eq!(stats.events_processed, 2);
794    }
795
796    #[tokio::test]
797    async fn test_intelligent_prefetcher() {
798        let prefetcher: IntelligentPrefetcher<String> = IntelligentPrefetcher::new(10);
799
800        // Load data with prefetcher
801        let data1 = prefetcher
802            .get_with_prefetch("key1", |key| async move { Ok(format!("data_for_{key}")) })
803            .await
804            .unwrap();
805
806        assert_eq!(data1, "data_for_key1");
807
808        // Access same key (should come from cache)
809        let data2 = prefetcher
810            .get_with_prefetch("key1", |_key| async move {
811                Ok("should_not_be_called".to_string())
812            })
813            .await
814            .unwrap();
815
816        assert_eq!(data2, "data_for_key1");
817
818        let (cache_size, pattern_count) = prefetcher.get_cache_stats().await;
819        assert_eq!(cache_size, 1);
820        assert_eq!(pattern_count, 1);
821    }
822}