Skip to main content

oxirs_cluster/
lib.rs

1//! # OxiRS Cluster
2//!
3//! [![Version](https://img.shields.io/badge/version-0.3.3-blue)](https://github.com/cool-japan/oxirs/releases)
4//! [![docs.rs](https://docs.rs/oxirs-cluster/badge.svg)](https://docs.rs/oxirs-cluster)
5//!
6//! **Status**: Production Release (v0.3.3)
7//! **Stability**: Public APIs are stable. Production-ready with comprehensive testing.
8//!
9//! Raft-backed distributed dataset for high availability and horizontal scaling.
10//!
11//! This crate provides distributed storage capabilities using Raft consensus with
12//! multi-region support, Byzantine fault tolerance, and advanced replication strategies.
13//!
14//! ## Features
15//!
16//! - **Raft Consensus**: Production-ready Raft implementation using openraft
17//! - **Distributed RDF Storage**: Scalable, consistent RDF triple storage
18//! - **Automatic Failover**: Leader election and automatic recovery
19//! - **Node Discovery**: Multiple discovery mechanisms (static, DNS, multicast)
20//! - **Replication Management**: Configurable replication strategies
21//! - **SPARQL Support**: Distributed SPARQL query execution
22//! - **Transaction Support**: Distributed ACID transactions
23//!
24//! ## Example
25//!
26//! ```ignore
27//! use oxirs_cluster::{ClusterNode, NodeConfig};
28//! use std::net::SocketAddr;
29//!
30//! async fn example() -> Result<(), Box<dyn std::error::Error>> {
31//!     let config = NodeConfig {
32//!         node_id: 1,
33//!         address: "127.0.0.1:8080".parse()?,
34//!         data_dir: "./data".to_string(),
35//!         peers: vec![2, 3],
36//!     };
37//!
38//!     let mut node = ClusterNode::new(config).await?;
39//!     node.start().await?;
40//!
41//!     // Insert data through consensus
42//!     node.insert_triple(
43//!         "<http://example.org/subject>",
44//!         "<http://example.org/predicate>",
45//!         "\"object\"")
46//!     .await?;
47//!
48//!     Ok(())
49//! }
50//! ```
51
52#![allow(clippy::field_reassign_with_default)]
53#![allow(clippy::single_match)]
54#![allow(clippy::collapsible_if)]
55#![allow(clippy::clone_on_copy)]
56#![allow(clippy::type_complexity)]
57#![allow(clippy::collapsible_match)]
58#![allow(clippy::manual_clamp)]
59#![allow(clippy::needless_range_loop)]
60#![allow(clippy::or_fun_call)]
61#![allow(clippy::if_same_then_else)]
62#![allow(clippy::only_used_in_recursion)]
63#![allow(clippy::new_without_default)]
64#![allow(clippy::derivable_impls)]
65#![allow(clippy::useless_conversion)]
66use serde::{Deserialize, Serialize};
67use std::collections::HashMap;
68use std::net::SocketAddr;
69use std::sync::Arc;
70use tokio::sync::RwLock;
71
72pub mod adaptive_leader_election;
73pub mod advanced_partitioning;
74pub mod advanced_storage;
75pub mod alerting;
76pub mod auto_scaling;
77pub mod backup_restore;
78pub mod circuit_breaker;
79pub mod cloud_integration;
80pub mod cluster_metrics;
81pub mod cluster_metrics_manager;
82pub mod cluster_metrics_stats;
83#[cfg(test)]
84mod cluster_metrics_tests;
85pub mod cluster_metrics_types;
86pub mod compression_strategy;
87pub mod conflict_resolution;
88pub mod consensus;
89pub mod crash_recovery;
90pub mod data_rebalancing;
91pub mod disaster_recovery;
92pub mod discovery;
93pub mod distributed_query;
94pub mod distributed_tracing;
95pub mod edge_computing;
96pub mod encryption;
97pub mod enhanced_node_discovery;
98pub mod enhanced_snapshotting;
99pub mod error;
100pub mod failover;
101pub mod federation;
102pub mod gpu_acceleration;
103pub mod health_monitor;
104pub mod health_monitoring;
105pub mod memory_optimization;
106pub mod merkle_tree;
107pub mod ml_optimization;
108pub mod multi_tenant;
109pub mod mvcc;
110pub mod mvcc_storage;
111pub mod network;
112pub mod neural_architecture_search;
113pub mod node_lifecycle;
114pub mod node_status_tracker;
115pub mod operational_transformation;
116pub mod optimization;
117pub mod partition_detection;
118pub mod performance_metrics;
119pub mod performance_monitor;
120pub mod raft;
121#[cfg(feature = "raft")]
122mod raft_network;
123pub mod raft_optimization;
124pub mod raft_profiling;
125pub mod raft_state;
126pub mod range_partitioning;
127pub mod read_replica;
128pub mod region_manager;
129pub mod replication;
130pub mod replication_lag_monitor;
131pub mod rl_consensus_optimizer;
132pub mod rolling_upgrade;
133pub mod rolling_upgrade_orchestrator;
134pub mod split_brain_detector;
135pub mod visualization_dashboard;
136pub mod zero_downtime_migration;
137// Temporarily disabled due to missing scirs2_core features
138// pub mod revolutionary_cluster_optimization;
139pub mod cross_dc;
140pub mod network_compression;
141pub mod security;
142pub mod serialization;
143pub mod shard;
144pub mod shard_manager;
145pub mod shard_migration;
146pub mod shard_routing;
147pub mod split_brain_prevention;
148pub mod storage;
149pub mod strong_consistency;
150pub mod tls;
151pub mod topology;
152pub mod transaction;
153pub mod transaction_optimizer;
154
155#[cfg(feature = "bft")]
156pub mod bft;
157#[cfg(feature = "bft")]
158pub mod bft_consensus;
159#[cfg(feature = "bft")]
160pub mod bft_network;
161
162pub mod gossip_scaling;
163pub mod sla_manager;
164pub mod stream_integration;
165
166// New modules added in v0.2.0
167pub mod adaptive_consistent_hash;
168pub mod cross_dc_consistency;
169pub mod distributed_tx_coordinator;
170
171// v1.1.0 Consistent hashing with virtual nodes and bounded loads
172pub mod vnodes_hash_ring;
173
174// v1.2.0 Gossip protocol for cluster membership management
175pub mod membership_gossip;
176
177// v1.2.0 Bully algorithm leader election simulation
178pub mod leader_election;
179
180// v1.2.0 Raft snapshot management with retention and checksum validation
181pub mod snapshot_manager;
182
183// v1.5.0 Consistent-hash shard router
184pub mod consistent_shard_router;
185
186// v1.6.0 Partition rebalancing for cluster data redistribution
187pub mod partition_rebalancer;
188
189// v1.7.0 Cluster node health monitoring with heartbeats
190pub mod node_monitor;
191
192// v1.8.0 Automatic failover handling with split-brain prevention
193pub mod failover_manager;
194
195/// Anti-entropy protocol for distributed consistency (v1.9.0).
196///
197/// Includes [`anti_entropy::AntiEntropyThrottle`] — a tokio-Semaphore-backed
198/// concurrency gate that limits concurrent sync sessions to
199/// `max_concurrent_syncs` (default 4) per node.
200pub mod anti_entropy;
201
202/// Gossip protocol primitives — fanout policies (`GossipFanout`) for
203/// epidemic-protocol dissemination.  For adaptive fanout (log₂-based,
204/// loss-rate aware) see [`gossip_scaling`].
205pub mod gossip;
206
207/// In-memory cluster simulation for scalability testing (10 → 1000 nodes).
208///
209/// `SimCluster` runs N virtual nodes in a single tokio runtime with bounded
210/// `mpsc` mailboxes — no real network sockets.  Available unconditionally so
211/// integration tests in `tests/` can import it without the `simulation`
212/// feature flag.  The `simulation` feature remains available for downstream
213/// crates that want to opt in explicitly.
214pub mod simulation;
215
216/// Replication bandwidth throttling: token-bucket per-peer rate limiting with adaptive adjustment (v2.0.0).
217pub mod replication_throttle;
218
219/// Data migration between cluster nodes: plan creation, range-based transfer,
220/// checksum-validated chunks, migration lifecycle and statistics (v1.1.0 round 14)
221pub mod data_migrator;
222
223/// Consistent-hash shard routing for distributed cluster nodes (v1.1.0 round 15)
224pub mod shard_router;
225
226/// Raft-style election timer: randomised timeout, TimerState (Idle/Running/Expired),
227/// reset/check/stop lifecycle, LCG seed for deterministic tests (v1.1.0 round 16)
228pub mod election_timer;
229
230/// Advanced compression codec system: Compressor trait, IdentityCodec, RleCodec,
231/// Lz4Codec, ZstdCodec, CodecRegistry (v0.3.0 W2-S8).
232pub mod compression;
233
234/// Advanced backup policy DSL: BackupPolicy, RetentionTier, GfsRotation,
235/// BackupExecutor with audit log, DestinationConfig (v0.3.0 W2-S8).
236pub mod backup;
237
238/// Per-node SLA admission control on Raft log writes and read replicas.
239///
240/// Reuses the shared per-tenant SLA primitives from [`oxirs_core::sla`]
241/// (W2-S4): [`oxirs_core::sla::SlaClass`],
242/// [`oxirs_core::sla::AdmissionController`], [`oxirs_core::sla::SlaThresholds`].
243/// Adds cluster-aware wrappers: [`sla::ClusterAdmissionController`],
244/// [`sla::SlaProposerGate`], [`sla::SlaReaderGate`] (v0.3.0 W2-S5).
245pub mod sla;
246
247/// Real-time streaming integration with the Raft log (W3-S9).
248///
249/// Provides [`streaming::ClusterSink`], a [`streaming::StreamSink`]
250/// implementation that proposes streaming events through
251/// [`consensus::ConsensusManager`] using the existing
252/// [`raft::RdfCommand`] payload, plus a
253/// [`streaming::BackpressureBridge`] that upstream operators poll for
254/// flow-control signals.
255pub mod streaming;
256
257/// Hierarchical log-replication topology for O(log N) fan-out (Phase B, v0.3.0).
258///
259/// Replaces O(N) all-to-all log shipping with a √N-relay spanning tree that
260/// respects rack/AZ/region topology. The leader ships to R = ceil(√N) relays,
261/// each relay fans out to its AZ members: total messages per round ≈ 2·√N.
262///
263/// See [`log_replication_topology::ReplicationTopology`] for the main entry point.
264pub mod log_replication_topology;
265
266/// Witness nodes for quorum participation without full log storage (Phase B, v0.3.0).
267///
268/// A witness participates in Raft leader election and confirms recent
269/// `AppendEntries` RPCs, but stores only the last `tail_window` log entries.
270/// For a 1000-node cluster with 200 witnesses, disk usage drops by ~20%.
271///
272/// See [`witness_node::WitnessNode`] for the main entry point.
273pub mod witness_node;
274
275/// Real-TCP-network E2E cluster harness (Phase C, v0.3.0).
276///
277/// Proves gossip and replication primitives over actual TCP sockets
278/// (localhost, single-process, multiple tokio tasks).  Not a production
279/// cluster; designed for integration testing.
280///
281/// See [`tcp_cluster::TcpClusterNetwork`] for the main entry point.
282pub mod tcp_cluster;
283
284/// Comprehensive certification suite for operational correctness guarantees (v1.0.0 LTS).
285///
286/// Validates four correctness dimensions via deterministic in-memory simulation:
287/// consistency guarantees, network-partition resilience, Raft invariants, and
288/// SLA latency bounds.
289///
290/// See [`certification::CertificationSuite`] for the main entry point.
291pub mod certification;
292
293pub use log_replication_topology::{
294    NodeDescriptor, ReplicationRole, ReplicationTopology, TopologyNode,
295};
296pub use tcp_cluster::{
297    ClusterMessage, GossipState, MessageCodec, NetworkStats, TcpClusterNetwork, TcpClusterNode,
298    TcpNodeConfig, TcpNodeError,
299};
300pub use witness_node::{
301    VoteRequest, VoteResponse, WitnessAppendRequest, WitnessAppendResponse, WitnessLogEntry,
302    WitnessNode,
303};
304
305pub use error::{ClusterError, Result};
306pub use failover::{FailoverConfig, FailoverManager, FailoverStrategy, RecoveryAction};
307pub use health_monitor::{HealthMonitor, HealthMonitorConfig, NodeHealth, SystemMetrics};
308
309// Temporarily disabled - Re-export revolutionary cluster optimization types
310// pub use revolutionary_cluster_optimization::{
311//     RevolutionaryClusterOptimizer, RevolutionaryClusterConfig, ConsensusOptimizationConfig,
312//     DataDistributionConfig, AdaptiveReplicationConfig, NetworkOptimizationConfig,
313//     ClusterPerformanceTargets, ClusterOptimizationResult, ClusterState, NodeState,
314//     ClusterOptimizationContext, ClusterAnalytics, ScalingPrediction,
315//     RevolutionaryClusterOptimizerFactory, ConsensusOptimizationStrategy,
316//     DataDistributionStrategy, AdaptiveReplicationStrategy, NetworkOptimizationStrategy,
317// };
318
319use conflict_resolution::{
320    ConflictResolver, ResolutionStrategy, TimestampedOperation, VectorClock,
321};
322use consensus::ConsensusManager;
323use discovery::{DiscoveryConfig, DiscoveryService, NodeInfo};
324use distributed_query::{DistributedQueryExecutor, ResultBinding};
325use edge_computing::{EdgeComputingManager, EdgeDeploymentStrategy, EdgeDeviceProfile};
326use raft::{OxirsNodeId, RdfResponse};
327use region_manager::{
328    ConsensusStrategy as RegionConsensusStrategy, MultiRegionReplicationStrategy, Region,
329    RegionManager,
330};
331use replication::{ReplicationManager, ReplicationStats, ReplicationStrategy};
332
333/// Multi-region deployment configuration
334#[derive(Debug, Clone, Serialize, Deserialize)]
335pub struct MultiRegionConfig {
336    /// Region identifier where this node is located
337    pub region_id: String,
338    /// Availability zone identifier
339    pub availability_zone_id: String,
340    /// Data center identifier (optional)
341    pub data_center: Option<String>,
342    /// Rack identifier (optional)
343    pub rack: Option<String>,
344    /// List of all regions in the deployment
345    pub regions: Vec<Region>,
346    /// Consensus strategy for multi-region operations
347    pub consensus_strategy: RegionConsensusStrategy,
348    /// Replication strategy for multi-region
349    pub replication_strategy: MultiRegionReplicationStrategy,
350    /// Conflict resolution strategy for distributed operations
351    pub conflict_resolution_strategy: ResolutionStrategy,
352    /// Edge computing configuration
353    pub edge_config: Option<EdgeComputingConfig>,
354    /// Enable advanced monitoring and metrics
355    pub enable_monitoring: bool,
356}
357
358/// Edge computing configuration
359#[derive(Debug, Clone, Serialize, Deserialize)]
360pub struct EdgeComputingConfig {
361    /// Enable edge computing features
362    pub enabled: bool,
363    /// Local edge device profile
364    pub device_profile: EdgeDeviceProfile,
365    /// Edge deployment strategy
366    pub deployment_strategy: EdgeDeploymentStrategy,
367    /// Enable intelligent caching
368    pub enable_intelligent_caching: bool,
369    /// Enable network condition monitoring
370    pub enable_network_monitoring: bool,
371}
372
373/// Cluster node configuration
374#[derive(Debug, Clone, Serialize, Deserialize)]
375pub struct NodeConfig {
376    /// Unique node identifier
377    pub node_id: OxirsNodeId,
378    /// Network address for communication
379    pub address: SocketAddr,
380    /// Data directory for persistent storage
381    pub data_dir: String,
382    /// List of peer node IDs
383    pub peers: Vec<OxirsNodeId>,
384    /// Known network addresses of entries in `peers`, keyed by node id.
385    ///
386    /// Required (for every id in `peers`) to form a real multi-node Raft
387    /// cluster — `ClusterNode::new` feeds this straight into
388    /// `RaftNode::set_network` via `ConsensusManager::with_raft_network`, so
389    /// the Raft transport (`raft_network.rs`) knows where to dial each peer
390    /// and what address to advertise for itself. Left empty for a genuine
391    /// single-node deployment (`peers` empty), which never needs it.
392    pub peer_addresses: HashMap<OxirsNodeId, SocketAddr>,
393    /// Discovery configuration
394    pub discovery: Option<DiscoveryConfig>,
395    /// Replication strategy
396    pub replication_strategy: Option<ReplicationStrategy>,
397    /// Use Byzantine fault tolerance instead of Raft.
398    ///
399    /// This field is always present so a caller can request BFT regardless of
400    /// how the crate was compiled; if it is `true` but the `bft` feature was
401    /// not enabled, [`ClusterNode::start`] fails loud rather than silently
402    /// running Raft.
403    pub use_bft: bool,
404    /// Multi-region deployment configuration
405    pub region_config: Option<MultiRegionConfig>,
406}
407
408impl NodeConfig {
409    /// Create a new node configuration
410    pub fn new(node_id: OxirsNodeId, address: SocketAddr) -> Self {
411        Self {
412            node_id,
413            address,
414            data_dir: format!("./data/node-{node_id}"),
415            peers: Vec::new(),
416            peer_addresses: HashMap::new(),
417            discovery: Some(DiscoveryConfig::default()),
418            replication_strategy: Some(ReplicationStrategy::default()),
419            use_bft: false,
420            region_config: None,
421        }
422    }
423
424    /// Add a peer to the configuration
425    pub fn add_peer(&mut self, peer_id: OxirsNodeId) -> &mut Self {
426        if !self.peers.contains(&peer_id) && peer_id != self.node_id {
427            self.peers.push(peer_id);
428        }
429        self
430    }
431
432    /// Record (or update) the network address of a peer, so a multi-node
433    /// Raft cluster can be formed. Does not implicitly add `peer_id` to
434    /// `peers` — call `add_peer` too if it isn't already listed.
435    pub fn add_peer_address(&mut self, peer_id: OxirsNodeId, address: SocketAddr) -> &mut Self {
436        self.peer_addresses.insert(peer_id, address);
437        self
438    }
439
440    /// Set the discovery configuration
441    pub fn with_discovery(mut self, discovery: DiscoveryConfig) -> Self {
442        self.discovery = Some(discovery);
443        self
444    }
445
446    /// Set the replication strategy
447    pub fn with_replication_strategy(mut self, strategy: ReplicationStrategy) -> Self {
448        self.replication_strategy = Some(strategy);
449        self
450    }
451
452    /// Enable Byzantine fault tolerance.
453    ///
454    /// Requesting BFT here always compiles; it only takes effect when the crate
455    /// is built with the `bft` feature. Otherwise [`ClusterNode::start`] returns
456    /// an explicit configuration error.
457    pub fn with_bft(mut self, enable: bool) -> Self {
458        self.use_bft = enable;
459        self
460    }
461
462    /// Set multi-region configuration
463    pub fn with_multi_region(mut self, region_config: MultiRegionConfig) -> Self {
464        self.region_config = Some(region_config);
465        self
466    }
467
468    /// Check if multi-region is enabled
469    pub fn is_multi_region_enabled(&self) -> bool {
470        self.region_config.is_some()
471    }
472
473    /// Get region ID if configured
474    pub fn region_id(&self) -> Option<&str> {
475        self.region_config
476            .as_ref()
477            .map(|config| config.region_id.as_str())
478    }
479
480    /// Get availability zone ID if configured
481    pub fn availability_zone_id(&self) -> Option<&str> {
482        self.region_config
483            .as_ref()
484            .map(|config| config.availability_zone_id.as_str())
485    }
486}
487
488/// Cluster node implementation
489pub struct ClusterNode {
490    config: NodeConfig,
491    consensus: ConsensusManager,
492    discovery: DiscoveryService,
493    replication: ReplicationManager,
494    query_executor: DistributedQueryExecutor,
495    region_manager: Option<Arc<RegionManager>>,
496    conflict_resolver: Arc<ConflictResolver>,
497    #[allow(dead_code)]
498    edge_manager: Option<Arc<EdgeComputingManager>>,
499    local_vector_clock: Arc<RwLock<VectorClock>>,
500    running: Arc<RwLock<bool>>,
501    byzantine_mode: Arc<RwLock<bool>>,
502    network_isolated: Arc<RwLock<bool>>,
503    /// Byzantine fault-tolerant consensus manager, present only when the node
504    /// was started with `use_bft = true` and the `bft` feature is enabled.
505    /// Held so its background tasks keep running for the node's lifetime.
506    #[cfg(feature = "bft")]
507    bft_manager: Option<Arc<bft_consensus::BftConsensusManager>>,
508}
509
510impl ClusterNode {
511    /// Create a new cluster node
512    pub async fn new(config: NodeConfig) -> Result<Self> {
513        // Validate configuration
514        if config.data_dir.is_empty() {
515            return Err(ClusterError::Config(
516                "Data directory cannot be empty".to_string(),
517            ));
518        }
519
520        // Create data directory if it doesn't exist
521        tokio::fs::create_dir_all(&config.data_dir)
522            .await
523            .map_err(|e| ClusterError::Other(format!("Failed to create data directory: {e}")))?;
524
525        // Initialize consensus manager. `with_raft_network` feeds this
526        // node's own address and its peers' known addresses down to
527        // `RaftNode::set_network`, so `ConsensusManager::init()` (called
528        // from `ClusterNode::start`) can construct a real multi-node Raft
529        // instance rather than failing with `NetworkNotConfigured` (only a
530        // genuine single-node peer set works without it).
531        #[cfg(feature = "raft")]
532        let consensus = ConsensusManager::new(config.node_id, config.peers.clone())
533            .with_raft_network(config.address, config.peer_addresses.clone());
534        #[cfg(not(feature = "raft"))]
535        let consensus = ConsensusManager::new(config.node_id, config.peers.clone());
536
537        // Initialize discovery service
538        let discovery_config = config.discovery.clone().unwrap_or_default();
539        let discovery = DiscoveryService::new(config.node_id, config.address, discovery_config);
540
541        // Initialize replication manager
542        let replication_strategy = config.replication_strategy.clone().unwrap_or_default();
543        let replication = ReplicationManager::new(replication_strategy, config.node_id);
544
545        // Initialize distributed query executor
546        let query_executor = DistributedQueryExecutor::new(config.node_id);
547
548        // Initialize conflict resolver
549        let default_resolution_strategy = if let Some(region_config) = &config.region_config {
550            region_config.conflict_resolution_strategy.clone()
551        } else {
552            ResolutionStrategy::LastWriterWins
553        };
554        let conflict_resolver = Arc::new(ConflictResolver::new(default_resolution_strategy));
555
556        // Initialize vector clock
557        let mut vector_clock = VectorClock::new();
558        vector_clock.increment(config.node_id);
559        let local_vector_clock = Arc::new(RwLock::new(vector_clock));
560
561        // Initialize region manager if multi-region is configured
562        let region_manager = if let Some(region_config) = &config.region_config {
563            let manager = Arc::new(RegionManager::new(
564                region_config.region_id.clone(),
565                region_config.availability_zone_id.clone(),
566                region_config.consensus_strategy.clone(),
567                region_config.replication_strategy.clone(),
568            ));
569
570            // Initialize with region topology
571            manager
572                .initialize(region_config.regions.clone())
573                .await
574                .map_err(|e| {
575                    ClusterError::Other(format!("Failed to initialize region manager: {e}"))
576                })?;
577
578            // Register this node in the region manager
579            manager
580                .register_node(
581                    config.node_id,
582                    region_config.region_id.clone(),
583                    region_config.availability_zone_id.clone(),
584                    region_config.data_center.clone(),
585                    region_config.rack.clone(),
586                )
587                .await
588                .map_err(|e| {
589                    ClusterError::Other(format!("Failed to register node in region manager: {e}"))
590                })?;
591
592            Some(manager)
593        } else {
594            None
595        };
596
597        // Initialize edge computing manager if configured
598        let edge_manager = if let Some(region_config) = &config.region_config {
599            if let Some(edge_config) = &region_config.edge_config {
600                if edge_config.enabled {
601                    let manager = Arc::new(EdgeComputingManager::new());
602
603                    // Register this device with the edge manager
604                    manager
605                        .register_device(edge_config.device_profile.clone())
606                        .await
607                        .map_err(|e| {
608                            ClusterError::Other(format!("Failed to register edge device: {e}"))
609                        })?;
610
611                    Some(manager)
612                } else {
613                    None
614                }
615            } else {
616                None
617            }
618        } else {
619            None
620        };
621
622        Ok(Self {
623            config,
624            consensus,
625            discovery,
626            replication,
627            query_executor,
628            region_manager,
629            conflict_resolver,
630            edge_manager,
631            local_vector_clock,
632            running: Arc::new(RwLock::new(false)),
633            byzantine_mode: Arc::new(RwLock::new(false)),
634            network_isolated: Arc::new(RwLock::new(false)),
635            #[cfg(feature = "bft")]
636            bft_manager: None,
637        })
638    }
639
640    /// Access the Byzantine fault-tolerant consensus manager, if this node was
641    /// started with `use_bft = true` and the `bft` feature is enabled.
642    #[cfg(feature = "bft")]
643    pub fn bft_manager(&self) -> Option<&Arc<bft_consensus::BftConsensusManager>> {
644        self.bft_manager.as_ref()
645    }
646
647    /// Construct and start the Byzantine fault-tolerant consensus manager for
648    /// this node, binding it to a persistent state-machine backend rooted at the
649    /// node's data directory. Stores the manager so its background tasks stay
650    /// alive for the node's lifetime.
651    #[cfg(feature = "bft")]
652    async fn start_bft_consensus(&mut self) -> Result<()> {
653        use crate::bft_consensus::BftConsensusManager;
654        use crate::network::NetworkConfig;
655        use crate::storage::{PersistentStorage, StorageConfig};
656
657        let storage_config = StorageConfig {
658            data_dir: self.config.data_dir.clone(),
659            ..StorageConfig::default()
660        };
661        let storage = Arc::new(
662            PersistentStorage::new(self.config.node_id, storage_config)
663                .await
664                .map_err(|e| {
665                    ClusterError::Storage(format!("failed to open BFT storage backend: {e}"))
666                })?,
667        );
668
669        let peers: Vec<String> = self.config.peers.iter().map(|p| p.to_string()).collect();
670        let manager = BftConsensusManager::new(
671            self.config.node_id.to_string(),
672            peers,
673            storage,
674            NetworkConfig::default(),
675        )
676        .await?;
677        manager.start().await?;
678
679        self.bft_manager = Some(Arc::new(manager));
680        Ok(())
681    }
682
683    /// Start the cluster node
684    pub async fn start(&mut self) -> Result<()> {
685        {
686            let mut running = self.running.write().await;
687            if *running {
688                return Ok(());
689            }
690            *running = true;
691        }
692
693        // Byzantine fault-tolerant consensus is opt-in via `NodeConfig::use_bft`
694        // and replaces the Raft path. When requested but the crate was built
695        // without the `bft` feature, fail loud rather than silently running Raft
696        // under a caller who explicitly asked for BFT.
697        if self.config.use_bft {
698            #[cfg(feature = "bft")]
699            {
700                self.start_bft_consensus().await?;
701                tracing::info!(
702                    "Cluster node {} started in Byzantine fault-tolerant mode",
703                    self.config.node_id
704                );
705                return Ok(());
706            }
707            #[cfg(not(feature = "bft"))]
708            {
709                let mut running = self.running.write().await;
710                *running = false;
711                return Err(ClusterError::Config(format!(
712                    "node {} requested Byzantine fault tolerance (use_bft = true) but this build \
713                     was compiled without the 'bft' feature; refusing to silently fall back to \
714                     Raft",
715                    self.config.node_id
716                )));
717            }
718        }
719
720        tracing::info!(
721            "Starting cluster node {} at {} with {} peers",
722            self.config.node_id,
723            self.config.address,
724            self.config.peers.len()
725        );
726
727        // Start discovery service
728        self.discovery
729            .start()
730            .await
731            .map_err(|e| ClusterError::Other(format!("Failed to start discovery service: {e}")))?;
732
733        // Discover initial nodes
734        let discovered_nodes = self
735            .discovery
736            .discover_nodes()
737            .await
738            .map_err(|e| ClusterError::Other(format!("Failed to discover nodes: {e}")))?;
739
740        // Add discovered nodes to replication manager and query executor
741        for node in discovered_nodes {
742            if node.node_id != self.config.node_id {
743                self.replication
744                    .add_replica(node.node_id, node.address.to_string());
745                self.query_executor.add_node(node.node_id).await;
746            }
747        }
748
749        // Initialize consensus system
750        self.consensus
751            .init()
752            .await
753            .map_err(|e| ClusterError::Other(format!("Failed to initialize consensus: {e}")))?;
754
755        tracing::info!("Cluster node {} started successfully", self.config.node_id);
756
757        // Start background tasks
758        self.start_background_tasks().await;
759
760        Ok(())
761    }
762
763    /// Stop the cluster node.
764    ///
765    /// Models a real node crash/stop (not a graceful departure): this
766    /// node's Raft participation is torn down abruptly via
767    /// `ConsensusManager::stop_raft` (no leadership transfer — contrast
768    /// `graceful_shutdown`), so peers stop hearing from it and a healthy
769    /// remaining majority can elect a new leader if it was one. A later
770    /// `start()` call rebuilds and rejoins Raft from scratch.
771    pub async fn stop(&mut self) -> Result<()> {
772        let mut running = self.running.write().await;
773        if !*running {
774            return Ok(());
775        }
776
777        tracing::info!("Stopping cluster node {}", self.config.node_id);
778
779        // Stop discovery service
780        self.discovery
781            .stop()
782            .await
783            .map_err(|e| ClusterError::Other(format!("Failed to stop discovery service: {e}")))?;
784
785        // Abruptly stop Raft participation so peers actually notice this
786        // node is gone (see doc comment above) and free the Raft RPC
787        // listener's port for a later `start()` to rebind.
788        self.consensus
789            .stop_raft()
790            .await
791            .map_err(|e| ClusterError::Other(format!("Failed to stop consensus: {e}")))?;
792
793        *running = false;
794
795        tracing::info!("Cluster node {} stopped", self.config.node_id);
796
797        Ok(())
798    }
799
800    /// Check if this node is the leader
801    pub async fn is_leader(&self) -> bool {
802        self.consensus.is_leader().await
803    }
804
805    /// Get current consensus term
806    pub async fn current_term(&self) -> u64 {
807        self.consensus.current_term().await
808    }
809
810    /// Insert a triple through distributed consensus
811    pub async fn insert_triple(
812        &self,
813        subject: &str,
814        predicate: &str,
815        object: &str,
816    ) -> Result<RdfResponse> {
817        if !self.is_leader().await {
818            return Err(ClusterError::NotLeader);
819        }
820
821        let response = self
822            .consensus
823            .insert_triple(
824                subject.to_string(),
825                predicate.to_string(),
826                object.to_string(),
827            )
828            .await?;
829
830        Ok(response)
831    }
832
833    /// Delete a triple through distributed consensus
834    pub async fn delete_triple(
835        &self,
836        subject: &str,
837        predicate: &str,
838        object: &str,
839    ) -> Result<RdfResponse> {
840        if !self.is_leader().await {
841            return Err(ClusterError::NotLeader);
842        }
843
844        let response = self
845            .consensus
846            .delete_triple(
847                subject.to_string(),
848                predicate.to_string(),
849                object.to_string(),
850            )
851            .await?;
852
853        Ok(response)
854    }
855
856    /// Clear all triples through distributed consensus
857    pub async fn clear_store(&self) -> Result<RdfResponse> {
858        if !self.is_leader().await {
859            return Err(ClusterError::NotLeader);
860        }
861
862        let response = self.consensus.clear_store().await?;
863        Ok(response)
864    }
865
866    /// Begin a distributed transaction
867    pub async fn begin_transaction(&self) -> Result<String> {
868        if !self.is_leader().await {
869            return Err(ClusterError::NotLeader);
870        }
871
872        let tx_id = uuid::Uuid::new_v4().to_string();
873        let _response = self.consensus.begin_transaction(tx_id.clone()).await?;
874
875        Ok(tx_id)
876    }
877
878    /// Commit a distributed transaction
879    pub async fn commit_transaction(&self, tx_id: &str) -> Result<RdfResponse> {
880        if !self.is_leader().await {
881            return Err(ClusterError::NotLeader);
882        }
883
884        let response = self.consensus.commit_transaction(tx_id.to_string()).await?;
885        Ok(response)
886    }
887
888    /// Rollback a distributed transaction
889    pub async fn rollback_transaction(&self, tx_id: &str) -> Result<RdfResponse> {
890        if !self.is_leader().await {
891            return Err(ClusterError::NotLeader);
892        }
893
894        let response = self
895            .consensus
896            .rollback_transaction(tx_id.to_string())
897            .await?;
898        Ok(response)
899    }
900
901    /// Query triples (can be done on any node)
902    pub async fn query_triples(
903        &self,
904        subject: Option<&str>,
905        predicate: Option<&str>,
906        object: Option<&str>,
907    ) -> Vec<(String, String, String)> {
908        self.consensus.query(subject, predicate, object).await
909    }
910
911    /// Execute SPARQL query using distributed query processing
912    pub async fn query_sparql(&self, sparql: &str) -> Result<Vec<String>> {
913        let bindings = self
914            .query_executor
915            .execute_query(sparql)
916            .await
917            .map_err(|e| ClusterError::Other(format!("Query execution failed: {e}")))?;
918
919        // Convert result bindings to string format
920        let results = bindings
921            .into_iter()
922            .map(|binding| {
923                let vars: Vec<String> = binding
924                    .variables
925                    .into_iter()
926                    .map(|(var, val)| format!("{var}: {val}"))
927                    .collect();
928                vars.join(", ")
929            })
930            .collect();
931
932        Ok(results)
933    }
934
935    /// Execute SPARQL query and return structured results
936    pub async fn query_sparql_bindings(&self, sparql: &str) -> Result<Vec<ResultBinding>> {
937        self.query_executor
938            .execute_query(sparql)
939            .await
940            .map_err(|e| ClusterError::Other(format!("Query execution failed: {e}")))
941    }
942
943    /// Get query execution statistics
944    pub async fn get_query_statistics(
945        &self,
946    ) -> Result<std::collections::HashMap<String, distributed_query::QueryStats>> {
947        Ok(self.query_executor.get_statistics().await)
948    }
949
950    /// Clear query cache
951    pub async fn clear_query_cache(&self) -> Result<()> {
952        self.query_executor.clear_cache().await;
953        Ok(())
954    }
955
956    /// Get the number of triples in the store
957    pub async fn len(&self) -> usize {
958        self.consensus.len().await
959    }
960
961    /// Check if the store is empty
962    pub async fn is_empty(&self) -> bool {
963        self.consensus.is_empty().await
964    }
965
966    /// Add a new node to the cluster
967    pub async fn add_cluster_node(
968        &mut self,
969        node_id: OxirsNodeId,
970        address: SocketAddr,
971    ) -> Result<()> {
972        if node_id == self.config.node_id {
973            return Err(ClusterError::Config(
974                "Cannot add self to cluster".to_string(),
975            ));
976        }
977
978        // Add to configuration
979        self.config.add_peer(node_id);
980
981        // Add to discovery
982        let node_info = NodeInfo::new(node_id, address);
983        self.discovery.add_node(node_info);
984
985        // Add to replication
986        self.replication.add_replica(node_id, address.to_string());
987
988        // Add to query executor
989        self.query_executor.add_node(node_id).await;
990
991        // Add to consensus (this would trigger Raft membership change)
992        self.consensus.add_peer(node_id);
993
994        tracing::info!("Added node {} at {} to cluster", node_id, address);
995
996        Ok(())
997    }
998
999    /// Remove a node from the cluster
1000    pub async fn remove_cluster_node(&mut self, node_id: OxirsNodeId) -> Result<()> {
1001        if node_id == self.config.node_id {
1002            return Err(ClusterError::Config(
1003                "Cannot remove self from cluster".to_string(),
1004            ));
1005        }
1006
1007        // Remove from configuration
1008        self.config.peers.retain(|&id| id != node_id);
1009
1010        // Remove from discovery
1011        self.discovery.remove_node(node_id);
1012
1013        // Remove from replication
1014        self.replication.remove_replica(node_id);
1015
1016        // Remove from query executor
1017        self.query_executor.remove_node(node_id).await;
1018
1019        // Remove from consensus (this would trigger Raft membership change)
1020        self.consensus.remove_peer(node_id);
1021
1022        tracing::info!("Removed node {} from cluster", node_id);
1023
1024        Ok(())
1025    }
1026
1027    /// Get comprehensive cluster status
1028    pub async fn get_status(&self) -> ClusterStatus {
1029        let consensus_status = self.consensus.get_status().await;
1030        let discovery_stats = self.discovery.get_stats().clone();
1031        let replication_stats = self.replication.get_stats().clone();
1032
1033        // Get region status if multi-region is enabled
1034        let region_status = if let Some(region_manager) = &self.region_manager {
1035            let region_id = region_manager.get_local_region().to_string();
1036            let availability_zone_id = region_manager.get_local_availability_zone().to_string();
1037            let regional_peers = region_manager.get_nodes_in_region(&region_id).await;
1038            let topology = region_manager.get_topology().await;
1039            let monitoring_active = region_manager.is_monitoring_active().await;
1040
1041            Some(RegionStatus {
1042                region_id,
1043                availability_zone_id,
1044                regional_peer_count: regional_peers.len(),
1045                total_regions: topology.regions.len(),
1046                monitoring_active,
1047            })
1048        } else {
1049            None
1050        };
1051
1052        ClusterStatus {
1053            node_id: self.config.node_id,
1054            address: self.config.address,
1055            is_leader: consensus_status.is_leader,
1056            current_term: consensus_status.current_term,
1057            peer_count: consensus_status.peer_count,
1058            triple_count: consensus_status.triple_count,
1059            discovery_stats,
1060            replication_stats,
1061            is_running: *self.running.read().await,
1062            region_status,
1063        }
1064    }
1065
1066    /// Start background maintenance tasks
1067    async fn start_background_tasks(&mut self) {
1068        let running = Arc::clone(&self.running);
1069
1070        // Discovery and health check task
1071        let discovery_config = self.config.discovery.clone().unwrap_or_default();
1072        let mut discovery_clone =
1073            DiscoveryService::new(self.config.node_id, self.config.address, discovery_config);
1074
1075        tokio::spawn(async move {
1076            while *running.read().await {
1077                discovery_clone.run_periodic_tasks().await;
1078                tokio::time::sleep(std::time::Duration::from_secs(1)).await;
1079            }
1080        });
1081
1082        // Replication maintenance task
1083        let mut replication_clone = ReplicationManager::with_raft_consensus(self.config.node_id);
1084        let running_clone = Arc::clone(&self.running);
1085
1086        tokio::spawn(async move {
1087            if *running_clone.read().await {
1088                replication_clone.run_maintenance().await; // run_maintenance() is infinite loop
1089            }
1090        });
1091    }
1092
1093    /// Add a new node to the cluster using consensus protocol
1094    pub async fn add_node_with_consensus(
1095        &mut self,
1096        node_id: OxirsNodeId,
1097        address: SocketAddr,
1098    ) -> Result<()> {
1099        self.consensus
1100            .add_node_with_consensus(node_id, address.to_string())
1101            .await
1102            .map_err(|e| {
1103                ClusterError::Other(format!("Failed to add node through consensus: {e}"))
1104            })?;
1105
1106        // Update local configuration
1107        self.config.add_peer(node_id);
1108
1109        // Add to discovery, replication, and query executor
1110        let node_info = NodeInfo::new(node_id, address);
1111        self.discovery.add_node(node_info);
1112        self.replication.add_replica(node_id, address.to_string());
1113        self.query_executor.add_node(node_id).await;
1114
1115        Ok(())
1116    }
1117
1118    /// Remove a node from the cluster using consensus protocol
1119    pub async fn remove_node_with_consensus(&mut self, node_id: OxirsNodeId) -> Result<()> {
1120        self.consensus
1121            .remove_node_with_consensus(node_id)
1122            .await
1123            .map_err(|e| {
1124                ClusterError::Other(format!("Failed to remove node through consensus: {e}"))
1125            })?;
1126
1127        // Update local configuration
1128        self.config.peers.retain(|&id| id != node_id);
1129
1130        // Remove from discovery, replication, and query executor
1131        self.discovery.remove_node(node_id);
1132        self.replication.remove_replica(node_id);
1133        self.query_executor.remove_node(node_id).await;
1134
1135        Ok(())
1136    }
1137
1138    /// Gracefully shutdown this node
1139    pub async fn graceful_shutdown(&mut self) -> Result<()> {
1140        tracing::info!(
1141            "Initiating graceful shutdown of cluster node {}",
1142            self.config.node_id
1143        );
1144
1145        // Stop background tasks first
1146        {
1147            let mut running = self.running.write().await;
1148            *running = false;
1149        }
1150
1151        // Gracefully shutdown consensus layer (includes leadership transfer if needed)
1152        self.consensus
1153            .graceful_shutdown()
1154            .await
1155            .map_err(|e| ClusterError::Other(format!("Failed to shutdown consensus: {e}")))?;
1156
1157        // Stop discovery and replication services
1158        self.discovery
1159            .stop()
1160            .await
1161            .map_err(|e| ClusterError::Other(format!("Failed to stop discovery: {e}")))?;
1162
1163        tracing::info!("Cluster node {} gracefully shutdown", self.config.node_id);
1164        Ok(())
1165    }
1166
1167    /// Transfer leadership to another node
1168    pub async fn transfer_leadership(&mut self, target_node: OxirsNodeId) -> Result<()> {
1169        if !self.config.peers.contains(&target_node) {
1170            return Err(ClusterError::Config(format!(
1171                "Target node {target_node} not in cluster"
1172            )));
1173        }
1174
1175        self.consensus
1176            .transfer_leadership(target_node)
1177            .await
1178            .map_err(|e| ClusterError::Other(format!("Failed to transfer leadership: {e}")))?;
1179
1180        Ok(())
1181    }
1182
1183    /// Force evict a non-responsive node
1184    pub async fn force_evict_node(&mut self, node_id: OxirsNodeId) -> Result<()> {
1185        self.consensus
1186            .force_evict_node(node_id)
1187            .await
1188            .map_err(|e| ClusterError::Other(format!("Failed to force evict node: {e}")))?;
1189
1190        // Update local configuration
1191        self.config.peers.retain(|&id| id != node_id);
1192        self.discovery.remove_node(node_id);
1193        self.replication.remove_replica(node_id);
1194        self.query_executor.remove_node(node_id).await;
1195
1196        Ok(())
1197    }
1198
1199    /// Check health of all peer nodes
1200    pub async fn check_cluster_health(&self) -> Result<Vec<consensus::NodeHealthStatus>> {
1201        self.consensus
1202            .check_peer_health()
1203            .await
1204            .map_err(|e| ClusterError::Other(format!("Failed to check cluster health: {e}")))
1205    }
1206
1207    /// Attempt recovery from partition or failure
1208    pub async fn attempt_recovery(&mut self) -> Result<()> {
1209        self.consensus
1210            .attempt_recovery()
1211            .await
1212            .map_err(|e| ClusterError::Other(format!("Failed to recover cluster: {e}")))?;
1213
1214        tracing::info!(
1215            "Cluster recovery completed for node {}",
1216            self.config.node_id
1217        );
1218        Ok(())
1219    }
1220
1221    /// Get the node ID
1222    pub fn id(&self) -> OxirsNodeId {
1223        self.config.node_id
1224    }
1225
1226    /// Count triples in the store
1227    pub async fn count_triples(&self) -> Result<usize> {
1228        Ok(self.len().await)
1229    }
1230
1231    /// Check if the node is active (running and not isolated)
1232    pub async fn is_active(&self) -> Result<bool> {
1233        Ok(*self.running.read().await && !*self.network_isolated.read().await)
1234    }
1235
1236    /// Isolate the node from network (simulate network partition)
1237    pub async fn isolate_network(&self) -> Result<()> {
1238        let mut isolated = self.network_isolated.write().await;
1239        *isolated = true;
1240        tracing::info!("Node {} network isolated", self.config.node_id);
1241        Ok(())
1242    }
1243
1244    /// Restore network connectivity
1245    pub async fn restore_network(&self) -> Result<()> {
1246        let mut isolated = self.network_isolated.write().await;
1247        *isolated = false;
1248        tracing::info!("Node {} network restored", self.config.node_id);
1249        Ok(())
1250    }
1251
1252    /// Enable Byzantine behavior (for testing)
1253    pub async fn enable_byzantine_mode(&self) -> Result<()> {
1254        let mut byzantine = self.byzantine_mode.write().await;
1255        *byzantine = true;
1256        tracing::info!("Node {} Byzantine mode enabled", self.config.node_id);
1257        Ok(())
1258    }
1259
1260    /// Check if node is in Byzantine mode
1261    pub async fn is_byzantine(&self) -> Result<bool> {
1262        Ok(*self.byzantine_mode.read().await)
1263    }
1264
1265    /// Get multi-region manager (if configured)
1266    pub fn region_manager(&self) -> Option<&Arc<RegionManager>> {
1267        self.region_manager.as_ref()
1268    }
1269
1270    /// Check if multi-region deployment is enabled
1271    pub fn is_multi_region_enabled(&self) -> bool {
1272        self.region_manager.is_some()
1273    }
1274
1275    /// Get current node's region ID
1276    pub fn get_region_id(&self) -> Option<String> {
1277        self.region_manager
1278            .as_ref()
1279            .map(|rm| rm.get_local_region().to_string())
1280    }
1281
1282    /// Get current node's availability zone ID
1283    pub fn get_availability_zone_id(&self) -> Option<String> {
1284        self.region_manager
1285            .as_ref()
1286            .map(|rm| rm.get_local_availability_zone().to_string())
1287    }
1288
1289    /// Get nodes in the same region
1290    pub async fn get_regional_peers(&self) -> Result<Vec<OxirsNodeId>> {
1291        if let Some(region_manager) = &self.region_manager {
1292            let region_id = region_manager.get_local_region();
1293            Ok(region_manager.get_nodes_in_region(region_id).await)
1294        } else {
1295            Err(ClusterError::Config(
1296                "Multi-region not configured".to_string(),
1297            ))
1298        }
1299    }
1300
1301    /// Get optimal leader candidates considering region affinity
1302    pub async fn get_regional_leader_candidates(&self) -> Result<Vec<OxirsNodeId>> {
1303        if let Some(region_manager) = &self.region_manager {
1304            let region_id = region_manager.get_local_region();
1305            Ok(region_manager.get_leader_candidates(region_id).await)
1306        } else {
1307            // Fall back to regular peer list
1308            Ok(self.config.peers.clone())
1309        }
1310    }
1311
1312    /// Calculate cross-region replication targets
1313    pub async fn get_cross_region_replication_targets(&self) -> Result<Vec<String>> {
1314        if let Some(region_manager) = &self.region_manager {
1315            let region_id = region_manager.get_local_region();
1316            region_manager
1317                .calculate_replication_targets(region_id)
1318                .await
1319                .map_err(|e| {
1320                    ClusterError::Other(format!("Failed to calculate replication targets: {e}"))
1321                })
1322        } else {
1323            Ok(Vec::new())
1324        }
1325    }
1326
1327    /// Monitor inter-region latencies and update metrics
1328    pub async fn monitor_region_latencies(&self) -> Result<()> {
1329        if let Some(region_manager) = &self.region_manager {
1330            region_manager.monitor_latencies().await.map_err(|e| {
1331                ClusterError::Other(format!("Failed to monitor region latencies: {e}"))
1332            })
1333        } else {
1334            Ok(())
1335        }
1336    }
1337
1338    /// Get region health status
1339    pub async fn get_region_health(&self, region_id: &str) -> Result<region_manager::RegionHealth> {
1340        if let Some(region_manager) = &self.region_manager {
1341            region_manager
1342                .get_region_health(region_id)
1343                .await
1344                .map_err(|e| ClusterError::Other(format!("Failed to get region health: {e}")))
1345        } else {
1346            Err(ClusterError::Config(
1347                "Multi-region not configured".to_string(),
1348            ))
1349        }
1350    }
1351
1352    /// Perform region failover operation
1353    pub async fn perform_region_failover(
1354        &self,
1355        failed_region: &str,
1356        target_region: &str,
1357    ) -> Result<()> {
1358        if let Some(region_manager) = &self.region_manager {
1359            region_manager
1360                .perform_region_failover(failed_region, target_region)
1361                .await
1362                .map_err(|e| ClusterError::Other(format!("Failed to perform region failover: {e}")))
1363        } else {
1364            Err(ClusterError::Config(
1365                "Multi-region not configured".to_string(),
1366            ))
1367        }
1368    }
1369
1370    /// Get multi-region topology information
1371    pub async fn get_region_topology(&self) -> Result<region_manager::RegionTopology> {
1372        if let Some(region_manager) = &self.region_manager {
1373            Ok(region_manager.get_topology().await)
1374        } else {
1375            Err(ClusterError::Config(
1376                "Multi-region not configured".to_string(),
1377            ))
1378        }
1379    }
1380
1381    /// Add a node to a specific region and availability zone
1382    pub async fn add_node_to_region(
1383        &self,
1384        node_id: OxirsNodeId,
1385        region_id: String,
1386        availability_zone_id: String,
1387        data_center: Option<String>,
1388        rack: Option<String>,
1389    ) -> Result<()> {
1390        if let Some(region_manager) = &self.region_manager {
1391            region_manager
1392                .register_node(node_id, region_id, availability_zone_id, data_center, rack)
1393                .await
1394                .map_err(|e| ClusterError::Other(format!("Failed to add node to region: {e}")))
1395        } else {
1396            Err(ClusterError::Config(
1397                "Multi-region not configured".to_string(),
1398            ))
1399        }
1400    }
1401
1402    /// Get conflict resolver instance
1403    pub fn conflict_resolver(&self) -> &Arc<ConflictResolver> {
1404        &self.conflict_resolver
1405    }
1406
1407    /// Get current vector clock value
1408    pub async fn get_vector_clock(&self) -> VectorClock {
1409        self.local_vector_clock.read().await.clone()
1410    }
1411
1412    /// Update vector clock with received clock
1413    pub async fn update_vector_clock(&self, received_clock: &VectorClock) {
1414        let mut clock = self.local_vector_clock.write().await;
1415        clock.update(received_clock);
1416        clock.increment(self.config.node_id);
1417    }
1418
1419    /// Create a timestamped operation with current vector clock
1420    pub async fn create_timestamped_operation(
1421        &self,
1422        operation: conflict_resolution::RdfOperation,
1423        priority: u32,
1424    ) -> TimestampedOperation {
1425        let mut clock = self.local_vector_clock.write().await;
1426        clock.increment(self.config.node_id);
1427
1428        TimestampedOperation {
1429            operation_id: uuid::Uuid::new_v4().to_string(),
1430            origin_node: self.config.node_id,
1431            vector_clock: clock.clone(),
1432            physical_time: std::time::SystemTime::now(),
1433            operation,
1434            priority,
1435        }
1436    }
1437
1438    /// Detect conflicts in a batch of operations
1439    pub async fn detect_operation_conflicts(
1440        &self,
1441        operations: &[TimestampedOperation],
1442    ) -> Result<Vec<conflict_resolution::ConflictType>> {
1443        self.conflict_resolver
1444            .detect_conflicts(operations)
1445            .await
1446            .map_err(|e| ClusterError::Other(format!("Failed to detect conflicts: {e}")))
1447    }
1448
1449    /// Resolve conflicts using configured strategies
1450    pub async fn resolve_operation_conflicts(
1451        &self,
1452        conflicts: &[conflict_resolution::ConflictType],
1453    ) -> Result<Vec<conflict_resolution::ResolutionResult>> {
1454        self.conflict_resolver
1455            .resolve_conflicts(conflicts)
1456            .await
1457            .map_err(|e| ClusterError::Other(format!("Failed to resolve conflicts: {e}")))
1458    }
1459
1460    /// Submit an operation for conflict-aware processing
1461    pub async fn submit_conflict_aware_operation(
1462        &self,
1463        operation: conflict_resolution::RdfOperation,
1464        priority: u32,
1465    ) -> Result<RdfResponse> {
1466        // Create timestamped operation
1467        let _timestamped_op = self
1468            .create_timestamped_operation(operation.clone(), priority)
1469            .await;
1470
1471        // For now, submit to consensus without conflict detection
1472        // In a full implementation, this would be integrated with the consensus layer
1473        match operation {
1474            conflict_resolution::RdfOperation::Insert {
1475                subject,
1476                predicate,
1477                object,
1478                ..
1479            } => self.insert_triple(&subject, &predicate, &object).await,
1480            conflict_resolution::RdfOperation::Delete {
1481                subject,
1482                predicate,
1483                object,
1484                ..
1485            } => self.delete_triple(&subject, &predicate, &object).await,
1486            conflict_resolution::RdfOperation::Clear { .. } => self.clear_store().await,
1487            conflict_resolution::RdfOperation::Update {
1488                old_triple,
1489                new_triple,
1490                ..
1491            } => {
1492                // Implement as delete + insert
1493                let _delete_result = self
1494                    .delete_triple(&old_triple.0, &old_triple.1, &old_triple.2)
1495                    .await?;
1496                self.insert_triple(&new_triple.0, &new_triple.1, &new_triple.2)
1497                    .await
1498            }
1499            conflict_resolution::RdfOperation::Batch { operations: _ } => {
1500                // Process batch operations sequentially
1501                // Note: This is a simplified implementation that doesn't use recursion
1502                // In a full implementation, each operation would be processed individually
1503                // For now, just return success for batch operations
1504                Ok(RdfResponse::Success)
1505            }
1506        }
1507    }
1508
1509    /// Get conflict resolution statistics
1510    pub async fn get_conflict_resolution_statistics(
1511        &self,
1512    ) -> conflict_resolution::ResolutionStatistics {
1513        self.conflict_resolver.get_statistics().await
1514    }
1515}
1516
1517/// Comprehensive cluster status information
1518#[derive(Debug, Clone)]
1519pub struct ClusterStatus {
1520    /// Local node ID
1521    pub node_id: OxirsNodeId,
1522    /// Local node address
1523    pub address: SocketAddr,
1524    /// Whether this node is the current leader
1525    pub is_leader: bool,
1526    /// Current Raft term
1527    pub current_term: u64,
1528    /// Number of peer nodes
1529    pub peer_count: usize,
1530    /// Number of triples in the store
1531    pub triple_count: usize,
1532    /// Discovery service statistics
1533    pub discovery_stats: discovery::DiscoveryStats,
1534    /// Replication statistics
1535    pub replication_stats: ReplicationStats,
1536    /// Whether the node is currently running
1537    pub is_running: bool,
1538    /// Multi-region status (if enabled)
1539    pub region_status: Option<RegionStatus>,
1540}
1541
1542/// Multi-region status information
1543#[derive(Debug, Clone)]
1544pub struct RegionStatus {
1545    /// Current region ID
1546    pub region_id: String,
1547    /// Current availability zone ID
1548    pub availability_zone_id: String,
1549    /// Number of nodes in the same region
1550    pub regional_peer_count: usize,
1551    /// Total number of regions in topology
1552    pub total_regions: usize,
1553    /// Whether multi-region monitoring is active
1554    pub monitoring_active: bool,
1555}
1556
1557/// Distributed RDF store (simplified interface)
1558pub struct DistributedStore {
1559    node: ClusterNode,
1560}
1561
1562impl DistributedStore {
1563    /// Create a new distributed store
1564    pub async fn new(config: NodeConfig) -> Result<Self> {
1565        let node = ClusterNode::new(config).await?;
1566        Ok(Self { node })
1567    }
1568
1569    /// Start the distributed store
1570    pub async fn start(&mut self) -> Result<()> {
1571        self.node.start().await
1572    }
1573
1574    /// Stop the distributed store
1575    pub async fn stop(&mut self) -> Result<()> {
1576        self.node.stop().await
1577    }
1578
1579    /// Insert a triple (only on leader)
1580    pub async fn insert_triple(
1581        &mut self,
1582        subject: &str,
1583        predicate: &str,
1584        object: &str,
1585    ) -> Result<()> {
1586        let _response = self.node.insert_triple(subject, predicate, object).await?;
1587        Ok(())
1588    }
1589
1590    /// Query triples using SPARQL
1591    pub async fn query_sparql(&self, sparql: &str) -> Result<Vec<String>> {
1592        self.node.query_sparql(sparql).await
1593    }
1594
1595    /// Query triples by pattern
1596    pub async fn query_pattern(
1597        &self,
1598        subject: Option<&str>,
1599        predicate: Option<&str>,
1600        object: Option<&str>,
1601    ) -> Vec<(String, String, String)> {
1602        self.node.query_triples(subject, predicate, object).await
1603    }
1604
1605    /// Get cluster status
1606    pub async fn get_status(&self) -> ClusterStatus {
1607        self.node.get_status().await
1608    }
1609}
1610
1611/// Re-export commonly used types
1612pub use consensus::ConsensusError;
1613pub use discovery::DiscoveryError;
1614pub use replication::ReplicationError;
1615
1616#[cfg(test)]
1617mod tests {
1618    use super::*;
1619    use std::net::{IpAddr, Ipv4Addr};
1620
1621    #[tokio::test]
1622    async fn test_node_config_creation() {
1623        let addr = SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)), 8080);
1624        let config = NodeConfig::new(1, addr);
1625
1626        assert_eq!(config.node_id, 1);
1627        assert_eq!(config.address, addr);
1628        assert_eq!(config.data_dir, "./data/node-1");
1629        assert!(config.peers.is_empty());
1630        assert!(config.discovery.is_some());
1631        assert!(config.replication_strategy.is_some());
1632        assert!(config.region_config.is_none());
1633    }
1634
1635    #[tokio::test]
1636    async fn test_node_config_add_peer() {
1637        let addr = SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)), 8080);
1638        let mut config = NodeConfig::new(1, addr);
1639
1640        config.add_peer(2);
1641        config.add_peer(3);
1642        config.add_peer(2); // Duplicate should be ignored
1643
1644        assert_eq!(config.peers, vec![2, 3]);
1645    }
1646
1647    #[tokio::test]
1648    async fn test_node_config_no_self_peer() {
1649        let addr = SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)), 8080);
1650        let mut config = NodeConfig::new(1, addr);
1651
1652        config.add_peer(1); // Should not add self
1653
1654        assert!(config.peers.is_empty());
1655    }
1656
1657    #[tokio::test]
1658    async fn test_cluster_node_creation() {
1659        let addr = SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)), 8080);
1660        let config = NodeConfig::new(1, addr);
1661
1662        let node = ClusterNode::new(config).await;
1663        assert!(node.is_ok());
1664
1665        let node = node.unwrap();
1666        assert_eq!(node.config.node_id, 1);
1667        assert_eq!(node.config.address, addr);
1668    }
1669
1670    #[tokio::test]
1671    async fn test_cluster_node_empty_data_dir_error() {
1672        let addr = SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)), 8080);
1673        let mut config = NodeConfig::new(1, addr);
1674        config.data_dir = String::new();
1675
1676        let result = ClusterNode::new(config).await;
1677        assert!(result.is_err());
1678        if let Err(e) = result {
1679            assert!(e.to_string().contains("Data directory cannot be empty"));
1680        }
1681    }
1682
1683    #[tokio::test]
1684    async fn test_distributed_store_creation() {
1685        let addr = SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)), 8080);
1686        let config = NodeConfig::new(1, addr);
1687
1688        let store = DistributedStore::new(config).await;
1689        assert!(store.is_ok());
1690    }
1691
1692    #[test]
1693    fn test_cluster_error_types() {
1694        let err = ClusterError::Config("test error".to_string());
1695        assert!(err.to_string().contains("Configuration error: test error"));
1696
1697        let err = ClusterError::NotLeader;
1698        assert_eq!(err.to_string(), "Not the leader node");
1699
1700        let err = ClusterError::Network("connection failed".to_string());
1701        assert!(err.to_string().contains("Network error: connection failed"));
1702    }
1703}