1use crate::ast::{Document, OperationType};
7use anyhow::Result;
8use serde::{Deserialize, Serialize};
9use std::cmp::Reverse;
10use std::collections::{HashMap, VecDeque};
11use std::sync::{Arc, RwLock};
12use std::time::{Duration, Instant, SystemTime};
13
14#[derive(Debug, Clone, Serialize, Deserialize)]
16pub struct OperationMetrics {
17 pub operation_name: Option<String>,
18 pub operation_type: OperationType,
19 pub query_hash: u64,
20 pub execution_time: Duration,
21 pub parsing_time: Duration,
22 pub validation_time: Duration,
23 pub planning_time: Duration,
24 pub field_count: usize,
25 pub depth: usize,
26 pub complexity_score: usize,
27 pub cache_hit: bool,
28 pub error_count: usize,
29 pub timestamp: SystemTime,
30 pub client_info: ClientInfo,
31}
32
33#[derive(Debug, Clone, Serialize, Deserialize, Default)]
35pub struct ClientInfo {
36 pub ip_address: Option<String>,
37 pub user_agent: Option<String>,
38 pub user_id: Option<String>,
39 pub session_id: Option<String>,
40}
41
42#[derive(Debug, Clone, Serialize, Deserialize)]
44pub struct PerformanceStats {
45 pub total_requests: u64,
46 pub total_errors: u64,
47 pub avg_execution_time: Duration,
48 pub p50_execution_time: Duration,
49 pub p95_execution_time: Duration,
50 pub p99_execution_time: Duration,
51 pub cache_hit_ratio: f64,
52 pub queries_per_second: f64,
53 pub error_rate: f64,
54 pub most_expensive_queries: Vec<ExpensiveQuery>,
55 pub slowest_fields: Vec<SlowField>,
56 pub client_stats: HashMap<String, ClientStats>,
57}
58
59#[derive(Debug, Clone, Serialize, Deserialize)]
61pub struct ExpensiveQuery {
62 pub query_hash: u64,
63 pub operation_name: Option<String>,
64 pub avg_execution_time: Duration,
65 pub max_execution_time: Duration,
66 pub execution_count: u64,
67 pub complexity_score: usize,
68}
69
70#[derive(Debug, Clone, Serialize, Deserialize)]
72pub struct SlowField {
73 pub field_name: String,
74 pub avg_resolution_time: Duration,
75 pub max_resolution_time: Duration,
76 pub call_count: u64,
77}
78
79#[derive(Debug, Clone, Serialize, Deserialize)]
81pub struct ClientStats {
82 pub request_count: u64,
83 pub error_count: u64,
84 pub avg_execution_time: Duration,
85 pub last_seen: SystemTime,
86}
87
88#[derive(Debug)]
90pub struct PerformanceTracker {
91 metrics: Arc<RwLock<VecDeque<OperationMetrics>>>,
92 field_metrics: Arc<RwLock<HashMap<String, FieldMetrics>>>,
93 config: PerformanceConfig,
94 start_time: Instant,
95}
96
97#[derive(Debug, Clone)]
99pub struct PerformanceConfig {
100 pub max_stored_metrics: usize,
101 pub enable_detailed_tracking: bool,
102 pub enable_client_tracking: bool,
103 pub enable_field_tracking: bool,
104 pub stats_window: Duration,
105 pub expensive_query_threshold: Duration,
106 pub slow_field_threshold: Duration,
107}
108
109impl Default for PerformanceConfig {
110 fn default() -> Self {
111 Self {
112 max_stored_metrics: 10000,
113 enable_detailed_tracking: true,
114 enable_client_tracking: true,
115 enable_field_tracking: true,
116 stats_window: Duration::from_secs(3600), expensive_query_threshold: Duration::from_millis(1000),
118 slow_field_threshold: Duration::from_millis(100),
119 }
120 }
121}
122
123#[derive(Debug, Clone)]
125struct FieldMetrics {
126 call_count: u64,
127 total_time: Duration,
128 max_time: Duration,
129 error_count: u64,
130 last_called: Instant,
131}
132
133impl FieldMetrics {
134 fn new() -> Self {
135 Self {
136 call_count: 0,
137 total_time: Duration::from_secs(0),
138 max_time: Duration::from_secs(0),
139 error_count: 0,
140 last_called: Instant::now(),
141 }
142 }
143
144 fn record_call(&mut self, duration: Duration, had_error: bool) {
145 self.call_count += 1;
146 self.total_time += duration;
147 self.max_time = self.max_time.max(duration);
148 if had_error {
149 self.error_count += 1;
150 }
151 self.last_called = Instant::now();
152 }
153
154 fn avg_time(&self) -> Duration {
155 if self.call_count == 0 {
156 Duration::from_secs(0)
157 } else {
158 self.total_time / self.call_count as u32
159 }
160 }
161}
162
163impl PerformanceTracker {
164 pub fn new() -> Self {
166 Self::with_config(PerformanceConfig::default())
167 }
168
169 pub fn with_config(config: PerformanceConfig) -> Self {
171 Self {
172 metrics: Arc::new(RwLock::new(VecDeque::new())),
173 field_metrics: Arc::new(RwLock::new(HashMap::new())),
174 config,
175 start_time: Instant::now(),
176 }
177 }
178
179 pub fn record_operation(&self, metrics: OperationMetrics) {
181 if let Ok(mut storage) = self.metrics.write() {
182 while storage.len() >= self.config.max_stored_metrics {
184 storage.pop_front();
185 }
186 storage.push_back(metrics);
187 }
188 }
189
190 pub fn record_field_resolution(&self, field_name: &str, duration: Duration, had_error: bool) {
192 if !self.config.enable_field_tracking {
193 return;
194 }
195
196 if let Ok(mut field_metrics) = self.field_metrics.write() {
197 let metrics = field_metrics
198 .entry(field_name.to_string())
199 .or_insert_with(FieldMetrics::new);
200 metrics.record_call(duration, had_error);
201 }
202 }
203
204 pub fn get_stats(&self) -> Result<PerformanceStats> {
206 let metrics = self
207 .metrics
208 .read()
209 .map_err(|_| anyhow::anyhow!("Lock poisoned"))?;
210 let field_metrics = self
211 .field_metrics
212 .read()
213 .map_err(|_| anyhow::anyhow!("Lock poisoned"))?;
214
215 let cutoff_time = SystemTime::now() - self.config.stats_window;
216 let recent_metrics: Vec<_> = metrics
217 .iter()
218 .filter(|m| m.timestamp > cutoff_time)
219 .collect();
220
221 if recent_metrics.is_empty() {
222 return Ok(PerformanceStats {
223 total_requests: 0,
224 total_errors: 0,
225 avg_execution_time: Duration::from_secs(0),
226 p50_execution_time: Duration::from_secs(0),
227 p95_execution_time: Duration::from_secs(0),
228 p99_execution_time: Duration::from_secs(0),
229 cache_hit_ratio: 0.0,
230 queries_per_second: 0.0,
231 error_rate: 0.0,
232 most_expensive_queries: Vec::new(),
233 slowest_fields: Vec::new(),
234 client_stats: HashMap::new(),
235 });
236 }
237
238 let total_requests = recent_metrics.len() as u64;
240 let total_errors = recent_metrics.iter().map(|m| m.error_count as u64).sum();
241 let cache_hits = recent_metrics.iter().filter(|m| m.cache_hit).count() as u64;
242
243 let mut execution_times: Vec<Duration> =
245 recent_metrics.iter().map(|m| m.execution_time).collect();
246 execution_times.sort();
247
248 let total_nanos = execution_times.iter().map(|d| d.as_nanos()).sum::<u128>()
249 / execution_times.len() as u128;
250 let avg_execution_time = Duration::from_nanos(total_nanos.min(u64::MAX as u128) as u64);
251
252 let p50_execution_time = execution_times[execution_times.len() * 50 / 100];
253 let p95_execution_time = execution_times[execution_times.len() * 95 / 100];
254 let p99_execution_time = execution_times[execution_times.len() * 99 / 100];
255
256 let cache_hit_ratio = if total_requests > 0 {
257 cache_hits as f64 / total_requests as f64
258 } else {
259 0.0
260 };
261
262 let queries_per_second = total_requests as f64 / self.config.stats_window.as_secs() as f64;
263 let error_rate = if total_requests > 0 {
264 total_errors as f64 / total_requests as f64
265 } else {
266 0.0
267 };
268
269 let most_expensive_queries = self.calculate_expensive_queries(&recent_metrics);
271
272 let slowest_fields = self.calculate_slowest_fields(&field_metrics);
274
275 let client_stats = if self.config.enable_client_tracking {
277 self.calculate_client_stats(&recent_metrics)
278 } else {
279 HashMap::new()
280 };
281
282 Ok(PerformanceStats {
283 total_requests,
284 total_errors,
285 avg_execution_time,
286 p50_execution_time,
287 p95_execution_time,
288 p99_execution_time,
289 cache_hit_ratio,
290 queries_per_second,
291 error_rate,
292 most_expensive_queries,
293 slowest_fields,
294 client_stats,
295 })
296 }
297
298 pub fn clear_metrics(&self) {
300 if let Ok(mut metrics) = self.metrics.write() {
301 metrics.clear();
302 }
303 if let Ok(mut field_metrics) = self.field_metrics.write() {
304 field_metrics.clear();
305 }
306 }
307
308 pub fn get_metrics_in_range(
310 &self,
311 start: SystemTime,
312 end: SystemTime,
313 ) -> Result<Vec<OperationMetrics>> {
314 let metrics = self
315 .metrics
316 .read()
317 .map_err(|_| anyhow::anyhow!("Lock poisoned"))?;
318
319 Ok(metrics
320 .iter()
321 .filter(|m| m.timestamp >= start && m.timestamp <= end)
322 .cloned()
323 .collect())
324 }
325
326 pub fn export_metrics_json(&self) -> Result<String> {
328 let stats = self.get_stats()?;
329 serde_json::to_string_pretty(&stats)
330 .map_err(|e| anyhow::anyhow!("JSON serialization failed: {}", e))
331 }
332
333 pub fn uptime(&self) -> Duration {
335 self.start_time.elapsed()
336 }
337
338 fn calculate_expensive_queries(&self, metrics: &[&OperationMetrics]) -> Vec<ExpensiveQuery> {
339 let mut query_stats: HashMap<u64, (Duration, Duration, u64, usize, Option<String>)> =
340 HashMap::new();
341
342 for metric in metrics {
343 if metric.execution_time >= self.config.expensive_query_threshold {
344 let entry = query_stats.entry(metric.query_hash).or_insert((
345 Duration::from_secs(0),
346 Duration::from_secs(0),
347 0,
348 metric.complexity_score,
349 metric.operation_name.clone(),
350 ));
351
352 entry.0 += metric.execution_time; entry.1 = entry.1.max(metric.execution_time); entry.2 += 1; }
356 }
357
358 let mut expensive_queries: Vec<ExpensiveQuery> = query_stats
359 .into_iter()
360 .map(
361 |(hash, (total_time, max_time, count, complexity, name))| ExpensiveQuery {
362 query_hash: hash,
363 operation_name: name,
364 avg_execution_time: total_time / count as u32,
365 max_execution_time: max_time,
366 execution_count: count,
367 complexity_score: complexity,
368 },
369 )
370 .collect();
371
372 expensive_queries.sort_by_key(|q| Reverse(q.avg_execution_time));
373 expensive_queries.truncate(10); expensive_queries
375 }
376
377 fn calculate_slowest_fields(
378 &self,
379 field_metrics: &HashMap<String, FieldMetrics>,
380 ) -> Vec<SlowField> {
381 let mut slow_fields: Vec<SlowField> = field_metrics
382 .iter()
383 .filter(|(_, metrics)| metrics.avg_time() >= self.config.slow_field_threshold)
384 .map(|(name, metrics)| SlowField {
385 field_name: name.clone(),
386 avg_resolution_time: metrics.avg_time(),
387 max_resolution_time: metrics.max_time,
388 call_count: metrics.call_count,
389 })
390 .collect();
391
392 slow_fields.sort_by_key(|f| Reverse(f.avg_resolution_time));
393 slow_fields.truncate(10); slow_fields
395 }
396
397 fn calculate_client_stats(
398 &self,
399 metrics: &[&OperationMetrics],
400 ) -> HashMap<String, ClientStats> {
401 let mut client_stats: HashMap<String, ClientStats> = HashMap::new();
402
403 for metric in metrics {
404 if let Some(ref ip) = metric.client_info.ip_address {
405 let stats = client_stats.entry(ip.clone()).or_insert(ClientStats {
406 request_count: 0,
407 error_count: 0,
408 avg_execution_time: Duration::from_secs(0),
409 last_seen: metric.timestamp,
410 });
411
412 stats.request_count += 1;
413 stats.error_count += metric.error_count as u64;
414 stats.last_seen = stats.last_seen.max(metric.timestamp);
415
416 let total_time = stats.avg_execution_time * (stats.request_count - 1) as u32
418 + metric.execution_time;
419 stats.avg_execution_time = total_time / stats.request_count as u32;
420 }
421 }
422
423 client_stats
424 }
425}
426
427impl Default for PerformanceTracker {
428 fn default() -> Self {
429 Self::new()
430 }
431}
432
433pub struct PerformanceMonitor {
435 tracker: Arc<PerformanceTracker>,
436}
437
438impl PerformanceMonitor {
439 pub fn new(tracker: Arc<PerformanceTracker>) -> Self {
440 Self { tracker }
441 }
442
443 pub fn start_operation(&self) -> OperationTimer {
445 OperationTimer::new(Arc::clone(&self.tracker))
446 }
447
448 pub fn get_stats(&self) -> Result<PerformanceStats> {
450 self.tracker.get_stats()
451 }
452
453 pub fn tracker(&self) -> &Arc<PerformanceTracker> {
455 &self.tracker
456 }
457}
458
459pub struct OperationTimer {
461 tracker: Arc<PerformanceTracker>,
462 start_time: Instant,
463 parsing_time: Option<Duration>,
464 validation_time: Option<Duration>,
465 planning_time: Option<Duration>,
466}
467
468impl OperationTimer {
469 fn new(tracker: Arc<PerformanceTracker>) -> Self {
470 Self {
471 tracker,
472 start_time: Instant::now(),
473 parsing_time: None,
474 validation_time: None,
475 planning_time: None,
476 }
477 }
478
479 pub fn mark_parsing_complete(&mut self) {
481 self.parsing_time = Some(self.start_time.elapsed());
482 }
483
484 pub fn mark_validation_complete(&mut self) {
486 self.validation_time =
487 Some(self.start_time.elapsed() - self.parsing_time.unwrap_or_default());
488 }
489
490 pub fn mark_planning_complete(&mut self) {
492 let elapsed = self.start_time.elapsed();
493 let previous =
494 self.parsing_time.unwrap_or_default() + self.validation_time.unwrap_or_default();
495 self.planning_time = Some(elapsed - previous);
496 }
497
498 pub fn complete(
500 &self,
501 document: &Document,
502 complexity: crate::optimizer::QueryComplexity,
503 cache_hit: bool,
504 error_count: usize,
505 client_info: ClientInfo,
506 ) {
507 let total_execution_time = self.start_time.elapsed();
508
509 let (operation_name, operation_type) =
511 if let Some(crate::ast::Definition::Operation(op)) = document.definitions.first() {
512 (op.name.clone(), op.operation_type.clone())
513 } else {
514 (None, OperationType::Query)
515 };
516
517 let metrics = OperationMetrics {
518 operation_name,
519 operation_type,
520 query_hash: self.calculate_query_hash(document),
521 execution_time: total_execution_time,
522 parsing_time: self.parsing_time.unwrap_or_default(),
523 validation_time: self.validation_time.unwrap_or_default(),
524 planning_time: self.planning_time.unwrap_or_default(),
525 field_count: complexity.field_count,
526 depth: complexity.depth,
527 complexity_score: complexity.complexity_score,
528 cache_hit,
529 error_count,
530 timestamp: SystemTime::now(),
531 client_info,
532 };
533
534 self.tracker.record_operation(metrics);
535 }
536
537 fn calculate_query_hash(&self, document: &Document) -> u64 {
538 use std::collections::hash_map::DefaultHasher;
539 use std::hash::{Hash, Hasher};
540
541 let mut hasher = DefaultHasher::new();
542 format!("{document:?}").hash(&mut hasher);
543 hasher.finish()
544 }
545}
546
547#[cfg(test)]
548mod tests {
549 use super::*;
550 use crate::ast::*;
551
552 #[test]
553 fn test_performance_tracker() {
554 let tracker = PerformanceTracker::new();
555
556 let metrics = OperationMetrics {
557 operation_name: Some("TestQuery".to_string()),
558 operation_type: OperationType::Query,
559 query_hash: 12345,
560 execution_time: Duration::from_millis(100),
561 parsing_time: Duration::from_millis(10),
562 validation_time: Duration::from_millis(5),
563 planning_time: Duration::from_millis(15),
564 field_count: 5,
565 depth: 3,
566 complexity_score: 50,
567 cache_hit: false,
568 error_count: 0,
569 timestamp: SystemTime::now(),
570 client_info: ClientInfo::default(),
571 };
572
573 tracker.record_operation(metrics);
574
575 let stats = tracker.get_stats().expect("should succeed");
576 assert_eq!(stats.total_requests, 1);
577 assert_eq!(stats.total_errors, 0);
578 }
579
580 #[test]
581 fn test_field_metrics() {
582 let tracker = PerformanceTracker::new();
583
584 let operation_metrics = OperationMetrics {
586 operation_name: Some("test_operation".to_string()),
587 operation_type: OperationType::Query,
588 query_hash: 12345,
589 execution_time: Duration::from_millis(100),
590 parsing_time: Duration::from_millis(10),
591 validation_time: Duration::from_millis(5),
592 planning_time: Duration::from_millis(5),
593 field_count: 1,
594 depth: 1,
595 complexity_score: 10,
596 cache_hit: false,
597 error_count: 0,
598 timestamp: SystemTime::now(),
599 client_info: ClientInfo::default(),
600 };
601 tracker.record_operation(operation_metrics);
602
603 tracker.record_field_resolution("test_field", Duration::from_millis(150), false);
604 tracker.record_field_resolution("test_field", Duration::from_millis(200), true);
605
606 let stats = tracker.get_stats().expect("should succeed");
607 assert_eq!(stats.slowest_fields.len(), 1);
608 assert_eq!(stats.slowest_fields[0].field_name, "test_field");
609 assert_eq!(stats.slowest_fields[0].call_count, 2);
610 }
611
612 #[test]
613 fn test_operation_timer() {
614 let tracker = Arc::new(PerformanceTracker::new());
615 let monitor = PerformanceMonitor::new(tracker);
616
617 let mut timer = monitor.start_operation();
618 std::thread::sleep(Duration::from_millis(1));
619 timer.mark_parsing_complete();
620
621 assert!(timer.parsing_time.is_some());
622 assert!(timer.parsing_time.expect("should succeed") > Duration::from_millis(0));
623 }
624}