oxirs_arq/advanced_optimizer/
mod.rs1pub mod index_advisor;
8pub mod ml_predictor;
9pub mod ml_predictor_features;
10pub mod ml_predictor_model;
11#[cfg(test)]
12mod ml_predictor_tests;
13pub mod ml_predictor_training;
14pub mod model_manager;
15pub mod optimization_cache;
16pub mod streaming_analyzer;
17pub mod training_collector;
18
19pub use index_advisor::*;
20pub use ml_predictor::*;
21pub use model_manager::*;
22pub use optimization_cache::*;
23pub use streaming_analyzer::*;
24pub use training_collector::*;
25
26use std::sync::{Arc, Mutex, RwLock};
27
28use anyhow::Result;
29
30use crate::algebra::Algebra;
31use crate::cost_model::CostModel;
32use crate::statistics_collector::StatisticsCollector;
33
34pub struct AdvancedOptimizer {
36 config: AdvancedOptimizerConfig,
37 cost_model: Arc<Mutex<CostModel>>,
38 statistics: Arc<StatisticsCollector>,
39 index_advisor: IndexAdvisor,
40 streaming_analyzer: StreamingAnalyzer,
41 ml_predictor: Option<MLPredictor>,
42 training_collector: Option<Arc<RwLock<TrainingCollector>>>,
43 model_manager: Option<Arc<RwLock<ModelManager>>>,
44 optimization_cache: OptimizationCache,
45}
46
47#[derive(Debug, Clone)]
49pub struct AdvancedOptimizerConfig {
50 pub enable_ml_optimization: bool,
52 pub adaptive_index_selection: bool,
54 pub enable_streaming: bool,
56 pub max_memory_usage: usize,
58 pub cross_query_optimization: bool,
60 pub learning_rate: f64,
62 pub cache_size: usize,
64 pub parallel_optimization: bool,
66}
67
68impl Default for AdvancedOptimizerConfig {
69 fn default() -> Self {
70 Self {
71 enable_ml_optimization: true,
72 adaptive_index_selection: true,
73 enable_streaming: true,
74 max_memory_usage: 1024 * 1024 * 1024, cross_query_optimization: true,
76 learning_rate: 0.01,
77 cache_size: 10000,
78 parallel_optimization: true,
79 }
80 }
81}
82
83impl AdvancedOptimizer {
84 pub fn new(
86 config: AdvancedOptimizerConfig,
87 cost_model: Arc<Mutex<CostModel>>,
88 statistics: Arc<StatisticsCollector>,
89 ) -> Self {
90 let cache_config = CacheConfig {
91 max_plan_entries: config.cache_size,
92 max_decision_entries: config.cache_size * 2,
93 ..Default::default()
94 };
95
96 let ml_predictor = if config.enable_ml_optimization {
97 MLPredictor::from_model_type(MLModelType::LinearRegression).ok()
99 } else {
100 None
101 };
102
103 let streaming_config = StreamingConfig {
104 enable_streaming: config.enable_streaming,
105 memory_threshold_mb: config.max_memory_usage / (1024 * 1024),
106 spill_threshold_percent: 0.8,
107 streaming_batch_size: 1000,
108 };
109
110 Self {
111 index_advisor: IndexAdvisor::new(),
112 streaming_analyzer: StreamingAnalyzer::new(streaming_config),
113 ml_predictor,
114 training_collector: None,
115 model_manager: None,
116 optimization_cache: OptimizationCache::new(cache_config),
117 config,
118 cost_model,
119 statistics,
120 }
121 }
122
123 pub fn with_training_collector(mut self, collector: Arc<RwLock<TrainingCollector>>) -> Self {
125 self.training_collector = Some(collector);
126 self
127 }
128
129 pub fn with_model_manager(mut self, manager: Arc<RwLock<ModelManager>>) -> Self {
131 self.model_manager = Some(manager);
132 self
133 }
134
135 pub fn optimize(&mut self, algebra: Algebra) -> Result<Algebra> {
137 let query_hash = self.hash_algebra(&algebra);
139 if let Some(cached_plan) = self.optimization_cache.get_cached_plan(query_hash) {
140 return Ok(cached_plan.clone());
141 }
142
143 let mut optimized = algebra;
145
146 if let Some(ref mut ml_predictor) = self.ml_predictor {
148 if let Ok(prediction) = ml_predictor.predict_cost(&optimized) {
149 optimized = self.apply_ml_recommendations(optimized, prediction)?;
151 }
152 }
153
154 if self.config.adaptive_index_selection {
156 optimized = self.apply_index_recommendations(optimized)?;
157 }
158
159 if self.config.enable_streaming {
161 if let Ok(Some(strategy)) = self
162 .streaming_analyzer
163 .analyze_streaming_potential(&optimized)
164 {
165 optimized = self.apply_streaming_strategy(optimized, strategy)?;
166 }
167 }
168
169 let cost = self.estimate_cost(&optimized)?;
171 self.optimization_cache
172 .cache_plan(query_hash, optimized.clone(), cost);
173
174 Ok(optimized)
175 }
176
177 pub fn get_index_recommendations(&self) -> &[IndexRecommendation] {
179 self.index_advisor.get_recommendations()
180 }
181
182 pub fn get_cache_statistics(&self) -> &CacheStatistics {
184 self.optimization_cache.statistics()
185 }
186
187 fn hash_algebra(&self, _algebra: &Algebra) -> u64 {
188 0
190 }
191
192 fn apply_ml_recommendations(
193 &self,
194 algebra: Algebra,
195 _prediction: MLPrediction,
196 ) -> Result<Algebra> {
197 Ok(algebra)
199 }
200
201 fn apply_index_recommendations(&self, algebra: Algebra) -> Result<Algebra> {
202 Ok(algebra)
204 }
205
206 fn apply_streaming_strategy(
207 &self,
208 algebra: Algebra,
209 _strategy: StreamingStrategy,
210 ) -> Result<Algebra> {
211 Ok(algebra)
213 }
214
215 fn estimate_cost(&self, algebra: &Algebra) -> Result<f64> {
216 if let Some(ref model_manager) = self.model_manager {
218 if let Ok(manager) = model_manager.read() {
219 if manager.should_use_ml() {
220 if let Some(ref ml_predictor) = self.ml_predictor {
222 if let Ok(prediction) = ml_predictor.clone().predict_cost(algebra) {
223 return Ok(prediction.predicted_cost);
224 }
225 }
226 }
227 }
228 } else if let Some(ref ml_predictor) = self.ml_predictor {
229 if ml_predictor.should_use_ml() {
231 if let Ok(prediction) = ml_predictor.clone().predict_cost(algebra) {
232 if prediction.confidence >= 0.7 {
233 return Ok(prediction.predicted_cost);
234 }
235 }
236 }
237 }
238
239 let _cost_model = self
241 .cost_model
242 .lock()
243 .map_err(|e| anyhow::anyhow!("Failed to acquire cost model lock: {}", e))?;
244
245 Ok(self.heuristic_cost_estimate(algebra))
248 }
249
250 fn heuristic_cost_estimate(&self, _algebra: &Algebra) -> f64 {
252 100.0
254 }
255
256 pub fn record_execution(&mut self, algebra: &Algebra, actual_cost: f64) -> Result<()> {
258 if let Some(ref mut ml_predictor) = self.ml_predictor {
260 ml_predictor.update_from_execution(algebra, actual_cost)?;
261 }
262
263 if let Some(ref collector) = self.training_collector {
265 if let Ok(mut collector_guard) = collector.write() {
266 if let Some(ref ml_predictor) = self.ml_predictor {
268 let features = ml_predictor.extract_features(algebra);
269 let characteristics = QueryCharacteristics {
270 triple_pattern_count: 1, join_count: 0,
272 filter_count: 0,
273 optional_count: 0,
274 has_aggregation: false,
275 has_sorting: false,
276 estimated_cardinality: 100,
277 complexity_score: 1.0,
278 query_graph_diameter: 1,
279 avg_degree: 0.0,
280 max_degree: 0,
281 };
282
283 collector_guard.record_execution(
284 algebra,
285 features,
286 characteristics,
287 actual_cost,
288 )?;
289 }
290 }
291 }
292
293 if let Some(ref manager) = self.model_manager {
295 if let Ok(manager_guard) = manager.read() {
296 if let Some(ref ml_predictor) = self.ml_predictor {
297 if let Ok(prediction) = ml_predictor.clone().predict_cost(algebra) {
298 manager_guard.record_prediction(prediction.predicted_cost, actual_cost)?;
299 }
300 }
301 }
302 }
303
304 Ok(())
305 }
306
307 pub fn optimize_batch(&mut self, queries: Vec<Algebra>) -> Result<Vec<Algebra>> {
309 use rayon::prelude::*;
310
311 let mut cached_results = Vec::with_capacity(queries.len());
313 let mut uncached_queries = Vec::new();
314 let mut uncached_indices = Vec::new();
315
316 for (i, algebra) in queries.iter().enumerate() {
317 let query_hash = self.hash_algebra(algebra);
318 if let Some(cached_plan) = self.optimization_cache.get_cached_plan(query_hash) {
319 cached_results.push((i, cached_plan.clone()));
320 } else {
321 uncached_queries.push(algebra.clone());
322 uncached_indices.push(i);
323 }
324 }
325
326 let uncached_results: Result<Vec<_>> = uncached_queries
328 .into_par_iter()
329 .enumerate()
330 .map(|(idx, algebra)| {
331 let mut thread_optimizer = self.clone_for_thread();
333 let optimized = thread_optimizer.optimize_single_threaded(algebra)?;
334 Ok((uncached_indices[idx], optimized))
335 })
336 .collect();
337
338 let uncached_results = uncached_results?;
339
340 let mut final_results = vec![Algebra::Empty; queries.len()];
342 for (index, result) in cached_results.into_iter().chain(uncached_results) {
343 final_results[index] = result;
344 }
345
346 Ok(final_results)
347 }
348
349 pub fn optimize_with_workload_adaptation(
351 &mut self,
352 algebra: Algebra,
353 workload_context: WorkloadContext,
354 ) -> Result<Algebra> {
355 let adapted_config = self.adapt_config_for_workload(&workload_context);
357 let original_config = std::mem::replace(&mut self.config, adapted_config);
358
359 let result = self.optimize(algebra);
360
361 self.config = original_config;
363
364 result
365 }
366
367 pub fn get_performance_metrics(&self) -> OptimizerPerformanceMetrics {
369 OptimizerPerformanceMetrics {
370 cache_hit_ratio: self.optimization_cache.hit_ratio(),
371 total_optimizations: self.optimization_cache.total_requests(),
372 ml_predictions_made: self
373 .ml_predictor
374 .as_ref()
375 .map(|p| p.predictions_count())
376 .unwrap_or(0),
377 index_recommendations_generated: self.index_advisor.recommendations_count(),
378 streaming_optimizations_applied: self.streaming_analyzer.optimizations_count(),
379 }
380 }
381
382 fn clone_for_thread(&self) -> Self {
384 Self {
385 config: self.config.clone(),
386 cost_model: Arc::clone(&self.cost_model),
387 statistics: Arc::clone(&self.statistics),
388 index_advisor: self.index_advisor.clone(),
389 streaming_analyzer: self.streaming_analyzer.clone(),
390 ml_predictor: self.ml_predictor.clone(),
391 training_collector: self.training_collector.as_ref().map(Arc::clone),
392 model_manager: self.model_manager.as_ref().map(Arc::clone),
393 optimization_cache: self.optimization_cache.clone(),
394 }
395 }
396
397 fn optimize_single_threaded(&mut self, algebra: Algebra) -> Result<Algebra> {
399 let mut optimized = algebra;
401
402 if let Some(ref mut ml_predictor) = self.ml_predictor {
403 if let Ok(prediction) = ml_predictor.predict_cost(&optimized) {
404 optimized = self.apply_ml_recommendations(optimized, prediction)?;
405 }
406 }
407
408 if self.config.adaptive_index_selection {
409 optimized = self.apply_index_recommendations(optimized)?;
410 }
411
412 if self.config.enable_streaming {
413 if let Ok(Some(strategy)) = self
414 .streaming_analyzer
415 .analyze_streaming_potential(&optimized)
416 {
417 optimized = self.apply_streaming_strategy(optimized, strategy)?;
418 }
419 }
420
421 Ok(optimized)
422 }
423
424 fn adapt_config_for_workload(&self, workload: &WorkloadContext) -> AdvancedOptimizerConfig {
426 let mut config = self.config.clone();
427
428 if workload.query_complexity == QueryComplexity::High {
430 config.enable_ml_optimization = true;
431 config.max_memory_usage *= 2;
432 } else if workload.query_complexity == QueryComplexity::Low {
433 config.enable_ml_optimization = false;
434 config.cache_size /= 2;
435 }
436
437 match workload.workload_type {
439 WorkloadType::AnalyticalHeavy => {
440 config.enable_streaming = true;
441 config.adaptive_index_selection = true;
442 }
443 WorkloadType::TransactionalLight => {
444 config.cache_size *= 2;
445 config.cross_query_optimization = false;
446 }
447 WorkloadType::Mixed => {
448 }
450 }
451
452 config
453 }
454}
455
456#[derive(Debug, Clone)]
458pub struct WorkloadContext {
459 pub query_complexity: QueryComplexity,
460 pub workload_type: WorkloadType,
461 pub expected_data_size: DataSize,
462 pub concurrency_level: usize,
463}
464
465#[derive(Debug, Clone, PartialEq)]
467pub enum QueryComplexity {
468 Low,
469 Medium,
470 High,
471}
472
473#[derive(Debug, Clone, PartialEq)]
475pub enum WorkloadType {
476 AnalyticalHeavy,
477 TransactionalLight,
478 Mixed,
479}
480
481#[derive(Debug, Clone, PartialEq)]
483pub enum DataSize {
484 Small,
485 Medium,
486 Large,
487 ExtraLarge,
488}
489
490#[derive(Debug, Clone)]
492pub struct OptimizerPerformanceMetrics {
493 pub cache_hit_ratio: f64,
494 pub total_optimizations: usize,
495 pub ml_predictions_made: usize,
496 pub index_recommendations_generated: usize,
497 pub streaming_optimizations_applied: usize,
498}