use crate::error::FusekiResult;
use async_trait::async_trait;
use dashmap::DashMap;
use reqwest::Client;
use serde::{Deserialize, Serialize};
use std::{
collections::{HashMap, HashSet},
sync::Arc,
time::{Duration, Instant},
};
use tokio::sync::{RwLock, Semaphore};
pub type QueryDecomposeFn = Box<dyn Fn(&str) -> Vec<QueryFragment> + Send + Sync>;
pub struct EndpointRegistry {
pub(crate) endpoints: HashMap<String, EndpointInfo>,
pub(crate) health_cache: DashMap<String, HealthStatus>,
pub(crate) discovery: Arc<EndpointDiscovery>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct EndpointInfo {
pub url: String,
pub name: String,
pub description: Option<String>,
pub capabilities: EndpointCapabilities,
pub authentication: Option<EndpointAuth>,
pub timeout_ms: u64,
pub max_retries: u32,
pub priority: u32,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct EndpointCapabilities {
pub sparql_version: String,
pub supports_update: bool,
pub supports_graph_store: bool,
pub supports_service_description: bool,
pub max_query_size: Option<usize>,
pub rate_limit: Option<RateLimit>,
pub features: HashSet<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct RateLimit {
pub requests_per_second: u32,
pub burst_size: u32,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum EndpointAuth {
Basic {
username: String,
password: String,
},
Bearer {
token: String,
},
ApiKey {
key: String,
header_name: String,
},
OAuth2 {
client_id: String,
client_secret: String,
token_url: String,
},
}
#[derive(Debug, Clone)]
pub struct HealthStatus {
pub is_healthy: bool,
pub last_check: Instant,
pub response_time_ms: u64,
pub error_count: u32,
pub success_count: u32,
}
pub struct EndpointDiscovery {
#[allow(dead_code)]
pub(crate) client: Client,
#[allow(dead_code)]
pub(crate) catalogs: Vec<String>,
}
pub struct QueryPlanner {
pub(crate) decomposition_rules: Vec<DecompositionRule>,
pub(crate) join_optimizer: Arc<JoinOrderOptimizer>,
#[allow(dead_code)]
pub(crate) statistics: Arc<RwLock<FederationStatistics>>,
}
pub struct DecompositionRule {
pub name: String,
pub pattern: String,
pub applicability_check: Box<dyn Fn(&str) -> bool + Send + Sync>,
pub decompose: QueryDecomposeFn,
}
#[derive(Debug, Clone)]
pub struct QueryFragment {
pub fragment_id: String,
pub sparql: String,
pub target_endpoints: Vec<String>,
pub dependencies: Vec<String>,
pub estimated_cost: f64,
pub is_optional: bool,
}
pub struct JoinOrderOptimizer {
#[allow(dead_code)]
pub(crate) cost_model: Arc<JoinCostModel>,
pub(crate) dp_cache: DashMap<String, JoinPlan>,
}
pub struct JoinCostModel {
#[allow(dead_code)]
pub(crate) latency_map: DashMap<(String, String), Duration>,
#[allow(dead_code)]
pub(crate) bandwidth_map: DashMap<String, f64>,
}
#[derive(Debug, Clone)]
pub struct JoinPlan {
pub steps: Vec<JoinStep>,
pub estimated_cost: f64,
pub estimated_time_ms: u64,
}
#[derive(Debug, Clone)]
pub struct JoinStep {
pub operation: JoinOperation,
pub left_source: String,
pub right_source: String,
pub output_destination: String,
}
#[derive(Debug, Clone)]
pub enum JoinOperation {
HashJoin,
SortMergeJoin,
NestedLoopJoin,
BroadcastJoin,
IndexJoin,
}
pub struct CostEstimator {
pub(crate) history: Arc<RwLock<QueryHistory>>,
#[allow(dead_code)]
pub(crate) ml_model: Option<Arc<CostPredictionModel>>,
pub(crate) cardinality: Arc<CardinalityEstimator>,
}
pub struct QueryHistory {
#[allow(dead_code)]
pub(crate) executions: Vec<QueryExecution>,
pub(crate) patterns: HashMap<String, PatternStats>,
}
#[derive(Debug, Clone)]
pub struct QueryExecution {
pub query_hash: String,
pub fragments: Vec<String>,
pub endpoints: Vec<String>,
pub execution_time_ms: u64,
pub result_count: usize,
pub timestamp: Instant,
}
#[derive(Debug, Clone)]
pub struct PatternStats {
pub pattern: String,
pub avg_execution_time: f64,
pub avg_result_count: f64,
pub execution_count: u32,
}
pub struct CostPredictionModel {
#[allow(dead_code)]
pub(crate) _model_data: Vec<u8>,
}
pub struct CardinalityEstimator {
#[allow(dead_code)]
pub(crate) endpoint_stats: DashMap<String, EndpointStatistics>,
#[allow(dead_code)]
pub(crate) histograms: DashMap<String, Histogram>,
}
#[derive(Debug, Clone)]
pub struct EndpointStatistics {
pub triple_count: u64,
pub distinct_subjects: u64,
pub distinct_predicates: u64,
pub distinct_objects: u64,
pub last_updated: Instant,
}
impl Default for EndpointStatistics {
fn default() -> Self {
Self {
triple_count: 0,
distinct_subjects: 0,
distinct_predicates: 0,
distinct_objects: 0,
last_updated: Instant::now(),
}
}
}
#[derive(Debug, Clone)]
pub struct Histogram {
pub buckets: Vec<HistogramBucket>,
pub total_count: u64,
}
#[derive(Debug, Clone)]
pub struct HistogramBucket {
pub min_value: String,
pub max_value: String,
pub count: u64,
}
pub struct FederationStatistics {
#[allow(dead_code)]
pub(crate) query_stats: HashMap<String, QueryStats>,
#[allow(dead_code)]
pub(crate) endpoint_stats: HashMap<String, EndpointPerformance>,
}
#[derive(Debug, Clone)]
pub struct QueryStats {
pub total_executions: u64,
pub avg_execution_time: f64,
pub success_rate: f64,
}
#[derive(Debug, Clone)]
pub struct EndpointPerformance {
pub avg_response_time: f64,
pub availability: f64,
pub throughput: f64,
}
pub struct FederatedExecutor {
pub(crate) client_pool: Arc<ClientPool>,
pub(crate) strategies: Vec<Arc<dyn ExecutionStrategy>>,
pub(crate) semaphore: Arc<Semaphore>,
pub(crate) retry_policy: Arc<RetryPolicy>,
}
pub struct ClientPool {
pub(crate) clients: DashMap<String, Client>,
pub(crate) max_connections_per_endpoint: usize,
}
#[async_trait]
pub trait ExecutionStrategy: Send + Sync {
fn name(&self) -> &str;
fn applicable(&self, plan: &ExecutionPlan) -> bool;
async fn execute(
&self,
plan: &ExecutionPlan,
executor: &FederatedExecutor,
) -> FusekiResult<QueryResults>;
}
#[derive(Debug, Clone)]
pub struct ExecutionPlan {
pub query_id: String,
pub fragments: Vec<QueryFragment>,
pub join_plan: JoinPlan,
pub timeout_ms: u64,
pub optimization_hints: HashMap<String, String>,
pub execution_steps: Vec<String>,
pub estimated_cost: f64,
pub resource_requirements: ResourceRequirements,
}
#[derive(Debug, Clone)]
pub struct ResourceRequirements {
pub required_endpoints: Vec<String>,
pub estimated_memory_mb: f64,
pub estimated_cpu_cores: f64,
}
#[derive(Debug, Clone)]
pub struct QueryResults {
pub bindings: Vec<HashMap<String, serde_json::Value>>,
pub metadata: ResultMetadata,
}
#[derive(Debug, Clone)]
pub struct ResultMetadata {
pub total_execution_time_ms: u64,
pub endpoint_times: HashMap<String, u64>,
pub result_count: usize,
pub partial_results: bool,
}
pub struct RetryPolicy {
pub max_retries: u32,
pub initial_backoff_ms: u64,
pub max_backoff_ms: u64,
pub exponential_base: f64,
}
#[derive(Debug, Clone)]
pub struct ServicePattern {
pub service_url: String,
pub pattern: String,
pub is_silent: bool,
pub is_optional: bool,
}
#[async_trait]
pub trait MergeStrategy: Send + Sync {
fn name(&self) -> &str;
async fn merge(&self, results: Vec<QueryResults>) -> FusekiResult<QueryResults>;
}
pub struct ResultMerger {
pub strategies: HashMap<String, Arc<dyn MergeStrategy>>,
#[allow(dead_code)]
pub(crate) dedup_cache: Arc<RwLock<HashSet<u64>>>,
}