use std::collections::{BTreeMap, HashMap};
use std::hash::Hash;
use std::time::SystemTime;
use std::time::{Duration, Instant};
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub enum PerformanceEventType {
FunctionCall,
MemoryAllocation,
GcEvent,
AsyncOperation,
IoOperation,
CpuIntensive,
}
#[derive(Debug, Clone)]
pub struct PerformanceEvent {
pub event_type: PerformanceEventType,
pub importance: f64,
pub timestamp: u64, pub metadata: Option<String>,
}
#[derive(Debug, Clone)]
pub struct SamplingConfig {
pub base_sample_rate: f64,
pub enable_dynamic_sampling: bool,
pub min_sample_interval: Duration,
pub max_sample_rate: f64,
pub system_load_threshold: f64,
pub importance_threshold: f64,
}
impl Default for SamplingConfig {
fn default() -> Self {
Self {
base_sample_rate: 0.1, enable_dynamic_sampling: true,
min_sample_interval: Duration::from_millis(1),
max_sample_rate: 1.0,
system_load_threshold: 0.8,
importance_threshold: 0.5,
}
}
}
#[derive(Debug)]
pub struct SamplingStrategy {
config: SamplingConfig,
current_sample_rate: f64,
last_sample_time: Option<Instant>,
estimated_system_load: f64,
stats: SamplingStats,
}
#[derive(Debug, Clone, Default)]
pub struct SamplingStats {
pub total_events: u64,
pub sampled_events: u64,
pub skipped_events: u64,
pub rate_adjustments: u64,
}
#[derive(Debug, Clone)]
pub struct SamplingDecision {
pub should_sample: bool,
pub sample_rate: f64,
pub skip_reason: Option<String>,
}
impl SamplingStrategy {
pub fn new(config: SamplingConfig) -> Self {
Self {
current_sample_rate: config.base_sample_rate,
last_sample_time: None,
estimated_system_load: 0.5,
stats: SamplingStats::default(),
config,
}
}
pub fn with_default_config() -> Self {
Self::new(SamplingConfig::default())
}
pub fn should_sample_event(&mut self, event: &PerformanceEvent) -> SamplingDecision {
self.stats.total_events += 1;
if let Some(last_time) = self.last_sample_time {
if last_time.elapsed() < self.config.min_sample_interval {
self.stats.skipped_events += 1;
return SamplingDecision {
should_sample: false,
sample_rate: self.current_sample_rate,
skip_reason: Some("Minimum interval not met".to_string()),
};
}
}
if event.importance < self.config.importance_threshold {
self.stats.skipped_events += 1;
return SamplingDecision {
should_sample: false,
sample_rate: self.current_sample_rate,
skip_reason: Some("Below importance threshold".to_string()),
};
}
let should_sample: _ = self.is_sample_accepted(event);
if should_sample {
self.stats.sampled_events += 1;
self.last_sample_time = Some(Instant::now());
} else {
self.stats.skipped_events += 1;
}
if self.config.enable_dynamic_sampling {
self.dynamic_adjust_rate();
}
SamplingDecision {
should_sample,
sample_rate: self.current_sample_rate,
skip_reason: if should_sample {
None
} else {
Some("Random sampling decision".to_string())
},
}
}
fn is_sample_accepted(&self, event: &PerformanceEvent) -> bool {
let adjusted_rate: _ = self.current_sample_rate * (0.5 + 0.5 * event.importance);
let random_value: _ = ((std::time::SystemTime::now()
.duration_since(std::time::UNIX_EPOCH)
.unwrap()
.as_secs()
- event.timestamp)
.max(1) as f64)
/ 1_000_000_000.0;
random_value <= adjusted_rate
}
fn dynamic_adjust_rate(&mut self) {
self.estimated_system_load = 0.5 + 0.3 * self.stats.total_events as f64 / 1000.0;
let old_rate: _ = self.current_sample_rate;
if self.estimated_system_load > self.config.system_load_threshold {
self.current_sample_rate *= 0.95;
} else if self.estimated_system_load < 0.3 {
self.current_sample_rate *= 1.05;
}
self.current_sample_rate = self
.current_sample_rate
.max(0.01)
.min(self.config.max_sample_rate);
if (self.current_sample_rate - old_rate).abs() > 0.001 {
self.stats.rate_adjustments += 1;
}
}
pub fn get_current_sample_rate(&self) -> f64 {
self.current_sample_rate
}
pub fn get_stats(&self) -> &SamplingStats {
&self.stats
}
pub fn reset_stats(&mut self) {
self.stats = SamplingStats::default();
}
pub fn set_sample_rate(&mut self, rate: f64) {
self.current_sample_rate = rate.max(0.0).min(1.0);
}
pub fn force_sample(&mut self) -> SamplingDecision {
self.stats.sampled_events += 1;
self.last_sample_time = Some(Instant::now());
SamplingDecision {
should_sample: true,
sample_rate: self.current_sample_rate,
skip_reason: None,
}
}
pub fn get_sample_rate_percentage(&self) -> f64 {
self.current_sample_rate * 100.0
}
pub fn get_sampling_efficiency(&self) -> f64 {
if self.stats.total_events == 0 {
return 0.0;
}
self.stats.sampled_events as f64 / self.stats.total_events as f64
}
}
#[cfg(test)]
mod tests {
#[test]
fn test_sampling_strategy_creation() {
let strategy: _ = SamplingStrategy::with_default_config();
assert_eq!(strategy.get_current_sample_rate(), 0.1);
assert!(strategy.get_stats().total_events == 0);
}
#[test]
fn test_should_sample_event() {
let config: _ = SamplingConfig {
base_sample_rate: 0.5,
enable_dynamic_sampling: false,
min_sample_interval: Duration::from_millis(10),
importance_threshold: 0.3,
..Default::default()
};
let mut strategy = SamplingStrategy::new(config);
let event: _ = PerformanceEvent {
event_type: PerformanceEventType::FunctionCall,
importance: 0.8,
timestamp: std::time::SystemTime::now()
.duration_since(std::time::UNIX_EPOCH)
.unwrap()
.as_secs(),
metadata: None,
};
let decision: _ = strategy.should_sample_event(&event);
assert!(decision.should_sample || !decision.should_sample); }
#[test]
fn test_importance_threshold() {
let config: _ = SamplingConfig {
importance_threshold: 0.7,
..Default::default()
};
let mut strategy = SamplingStrategy::new(config);
let low_importance_event: _ = PerformanceEvent {
event_type: PerformanceEventType::FunctionCall,
importance: 0.5,
timestamp: std::time::SystemTime::now()
.duration_since(std::time::UNIX_EPOCH)
.unwrap()
.as_secs(),
metadata: None,
};
let decision: _ = strategy.should_sample_event(&low_importance_event);
assert!(!decision.should_sample);
assert!(decision.skip_reason.is_some());
}
#[test]
fn test_dynamic_rate_adjustment() {
let config: _ = SamplingConfig {
enable_dynamic_sampling: true,
base_sample_rate: 0.5,
..Default::default()
};
let mut strategy = SamplingStrategy::new(config);
let initial_rate: _ = strategy.get_current_sample_rate();
for _ in 0..100 {
let event: _ = PerformanceEvent {
event_type: PerformanceEventType::FunctionCall,
importance: 0.5,
timestamp: std::time::SystemTime::now()
.duration_since(std::time::UNIX_EPOCH)
.unwrap()
.as_secs(),
metadata: None,
};
strategy.should_sample_event(&event);
}
let final_rate: _ = strategy.get_current_sample_rate();
assert!(strategy.get_stats().rate_adjustments > 0);
}
#[test]
fn test_force_sample() {
let mut strategy = SamplingStrategy::with_default_config();
let decision: _ = strategy.force_sample();
assert!(decision.should_sample);
let stats: _ = strategy.get_stats();
assert_eq!(stats.sampled_events, 1);
}
#[test]
fn test_sample_rate_limits() {
let config: _ = SamplingConfig {
base_sample_rate: 0.0,
..Default::default()
};
let mut strategy = SamplingStrategy::new(config);
assert_eq!(strategy.get_current_sample_rate(), 0.0);
strategy.set_sample_rate(1.5);
assert_eq!(strategy.get_current_sample_rate(), 1.0);
strategy.set_sample_rate(-0.5);
assert_eq!(strategy.get_current_sample_rate(), 0.0);
}
#[test]
fn test_sampling_efficiency() {
let mut strategy = SamplingStrategy::with_default_config();
strategy.force_sample();
let efficiency: _ = strategy.get_sampling_efficiency();
assert!(efficiency > 0.0);
}
#[test]
fn test_sample_rate_percentage() {
let strategy: _ = SamplingStrategy::with_default_config();
let percentage: _ = strategy.get_sample_rate_percentage();
assert_eq!(percentage, 10.0); }
}