1use anyhow::{anyhow, Context, Result};
7use async_trait::async_trait;
8use serde::{Deserialize, Serialize};
9use std::collections::hash_map::DefaultHasher;
10use std::collections::{BTreeMap, HashMap};
11use std::hash::{Hash, Hasher};
12use std::sync::Arc;
13use std::time::{Duration, Instant};
14use tokio::sync::{Mutex, RwLock};
15use tracing::{debug, info, warn};
16
17use super::config::{FederationConfig, RemoteEndpoint, RetryStrategy};
18use super::query_planner::{QueryPlan, QueryPlanner, QueryStep};
19use super::real_time_sync::{RealTimeSchemaSynchronizer, SyncConfig};
20use super::schema_stitcher::SchemaStitcher;
21use super::service_discovery::{
22 DiscoveryEvent, HealthStatus, ServiceDiscovery, ServiceDiscoveryConfig,
23 ServiceDiscoveryEventHandler, ServiceInfo,
24};
25use crate::ast::{Document, OperationType, Value};
26use crate::types::Schema;
27
28#[derive(Debug, Clone, Default)]
30pub struct EnhancedFederationConfig {
31 pub service_discovery: ServiceDiscoveryConfig,
32 pub load_balancing: LoadBalancingConfig,
33 pub circuit_breaker: CircuitBreakerConfig,
34 pub retry_policy: RetryPolicyConfig,
35 pub query_routing: QueryRoutingConfig,
36 pub caching: FederationCacheConfig,
37 pub real_time_sync: SyncConfig,
38}
39
40#[derive(Debug, Clone)]
42pub struct LoadBalancingConfig {
43 pub algorithm: LoadBalancingAlgorithm,
44 pub health_check_weight: f64,
45 pub response_time_weight: f64,
46 pub load_weight: f64,
47 pub max_requests_per_service: usize,
48}
49
50impl Default for LoadBalancingConfig {
51 fn default() -> Self {
52 Self {
53 algorithm: LoadBalancingAlgorithm::WeightedRoundRobin,
54 health_check_weight: 0.4,
55 response_time_weight: 0.3,
56 load_weight: 0.3,
57 max_requests_per_service: 1000,
58 }
59 }
60}
61
62#[derive(Debug, Clone)]
64pub enum LoadBalancingAlgorithm {
65 RoundRobin,
66 WeightedRoundRobin,
67 LeastConnections,
68 LeastResponseTime,
69 Adaptive,
70 ConsistentHashing,
71}
72
73#[derive(Debug, Clone)]
75pub struct CircuitBreakerConfig {
76 pub failure_threshold: usize,
77 pub success_threshold: usize,
78 pub timeout: Duration,
79 pub retry_after: Duration,
80}
81
82impl Default for CircuitBreakerConfig {
83 fn default() -> Self {
84 Self {
85 failure_threshold: 5,
86 success_threshold: 3,
87 timeout: Duration::from_secs(30),
88 retry_after: Duration::from_secs(60),
89 }
90 }
91}
92
93#[derive(Debug, Clone)]
95pub struct RetryPolicyConfig {
96 pub max_retries: usize,
97 pub base_delay: Duration,
98 pub max_delay: Duration,
99 pub backoff_multiplier: f64,
100 pub jitter: bool,
101}
102
103impl Default for RetryPolicyConfig {
104 fn default() -> Self {
105 Self {
106 max_retries: 3,
107 base_delay: Duration::from_millis(100),
108 max_delay: Duration::from_secs(5),
109 backoff_multiplier: 2.0,
110 jitter: true,
111 }
112 }
113}
114
115#[derive(Debug, Clone)]
117pub struct QueryRoutingConfig {
118 pub enable_query_analysis: bool,
119 pub enable_field_based_routing: bool,
120 pub enable_cost_based_routing: bool,
121 pub parallel_execution: bool,
122 pub max_parallel_requests: usize,
123}
124
125impl Default for QueryRoutingConfig {
126 fn default() -> Self {
127 Self {
128 enable_query_analysis: true,
129 enable_field_based_routing: true,
130 enable_cost_based_routing: true,
131 parallel_execution: true,
132 max_parallel_requests: 10,
133 }
134 }
135}
136
137#[derive(Debug, Clone)]
139pub struct FederationCacheConfig {
140 pub enable_schema_caching: bool,
141 pub enable_query_caching: bool,
142 pub schema_cache_ttl: Duration,
143 pub query_cache_ttl: Duration,
144 pub max_cache_size: usize,
145}
146
147impl Default for FederationCacheConfig {
148 fn default() -> Self {
149 Self {
150 enable_schema_caching: true,
151 enable_query_caching: true,
152 schema_cache_ttl: Duration::from_secs(3600),
153 query_cache_ttl: Duration::from_secs(300),
154 max_cache_size: 1000,
155 }
156 }
157}
158
159#[derive(Debug, Clone, PartialEq)]
161pub enum CircuitBreakerState {
162 Closed,
163 Open,
164 HalfOpen,
165}
166
167#[derive(Debug)]
169pub struct ServiceCircuitBreaker {
170 state: CircuitBreakerState,
171 failure_count: usize,
172 success_count: usize,
173 last_failure_time: Option<Instant>,
174 config: CircuitBreakerConfig,
175}
176
177impl ServiceCircuitBreaker {
178 pub fn new(config: CircuitBreakerConfig) -> Self {
179 Self {
180 state: CircuitBreakerState::Closed,
181 failure_count: 0,
182 success_count: 0,
183 last_failure_time: None,
184 config,
185 }
186 }
187
188 pub fn can_execute(&mut self) -> bool {
189 match self.state {
190 CircuitBreakerState::Closed => true,
191 CircuitBreakerState::Open => {
192 if let Some(last_failure) = self.last_failure_time {
193 if last_failure.elapsed() >= self.config.retry_after {
194 self.state = CircuitBreakerState::HalfOpen;
195 self.success_count = 0;
196 true
197 } else {
198 false
199 }
200 } else {
201 false
202 }
203 }
204 CircuitBreakerState::HalfOpen => true,
205 }
206 }
207
208 pub fn record_success(&mut self) {
209 match self.state {
210 CircuitBreakerState::Closed => {
211 self.failure_count = 0;
212 }
213 CircuitBreakerState::HalfOpen => {
214 self.success_count += 1;
215 if self.success_count >= self.config.success_threshold {
216 self.state = CircuitBreakerState::Closed;
217 self.failure_count = 0;
218 }
219 }
220 CircuitBreakerState::Open => {}
221 }
222 }
223
224 pub fn record_failure(&mut self) {
225 self.failure_count += 1;
226 self.last_failure_time = Some(Instant::now());
227
228 match self.state {
229 CircuitBreakerState::Closed => {
230 if self.failure_count >= self.config.failure_threshold {
231 self.state = CircuitBreakerState::Open;
232 }
233 }
234 CircuitBreakerState::HalfOpen => {
235 self.state = CircuitBreakerState::Open;
236 }
237 CircuitBreakerState::Open => {}
238 }
239 }
240
241 pub fn state(&self) -> CircuitBreakerState {
242 self.state.clone()
243 }
244}
245
246#[derive(Debug, Clone)]
248pub struct FederationContext {
249 pub request_id: String,
250 pub user_id: Option<String>,
251 pub headers: HashMap<String, String>,
252 pub variables: HashMap<String, Value>,
253 pub operation_name: Option<String>,
254}
255
256#[derive(Debug, Clone)]
258pub struct QueryAnalytics {
259 pub query_patterns: HashMap<String, QueryPatternStats>,
260 pub service_performance: HashMap<String, ServicePerformanceStats>,
261 pub recent_queries: Vec<QueryExecutionStats>,
262 pub max_history: usize,
263}
264
265#[derive(Debug, Clone)]
266pub struct QueryPatternStats {
267 pub pattern_hash: String,
268 pub execution_count: u64,
269 pub average_duration: Duration,
270 pub success_rate: f64,
271 pub preferred_services: Vec<String>,
272 pub complexity_score: f64,
273}
274
275#[derive(Debug, Clone)]
276pub struct ServicePerformanceStats {
277 pub service_id: String,
278 pub total_requests: u64,
279 pub successful_requests: u64,
280 pub average_response_time: Duration,
281 pub error_rate: f64,
282 pub query_type_performance: HashMap<String, Duration>,
283 pub last_updated: Instant,
284}
285
286#[derive(Debug, Clone)]
287pub struct QueryExecutionStats {
288 pub query_hash: String,
289 pub service_id: String,
290 pub duration: Duration,
291 pub success: bool,
292 pub timestamp: Instant,
293 pub complexity: f64,
294 pub cache_hit: bool,
295}
296
297impl Default for QueryAnalytics {
298 fn default() -> Self {
299 Self::new()
300 }
301}
302
303impl QueryAnalytics {
304 pub fn new() -> Self {
305 Self {
306 query_patterns: HashMap::new(),
307 service_performance: HashMap::new(),
308 recent_queries: Vec::new(),
309 max_history: 10000,
310 }
311 }
312
313 pub fn record_execution(&mut self, stats: QueryExecutionStats) {
315 let pattern_stats = self
317 .query_patterns
318 .entry(stats.query_hash.clone())
319 .or_insert_with(|| QueryPatternStats {
320 pattern_hash: stats.query_hash.clone(),
321 execution_count: 0,
322 average_duration: Duration::from_millis(0),
323 success_rate: 1.0,
324 preferred_services: Vec::new(),
325 complexity_score: stats.complexity,
326 });
327
328 pattern_stats.execution_count += 1;
329 pattern_stats.average_duration = Duration::from_nanos(
330 ((pattern_stats.average_duration.as_nanos() as u64
331 * (pattern_stats.execution_count - 1))
332 + stats.duration.as_nanos() as u64)
333 / pattern_stats.execution_count,
334 );
335
336 if stats.success {
337 pattern_stats.success_rate =
338 (pattern_stats.success_rate * (pattern_stats.execution_count - 1) as f64 + 1.0)
339 / pattern_stats.execution_count as f64;
340 } else {
341 pattern_stats.success_rate = (pattern_stats.success_rate
342 * (pattern_stats.execution_count - 1) as f64)
343 / pattern_stats.execution_count as f64;
344 }
345
346 let service_stats = self
348 .service_performance
349 .entry(stats.service_id.clone())
350 .or_insert_with(|| ServicePerformanceStats {
351 service_id: stats.service_id.clone(),
352 total_requests: 0,
353 successful_requests: 0,
354 average_response_time: Duration::from_millis(0),
355 error_rate: 0.0,
356 query_type_performance: HashMap::new(),
357 last_updated: Instant::now(),
358 });
359
360 service_stats.total_requests += 1;
361 if stats.success {
362 service_stats.successful_requests += 1;
363 }
364
365 service_stats.average_response_time = Duration::from_nanos(
366 ((service_stats.average_response_time.as_nanos() as u64
367 * (service_stats.total_requests - 1))
368 + stats.duration.as_nanos() as u64)
369 / service_stats.total_requests,
370 );
371
372 service_stats.error_rate =
373 1.0 - (service_stats.successful_requests as f64 / service_stats.total_requests as f64);
374 service_stats.last_updated = Instant::now();
375
376 self.recent_queries.push(stats);
378 if self.recent_queries.len() > self.max_history {
379 self.recent_queries
380 .drain(0..self.recent_queries.len() - self.max_history);
381 }
382 }
383
384 pub fn get_recommended_service(&self, query_hash: &str) -> Option<String> {
386 if let Some(_pattern_stats) = self.query_patterns.get(query_hash) {
387 let mut best_service = None;
389 let mut best_score = f64::NEG_INFINITY;
390
391 for (service_id, service_stats) in &self.service_performance {
392 let success_weight = 0.4;
394 let response_time_weight = 0.4;
395 let error_rate_weight = 0.2;
396
397 let success_score = service_stats.successful_requests as f64
398 / service_stats.total_requests.max(1) as f64;
399 let response_time_score =
400 1.0 / (service_stats.average_response_time.as_millis().max(1) as f64);
401 let error_score = 1.0 - service_stats.error_rate;
402
403 let composite_score = (success_score * success_weight)
404 + (response_time_score * response_time_weight)
405 + (error_score * error_rate_weight);
406
407 if composite_score > best_score {
408 best_score = composite_score;
409 best_service = Some(service_id.clone());
410 }
411 }
412
413 best_service
414 } else {
415 None
416 }
417 }
418
419 pub fn get_performance_insights(&self) -> HashMap<String, serde_json::Value> {
421 let mut insights = HashMap::new();
422
423 insights.insert(
424 "total_query_patterns".to_string(),
425 serde_json::Value::Number(self.query_patterns.len().into()),
426 );
427
428 insights.insert(
429 "total_services".to_string(),
430 serde_json::Value::Number(self.service_performance.len().into()),
431 );
432
433 insights.insert(
434 "recent_queries_count".to_string(),
435 serde_json::Value::Number(self.recent_queries.len().into()),
436 );
437
438 let total_requests: u64 = self
440 .service_performance
441 .values()
442 .map(|s| s.total_requests)
443 .sum();
444
445 let total_successful: u64 = self
446 .service_performance
447 .values()
448 .map(|s| s.successful_requests)
449 .sum();
450
451 if total_requests > 0 {
452 insights.insert(
453 "overall_success_rate".to_string(),
454 serde_json::Value::Number(
455 serde_json::Number::from_f64(total_successful as f64 / total_requests as f64)
456 .unwrap_or_else(|| serde_json::Number::from(0)),
457 ),
458 );
459 }
460
461 insights
462 }
463}
464
465#[derive(Debug, Clone, Serialize, Deserialize)]
467pub struct FederationResult {
468 pub data: Option<serde_json::Value>,
469 pub errors: Vec<FederationError>,
470 pub extensions: HashMap<String, serde_json::Value>,
471}
472
473#[derive(Debug, Clone, Serialize, Deserialize)]
475pub struct FederationError {
476 pub message: String,
477 pub path: Option<Vec<String>>,
478 pub locations: Option<Vec<ErrorLocation>>,
479 pub extensions: HashMap<String, serde_json::Value>,
480}
481
482#[derive(Debug, Clone, Serialize, Deserialize)]
483pub struct ErrorLocation {
484 pub line: usize,
485 pub column: usize,
486}
487
488pub struct EnhancedFederationManager {
490 config: EnhancedFederationConfig,
491 service_discovery: Arc<ServiceDiscovery>,
492 schema_stitcher: Arc<SchemaStitcher>,
493 query_planner: Arc<QueryPlanner>,
494 circuit_breakers: Arc<RwLock<HashMap<String, ServiceCircuitBreaker>>>,
495 load_balancer: Arc<Mutex<LoadBalancer>>,
496 merged_schema: Arc<RwLock<Option<Schema>>>,
497 schema_synchronizer: Arc<RealTimeSchemaSynchronizer>,
498 query_analytics: Arc<Mutex<QueryAnalytics>>,
499 http_client: reqwest::Client,
500}
501
502#[derive(Debug)]
504pub struct LoadBalancer {
505 algorithm: LoadBalancingAlgorithm,
506 round_robin_counter: usize,
507 request_counts: HashMap<String, usize>,
508 config: LoadBalancingConfig,
509 hash_ring: BTreeMap<u64, String>,
510 virtual_nodes_per_service: usize,
511}
512
513impl LoadBalancer {
514 pub fn new(config: LoadBalancingConfig) -> Self {
515 Self {
516 algorithm: config.algorithm.clone(),
517 round_robin_counter: 0,
518 request_counts: HashMap::new(),
519 config,
520 hash_ring: BTreeMap::new(),
521 virtual_nodes_per_service: 150, }
523 }
524
525 pub fn update_hash_ring(&mut self, services: &[ServiceInfo]) {
527 self.hash_ring.clear();
528
529 for service in services {
530 for i in 0..self.virtual_nodes_per_service {
532 let virtual_key = format!("{}:{}", service.id, i);
533 let hash = self.hash_string(&virtual_key);
534 self.hash_ring.insert(hash, service.id.clone());
535 }
536 }
537 }
538
539 fn hash_string(&self, s: &str) -> u64 {
541 let mut hasher = DefaultHasher::new();
542 s.hash(&mut hasher);
543 hasher.finish()
544 }
545
546 pub fn select_service(
548 &mut self,
549 services: &[ServiceInfo],
550 _query_hash: Option<u64>,
551 ) -> Option<String> {
552 if services.is_empty() {
553 return None;
554 }
555
556 let healthy_services: Vec<_> = services
557 .iter()
558 .filter(|s| s.health_status == HealthStatus::Healthy)
559 .collect();
560
561 if healthy_services.is_empty() {
562 return None;
563 }
564
565 match self.algorithm {
566 LoadBalancingAlgorithm::RoundRobin => {
567 let service = &healthy_services[self.round_robin_counter % healthy_services.len()];
568 self.round_robin_counter += 1;
569 Some(service.id.clone())
570 }
571 LoadBalancingAlgorithm::WeightedRoundRobin => {
572 self.select_weighted_round_robin(&healthy_services)
573 }
574 LoadBalancingAlgorithm::LeastConnections => {
575 self.select_least_connections(&healthy_services)
576 }
577 LoadBalancingAlgorithm::LeastResponseTime => {
578 self.select_least_response_time(&healthy_services)
579 }
580 LoadBalancingAlgorithm::Adaptive => self.select_adaptive(&healthy_services),
581 LoadBalancingAlgorithm::ConsistentHashing => {
582 let healthy_service_infos: Vec<_> =
584 healthy_services.iter().map(|&s| s.clone()).collect();
585 self.update_hash_ring(&healthy_service_infos);
586 self.select_consistent_hash(&healthy_services, _query_hash.unwrap_or(0))
588 }
589 }
590 }
591
592 fn select_weighted_round_robin(&mut self, services: &[&ServiceInfo]) -> Option<String> {
593 let mut best_service = None;
595 let mut best_score = f64::INFINITY;
596
597 for service in services {
598 let health_score = match service.health_status {
599 HealthStatus::Healthy => 1.0,
600 HealthStatus::Degraded => 0.5,
601 _ => 0.0,
602 };
603
604 let response_time_score = service
605 .response_time
606 .map(|rt| rt.as_millis() as f64)
607 .unwrap_or(1000.0);
608
609 let load_score = service.load_factor;
610 let request_count = self.request_counts.get(&service.id).copied().unwrap_or(0) as f64;
611
612 let total_score = (health_score * self.config.health_check_weight)
613 + (response_time_score * self.config.response_time_weight)
614 + (load_score * self.config.load_weight)
615 + (request_count * 0.1);
616
617 if total_score < best_score {
618 best_score = total_score;
619 best_service = Some(service.id.clone());
620 }
621 }
622
623 if let Some(ref service_id) = best_service {
624 *self.request_counts.entry(service_id.clone()).or_insert(0) += 1;
625 }
626
627 best_service
628 }
629
630 fn select_least_connections(&self, services: &[&ServiceInfo]) -> Option<String> {
631 services
632 .iter()
633 .min_by_key(|service| self.request_counts.get(&service.id).copied().unwrap_or(0))
634 .map(|service| service.id.clone())
635 }
636
637 fn select_least_response_time(&self, services: &[&ServiceInfo]) -> Option<String> {
638 services
639 .iter()
640 .min_by_key(|service| service.response_time.unwrap_or(Duration::from_secs(10)))
641 .map(|service| service.id.clone())
642 }
643
644 fn select_adaptive(&mut self, services: &[&ServiceInfo]) -> Option<String> {
645 self.select_weighted_round_robin(services)
647 }
648
649 fn select_consistent_hash(&self, _services: &[&ServiceInfo], hash: u64) -> Option<String> {
650 if self.hash_ring.is_empty() {
651 return None;
652 }
653
654 if let Some((_, service_id)) = self.hash_ring.range(hash..).next() {
656 Some(service_id.clone())
657 } else {
658 self.hash_ring
660 .first_key_value()
661 .map(|(_, service_id)| service_id.clone())
662 }
663 }
664
665 pub fn record_completion(&mut self, service_id: &str) {
666 if let Some(count) = self.request_counts.get_mut(service_id) {
667 if *count > 0 {
668 *count -= 1;
669 }
670 }
671 }
672}
673
674impl EnhancedFederationManager {
675 pub async fn new(config: EnhancedFederationConfig, local_schema: Arc<Schema>) -> Result<Self> {
677 let service_discovery = Arc::new(ServiceDiscovery::new(config.service_discovery.clone()));
678 let schema_stitcher = Arc::new(SchemaStitcher::new(local_schema));
679 let federation_config = FederationConfig::default();
680 let query_planner = Arc::new(QueryPlanner::new(
681 schema_stitcher.clone(),
682 federation_config,
683 ));
684 let load_balancer = Arc::new(Mutex::new(LoadBalancer::new(config.load_balancing.clone())));
685
686 let http_client = reqwest::Client::builder()
687 .timeout(Duration::from_secs(30))
688 .build()
689 .context("Failed to create HTTP client")?;
690
691 let schema_synchronizer = Arc::new(RealTimeSchemaSynchronizer::new(
692 config.real_time_sync.clone(),
693 service_discovery.clone(),
694 schema_stitcher.clone(),
695 ));
696
697 let manager = Self {
698 config,
699 service_discovery,
700 schema_stitcher,
701 query_planner,
702 circuit_breakers: Arc::new(RwLock::new(HashMap::new())),
703 load_balancer,
704 merged_schema: Arc::new(RwLock::new(None)),
705 schema_synchronizer,
706 query_analytics: Arc::new(Mutex::new(QueryAnalytics::new())),
707 http_client,
708 };
709
710 manager.setup_event_handling().await?;
712
713 Ok(manager)
714 }
715
716 pub async fn start(&self) -> Result<()> {
718 info!("Starting enhanced federation manager");
719
720 self.service_discovery.start().await?;
722
723 self.schema_synchronizer.start().await?;
725
726 self.rebuild_schema().await?;
728
729 Ok(())
730 }
731
732 pub async fn execute_query(
734 &self,
735 document: &Document,
736 variables: HashMap<String, Value>,
737 context: FederationContext,
738 ) -> Result<FederationResult> {
739 let start_time = Instant::now();
740
741 debug!("Executing federated query: {}", context.request_id);
742
743 let query_hash = self.generate_query_hash(document, &variables);
745
746 let recommended_service = {
748 let analytics = self.query_analytics.lock().await;
749 analytics.get_recommended_service(&query_hash)
750 };
751
752 let schema = {
754 let schema_guard = self.merged_schema.read().await;
755 schema_guard
756 .clone()
757 .ok_or_else(|| anyhow!("No schema available"))?
758 };
759
760 let query_plan = self
762 .query_planner
763 .plan_query(document, &schema)
764 .await
765 .context("Failed to plan federated query")?;
766
767 let result = self
769 .execute_query_plan_with_analytics(
770 &query_plan,
771 &context,
772 &query_hash,
773 recommended_service,
774 )
775 .await;
776
777 let execution_time = start_time.elapsed();
778 let success = result.is_ok();
779
780 if let Ok(ref _plan_result) = result {
782 for step in &query_plan.steps {
784 let service_id = &step.endpoint_id;
785 let stats = QueryExecutionStats {
786 query_hash: query_hash.clone(),
787 service_id: service_id.clone(),
788 duration: execution_time,
789 success,
790 timestamp: start_time,
791 complexity: self.calculate_query_complexity(document),
792 cache_hit: false, };
794
795 let mut analytics = self.query_analytics.lock().await;
796 analytics.record_execution(stats);
797 }
798 }
799
800 debug!(
801 "Query executed in {:?}: {} (success: {})",
802 execution_time, context.request_id, success
803 );
804
805 result
806 }
807
808 fn generate_query_hash(
810 &self,
811 document: &Document,
812 variables: &HashMap<String, Value>,
813 ) -> String {
814 let mut hasher = DefaultHasher::new();
815
816 let query_str = format!("{document:?}");
818 query_str.hash(&mut hasher);
819
820 let variables_str = format!("{variables:?}");
822 variables_str.hash(&mut hasher);
823
824 format!("{:x}", hasher.finish())
825 }
826
827 fn calculate_query_complexity(&self, document: &Document) -> f64 {
829 let mut complexity = 0.0;
830
831 for definition in &document.definitions {
833 match definition {
834 crate::ast::Definition::Operation(op) => {
835 complexity += match op.operation_type {
836 OperationType::Query => 1.0,
837 OperationType::Mutation => 2.0,
838 OperationType::Subscription => 1.5,
839 };
840
841 complexity += self.calculate_selection_complexity(&op.selection_set, 0) as f64;
843 }
844 crate::ast::Definition::Fragment(_) => {
845 complexity += 0.5; }
847 crate::ast::Definition::Schema(_) => {
848 complexity += 0.1; }
850 crate::ast::Definition::Type(_) => {
851 complexity += 0.1; }
853 crate::ast::Definition::Directive(_) => {
854 complexity += 0.1; }
856 crate::ast::Definition::SchemaExtension(_) => {
857 complexity += 0.1; }
859 crate::ast::Definition::TypeExtension(_) => {
860 complexity += 0.1; }
862 }
863 }
864
865 complexity
866 }
867
868 #[allow(clippy::only_used_in_recursion)]
870 fn calculate_selection_complexity(
871 &self,
872 selection_set: &crate::ast::SelectionSet,
873 depth: usize,
874 ) -> usize {
875 let mut complexity = depth * 2; for selection in &selection_set.selections {
878 match selection {
879 crate::ast::Selection::Field(field) => {
880 complexity += 1;
881 if let Some(ref selection_set) = field.selection_set {
882 complexity += self.calculate_selection_complexity(selection_set, depth + 1);
883 }
884 complexity += field.arguments.len();
886 }
887 crate::ast::Selection::InlineFragment(fragment) => {
888 complexity +=
889 self.calculate_selection_complexity(&fragment.selection_set, depth + 1);
890 }
891 crate::ast::Selection::FragmentSpread(_) => {
892 complexity += 2; }
894 }
895 }
896
897 complexity
898 }
899
900 async fn execute_query_plan_with_analytics(
902 &self,
903 query_plan: &QueryPlan,
904 context: &FederationContext,
905 _query_hash: &str,
906 recommended_service: Option<String>,
907 ) -> Result<FederationResult> {
908 if let Some(service_id) = recommended_service {
910 debug!("Using analytics-recommended service: {}", service_id);
911 }
912
913 self.execute_query_plan(query_plan, context).await
917 }
918
919 pub async fn get_analytics_insights(&self) -> HashMap<String, serde_json::Value> {
921 let analytics = self.query_analytics.lock().await;
922 analytics.get_performance_insights()
923 }
924
925 pub async fn get_schema(&self) -> Option<Schema> {
927 let schema_guard = self.merged_schema.read().await;
928 schema_guard.clone()
929 }
930
931 pub async fn get_services(&self) -> Vec<ServiceInfo> {
933 self.service_discovery.get_services().await
934 }
935
936 pub async fn get_healthy_services(&self) -> Vec<ServiceInfo> {
938 self.service_discovery.get_healthy_services().await
939 }
940
941 pub async fn get_sync_status(&self) -> super::real_time_sync::SyncStatus {
943 self.schema_synchronizer.get_sync_status().await
944 }
945
946 pub async fn get_schema_conflicts(&self) -> Vec<super::real_time_sync::SchemaConflict> {
948 self.schema_synchronizer.get_active_conflicts().await
949 }
950
951 pub async fn force_schema_sync(&self) -> Result<()> {
953 self.schema_synchronizer.perform_full_sync().await
954 }
955
956 pub async fn subscribe_to_schema_changes(
958 &self,
959 ) -> tokio::sync::mpsc::UnboundedReceiver<super::real_time_sync::SchemaChangeEvent> {
960 self.schema_synchronizer.subscribe_to_changes().await
961 }
962
963 async fn execute_query_plan(
965 &self,
966 plan: &QueryPlan,
967 context: &FederationContext,
968 ) -> Result<FederationResult> {
969 let mut results = HashMap::new();
970 let mut errors = Vec::new();
971
972 for step in &plan.steps {
974 match self.execute_query_step(step, context, &results).await {
975 Ok(step_result) => {
976 if let Some(data) = step_result.data {
977 results.insert(step.endpoint_id.clone(), data);
978 }
979 errors.extend(step_result.errors);
980 }
981 Err(e) => {
982 errors.push(FederationError {
983 message: format!(
984 "Failed to execute step for service {}: {}",
985 step.endpoint_id, e
986 ),
987 path: None,
988 locations: None,
989 extensions: HashMap::new(),
990 });
991 }
992 }
993 }
994
995 let merged_data = self.merge_results(&results, plan).await?;
997
998 Ok(FederationResult {
999 data: Some(merged_data),
1000 errors,
1001 extensions: HashMap::new(),
1002 })
1003 }
1004
1005 async fn execute_query_step(
1007 &self,
1008 step: &QueryStep,
1009 context: &FederationContext,
1010 _previous_results: &HashMap<String, serde_json::Value>,
1011 ) -> Result<FederationResult> {
1012 let service = self
1014 .service_discovery
1015 .get_service(&step.endpoint_id)
1016 .await
1017 .ok_or_else(|| anyhow!("Service not found: {}", step.endpoint_id))?;
1018
1019 {
1021 let mut breakers = self.circuit_breakers.write().await;
1022 let breaker = breakers
1023 .entry(service.id.clone())
1024 .or_insert_with(|| ServiceCircuitBreaker::new(self.config.circuit_breaker.clone()));
1025
1026 if !breaker.can_execute() {
1027 return Err(anyhow!("Circuit breaker open for service: {}", service.id));
1028 }
1029 }
1030
1031 let result = self
1033 .execute_with_retry(&service, &step.query_fragment, &context.variables)
1034 .await;
1035
1036 {
1038 let mut breakers = self.circuit_breakers.write().await;
1039 if let Some(breaker) = breakers.get_mut(&service.id) {
1040 match result {
1041 Ok(_) => breaker.record_success(),
1042 Err(_) => breaker.record_failure(),
1043 }
1044 }
1045 }
1046
1047 self.load_balancer
1049 .lock()
1050 .await
1051 .record_completion(&service.id);
1052
1053 result
1054 }
1055
1056 async fn execute_with_retry(
1058 &self,
1059 service: &ServiceInfo,
1060 query: &str,
1061 variables: &HashMap<String, Value>,
1062 ) -> Result<FederationResult> {
1063 let mut last_error = None;
1064
1065 for attempt in 0..=self.config.retry_policy.max_retries {
1066 if attempt > 0 {
1067 let delay = self.calculate_retry_delay(attempt);
1068 tokio::time::sleep(delay).await;
1069 }
1070
1071 match self.execute_single_request(service, query, variables).await {
1072 Ok(result) => return Ok(result),
1073 Err(e) => {
1074 last_error = Some(e);
1075 warn!(
1076 "Query attempt {} failed for service {}: {}",
1077 attempt + 1,
1078 service.id,
1079 last_error
1080 .as_ref()
1081 .expect("last_error should be set after failed attempt")
1082 );
1083 }
1084 }
1085 }
1086
1087 Err(last_error.unwrap_or_else(|| anyhow!("All retry attempts failed")))
1088 }
1089
1090 fn calculate_retry_delay(&self, attempt: usize) -> Duration {
1092 let base_delay = self.config.retry_policy.base_delay;
1093 let multiplier = self.config.retry_policy.backoff_multiplier;
1094 let max_delay = self.config.retry_policy.max_delay;
1095
1096 let delay = base_delay.as_millis() as f64 * multiplier.powi(attempt as i32);
1097 let delay = Duration::from_millis(delay.min(max_delay.as_millis() as f64) as u64);
1098
1099 if self.config.retry_policy.jitter {
1100 let jitter = fastrand::f64() * 0.1; let jitter_factor = 1.0 + (jitter - 0.05);
1102 Duration::from_millis((delay.as_millis() as f64 * jitter_factor) as u64)
1103 } else {
1104 delay
1105 }
1106 }
1107
1108 async fn execute_single_request(
1110 &self,
1111 service: &ServiceInfo,
1112 query: &str,
1113 variables: &HashMap<String, Value>,
1114 ) -> Result<FederationResult> {
1115 let request_body = serde_json::json!({
1116 "query": query,
1117 "variables": variables
1118 });
1119
1120 let response = self
1121 .http_client
1122 .post(&service.url)
1123 .json(&request_body)
1124 .send()
1125 .await
1126 .context("Failed to send request")?;
1127
1128 if !response.status().is_success() {
1129 return Err(anyhow!(
1130 "HTTP request failed with status: {}",
1131 response.status()
1132 ));
1133 }
1134
1135 let response_json: serde_json::Value = response
1136 .json()
1137 .await
1138 .context("Failed to parse response JSON")?;
1139
1140 let data = response_json.get("data").cloned();
1142 let errors = response_json
1143 .get("errors")
1144 .and_then(|e| e.as_array())
1145 .map(|errors| {
1146 errors
1147 .iter()
1148 .filter_map(|error| {
1149 Some(FederationError {
1150 message: error.get("message")?.as_str()?.to_string(),
1151 path: error.get("path").and_then(|p| {
1152 p.as_array().map(|arr| {
1153 arr.iter()
1154 .filter_map(|v| v.as_str().map(|s| s.to_string()))
1155 .collect()
1156 })
1157 }),
1158 locations: None, extensions: HashMap::new(),
1160 })
1161 })
1162 .collect()
1163 })
1164 .unwrap_or_default();
1165
1166 Ok(FederationResult {
1167 data,
1168 errors,
1169 extensions: HashMap::new(),
1170 })
1171 }
1172
1173 async fn merge_results(
1187 &self,
1188 results: &HashMap<String, serde_json::Value>,
1189 _plan: &QueryPlan,
1190 ) -> Result<serde_json::Value> {
1191 if results.len() == 1 {
1192 return results
1193 .values()
1194 .next()
1195 .cloned()
1196 .ok_or_else(|| anyhow!("federated result map was unexpectedly empty"));
1197 }
1198
1199 let mut endpoint_ids: Vec<&String> = results.keys().collect();
1201 endpoint_ids.sort();
1202
1203 let mut merged = serde_json::Value::Object(serde_json::Map::new());
1204 for id in endpoint_ids {
1205 if let Some(value) = results.get(id) {
1206 Self::deep_merge_json(&mut merged, value);
1207 }
1208 }
1209 Ok(merged)
1210 }
1211
1212 fn deep_merge_json(target: &mut serde_json::Value, source: &serde_json::Value) {
1220 use serde_json::Value;
1221 match (target, source) {
1222 (Value::Object(t), Value::Object(s)) => {
1223 for (k, v) in s {
1224 match t.get_mut(k) {
1225 Some(existing) => Self::deep_merge_json(existing, v),
1226 None => {
1227 t.insert(k.clone(), v.clone());
1228 }
1229 }
1230 }
1231 }
1232 (Value::Array(t), Value::Array(s)) => {
1233 t.extend(s.iter().cloned());
1234 }
1235 (t @ Value::Null, s) => {
1236 *t = s.clone();
1237 }
1238 _ => {}
1241 }
1242 }
1243
1244 async fn rebuild_schema(&self) -> Result<()> {
1246 rebuild_federated_schema(
1247 &self.service_discovery,
1248 &self.schema_stitcher,
1249 &self.merged_schema,
1250 )
1251 .await
1252 }
1253
1254 async fn setup_event_handling(&self) -> Result<()> {
1263 let handler = SchemaRebuildHandler {
1264 service_discovery: Arc::clone(&self.service_discovery),
1265 schema_stitcher: Arc::clone(&self.schema_stitcher),
1266 merged_schema: Arc::clone(&self.merged_schema),
1267 };
1268 self.service_discovery
1269 .add_event_handler(Box::new(handler))
1270 .await;
1271 info!("Service discovery event handling configured (dynamic re-federation active)");
1272 Ok(())
1273 }
1274}
1275
1276async fn rebuild_federated_schema(
1282 service_discovery: &Arc<ServiceDiscovery>,
1283 schema_stitcher: &Arc<SchemaStitcher>,
1284 merged_schema: &Arc<RwLock<Option<Schema>>>,
1285) -> Result<()> {
1286 info!("Rebuilding federated schema");
1287
1288 let services = service_discovery.get_healthy_services().await;
1289 let endpoints: Vec<RemoteEndpoint> = services
1290 .into_iter()
1291 .map(|service| RemoteEndpoint {
1292 id: service.id,
1293 url: service.url,
1294 namespace: Some(service.name),
1295 auth_header: None,
1296 timeout_secs: 30,
1297 max_retries: 3,
1298 retry_strategy: RetryStrategy::ExponentialBackoff {
1299 initial_delay_ms: 100,
1300 max_delay_ms: 5000,
1301 multiplier: 2.0,
1302 },
1303 health_check_url: None,
1304 priority: 0,
1305 schema_version: service.federation_version,
1306 min_compatible_version: None,
1307 })
1308 .collect();
1309
1310 let schema = schema_stitcher.merge_schemas(&endpoints).await?;
1311
1312 {
1313 let mut schema_guard = merged_schema.write().await;
1314 *schema_guard = Some(schema);
1315 }
1316
1317 info!("Schema rebuilt successfully");
1318 Ok(())
1319}
1320
1321struct SchemaRebuildHandler {
1324 service_discovery: Arc<ServiceDiscovery>,
1325 schema_stitcher: Arc<SchemaStitcher>,
1326 merged_schema: Arc<RwLock<Option<Schema>>>,
1327}
1328
1329#[async_trait]
1330impl ServiceDiscoveryEventHandler for SchemaRebuildHandler {
1331 async fn handle_event(&self, event: DiscoveryEvent) -> Result<()> {
1332 match event {
1333 DiscoveryEvent::ServiceRegistered(_)
1334 | DiscoveryEvent::ServiceDeregistered(_)
1335 | DiscoveryEvent::HealthChanged { .. } => {
1336 info!("Service discovery event received; rebuilding federated schema");
1337 if let Err(e) = rebuild_federated_schema(
1338 &self.service_discovery,
1339 &self.schema_stitcher,
1340 &self.merged_schema,
1341 )
1342 .await
1343 {
1344 warn!("Failed to rebuild federated schema after discovery event: {e}");
1345 return Err(e);
1346 }
1347 Ok(())
1348 }
1349 _ => Ok(()),
1350 }
1351 }
1352}
1353
1354#[cfg(test)]
1355mod tests {
1356 use super::*;
1357
1358 #[tokio::test]
1359 async fn test_load_balancer_round_robin() {
1360 let config = LoadBalancingConfig {
1361 algorithm: LoadBalancingAlgorithm::RoundRobin,
1362 ..Default::default()
1363 };
1364 let mut balancer = LoadBalancer::new(config);
1365
1366 let services = vec![
1367 ServiceInfo {
1368 id: "service-1".to_string(),
1369 name: "Service 1".to_string(),
1370 url: "http://localhost:4001".to_string(),
1371 version: None,
1372 capabilities: Default::default(),
1373 health_status: HealthStatus::Healthy,
1374 metadata: HashMap::new(),
1375 last_seen: chrono::Utc::now(),
1376 response_time: None,
1377 load_factor: 0.5,
1378 federation_version: None,
1379 },
1380 ServiceInfo {
1381 id: "service-2".to_string(),
1382 name: "Service 2".to_string(),
1383 url: "http://localhost:4002".to_string(),
1384 version: None,
1385 capabilities: Default::default(),
1386 health_status: HealthStatus::Healthy,
1387 metadata: HashMap::new(),
1388 last_seen: chrono::Utc::now(),
1389 response_time: None,
1390 load_factor: 0.5,
1391 federation_version: None,
1392 },
1393 ];
1394
1395 let selected1 = balancer.select_service(&services, None);
1396 let selected2 = balancer.select_service(&services, None);
1397
1398 assert_eq!(selected1, Some("service-1".to_string()));
1399 assert_eq!(selected2, Some("service-2".to_string()));
1400 }
1401
1402 #[test]
1403 fn test_circuit_breaker() {
1404 let config = CircuitBreakerConfig {
1405 failure_threshold: 2,
1406 success_threshold: 1,
1407 timeout: Duration::from_secs(1),
1408 retry_after: Duration::from_secs(1),
1409 };
1410 let mut breaker = ServiceCircuitBreaker::new(config);
1411
1412 assert!(breaker.can_execute());
1413 assert_eq!(breaker.state(), CircuitBreakerState::Closed);
1414
1415 breaker.record_failure();
1417 assert!(breaker.can_execute());
1418 breaker.record_failure();
1419 assert!(!breaker.can_execute());
1420 assert_eq!(breaker.state(), CircuitBreakerState::Open);
1421 }
1422
1423 #[test]
1424 fn regression_deep_merge_preserves_both_subgraphs_under_shared_key() {
1425 let mut merged = serde_json::json!({});
1427 let a = serde_json::json!({"data": {"product": {"id": "1"}}});
1428 let b = serde_json::json!({"data": {"review": {"stars": 5}}});
1429 EnhancedFederationManager::deep_merge_json(&mut merged, &a);
1430 EnhancedFederationManager::deep_merge_json(&mut merged, &b);
1431
1432 assert_eq!(merged["data"]["product"]["id"], serde_json::json!("1"));
1434 assert_eq!(merged["data"]["review"]["stars"], serde_json::json!(5));
1435 }
1436
1437 #[test]
1438 fn regression_deep_merge_entity_extension_merges_fields() {
1439 let mut merged = serde_json::json!({});
1441 let base = serde_json::json!({"product": {"id": "1", "name": "Widget"}});
1442 let ext = serde_json::json!({"product": {"id": "1", "price": 9.99}});
1443 EnhancedFederationManager::deep_merge_json(&mut merged, &base);
1444 EnhancedFederationManager::deep_merge_json(&mut merged, &ext);
1445
1446 assert_eq!(merged["product"]["name"], serde_json::json!("Widget"));
1447 assert_eq!(merged["product"]["price"], serde_json::json!(9.99));
1448 assert_eq!(merged["product"]["id"], serde_json::json!("1"));
1449 }
1450
1451 #[test]
1452 fn regression_deep_merge_concatenates_arrays() {
1453 let mut merged = serde_json::json!({"items": [1, 2]});
1454 let more = serde_json::json!({"items": [3, 4]});
1455 EnhancedFederationManager::deep_merge_json(&mut merged, &more);
1456 assert_eq!(merged["items"], serde_json::json!([1, 2, 3, 4]));
1457 }
1458}