1use std::collections::{HashMap, HashSet};
17use std::hash::Hash;
18use std::net::SocketAddr;
19use std::sync::{Arc, Mutex, RwLock};
20use std::thread;
21use std::time::{Duration, Instant};
22
23pub type MessageHandlerFn =
25 Box<dyn Fn(&DistributedMessage) -> Result<MessageResponse, DistributedError> + Send + Sync>;
26
27#[derive(Debug, Clone)]
28pub struct ResourceReservation {
29 pub id: String,
30 pub node_id: String,
31 pub start_time: Instant,
32 pub duration: Duration,
33 pub resources: ResourceAllocation,
34}
35#[derive(Debug, Clone, PartialEq)]
36pub enum ConsensusState {
37 Follower,
38 Candidate,
39 Leader,
40}
41#[derive(Debug, Clone)]
43pub struct ClusterConfig {
44 pub heartbeat_interval: Duration,
45 pub node_timeout: Duration,
46 pub job_timeout: Duration,
47 pub max_retries: u32,
48 pub load_threshold: f64,
49 pub replication_factor: u32,
50}
51#[derive(Debug, Clone)]
52pub enum MessagePriority {
53 Low,
54 Normal,
55 High,
56 Critical,
57}
58#[derive(Debug, Clone)]
59pub struct MessageResponse {
60 pub message_id: String,
61 pub success: bool,
62 pub data: Vec<u8>,
63 pub error: Option<String>,
64}
65#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
67pub enum JobPriority {
68 Low,
69 Normal,
70 High,
71 Critical,
72}
73#[derive(Debug, Clone, PartialEq)]
75pub enum JobStatus {
76 Pending,
77 Running,
78 Completed,
79 Failed,
80 Cancelled,
81 Timeout,
82}
83#[derive(Debug, Clone)]
84pub struct SchedulingDecision {
85 pub job_id: String,
86 pub node_id: String,
87 pub estimated_start_time: Instant,
88 pub resource_allocation: ResourceAllocation,
89}
90#[derive(Debug, thiserror::Error)]
92pub enum DistributedError {
93 #[error("Node not found")]
94 NodeNotFound,
95 #[error("Job not found")]
96 JobNotFound,
97 #[error("Insufficient resources")]
98 InsufficientResources,
99 #[error("Node unreachable")]
100 NodeUnreachable,
101 #[error("Job timeout")]
102 JobTimeout,
103 #[error("Scheduling error: {0}")]
104 SchedulingError(String),
105 #[error("Communication error: {0}")]
106 CommunicationError(String),
107}
108#[derive(Debug, Clone)]
109pub struct VoteRequest {
110 pub term: u64,
111 pub candidate_id: String,
112 pub last_log_index: usize,
113 pub last_log_term: u64,
114}
115#[derive(Debug, Clone)]
116pub struct VoteResponse {
117 pub term: u64,
118 pub vote_granted: bool,
119}
120#[allow(dead_code)]
123pub struct MessagePassingSystem {
124 node_id: String,
125 message_handlers: HashMap<String, MessageHandler>,
126 pending_messages: Arc<Mutex<Vec<DistributedMessage>>>,
127 pub(crate) message_queue: Arc<Mutex<Vec<DistributedMessage>>>,
128 pub(crate) routing_table: Arc<RwLock<HashMap<String, SocketAddr>>>,
129}
130impl MessagePassingSystem {
131 pub fn new(node_id: String) -> Self {
133 Self {
134 node_id,
135 message_handlers: HashMap::new(),
136 pending_messages: Arc::new(Mutex::new(Vec::new())),
137 message_queue: Arc::new(Mutex::new(Vec::new())),
138 routing_table: Arc::new(RwLock::new(HashMap::new())),
139 }
140 }
141 pub fn send_message(&self, message: DistributedMessage) -> Result<(), DistributedError> {
143 let routing_table = self.routing_table.read().expect("operation should succeed");
144 if let Some(_address) = routing_table.get(&message.destination) {
145 self.message_queue
146 .lock()
147 .expect("operation should succeed")
148 .push(message);
149 Ok(())
150 } else {
151 Err(DistributedError::NodeUnreachable)
152 }
153 }
154 pub fn broadcast_message(
156 &self,
157 message_type: MessageType,
158 data: Vec<u8>,
159 ) -> Result<(), DistributedError> {
160 let routing_table = self.routing_table.read().expect("operation should succeed");
161 for node_id in routing_table.keys() {
162 if node_id != &self.node_id {
163 let message = DistributedMessage {
164 id: format!("{}_{}", self.node_id, Instant::now().elapsed().as_millis()),
165 source: self.node_id.clone(),
166 destination: node_id.clone(),
167 message_type: message_type.clone(),
168 data: data.clone(),
169 timestamp: Instant::now(),
170 priority: MessagePriority::Normal,
171 };
172 self.send_message(message)?;
173 }
174 }
175 Ok(())
176 }
177 pub fn register_handler(&mut self, message_type: String, handler: MessageHandler) {
179 self.message_handlers.insert(message_type, handler);
180 }
181 pub fn process_messages(&self) -> Result<Vec<MessageResponse>, DistributedError> {
183 let mut responses = Vec::new();
184 let mut queue = self.message_queue.lock().expect("operation should succeed");
185 for message in queue.drain(..) {
186 if let Some(handler) = self
187 .message_handlers
188 .get(&format!("{}", message.message_type))
189 {
190 let response = handler.handle(&message)?;
191 responses.push(response);
192 }
193 }
194 Ok(responses)
195 }
196}
197#[derive(Debug, Clone)]
198pub struct PeerInfo {
199 pub id: String,
200 pub next_index: usize,
201 pub match_index: usize,
202 pub last_response: Instant,
203}
204#[allow(dead_code)]
206pub struct AdvancedJobScheduler {
207 scheduling_policies: Vec<SchedulingPolicy>,
208 pub(crate) resource_reservations: HashMap<String, ResourceReservation>,
209 job_dependencies: HashMap<String, Vec<String>>,
210 priority_queues: HashMap<JobPriority, Vec<DistributedJob>>,
211 backfill_enabled: bool,
212}
213impl AdvancedJobScheduler {
214 pub fn new() -> Self {
216 Self {
217 scheduling_policies: vec![
218 SchedulingPolicy::FIFO,
219 SchedulingPolicy::ShortestJobFirst,
220 SchedulingPolicy::GangScheduling,
221 ],
222 resource_reservations: HashMap::new(),
223 job_dependencies: HashMap::new(),
224 priority_queues: HashMap::new(),
225 backfill_enabled: true,
226 }
227 }
228 pub fn gang_schedule(
230 &mut self,
231 jobs: &[DistributedJob],
232 nodes: &HashMap<String, ClusterNode>,
233 ) -> Result<Vec<SchedulingDecision>, DistributedError> {
234 let mut decisions = Vec::new();
235 let job_groups = self.group_related_jobs(jobs);
236 for group in job_groups {
237 if let Some(node_assignment) = self.find_gang_assignment(&group, nodes) {
238 for (job, node_id) in group.iter().zip(node_assignment.iter()) {
239 decisions.push(SchedulingDecision {
240 job_id: job.id.clone(),
241 node_id: node_id.clone(),
242 estimated_start_time: Instant::now(),
243 resource_allocation: self
244 .calculate_resource_allocation(job, node_id, nodes),
245 });
246 }
247 }
248 }
249 Ok(decisions)
250 }
251 pub fn backfill_schedule(
253 &mut self,
254 waiting_jobs: &[DistributedJob],
255 nodes: &HashMap<String, ClusterNode>,
256 ) -> Result<Vec<SchedulingDecision>, DistributedError> {
257 let mut decisions = Vec::new();
258 if !self.backfill_enabled {
259 return Ok(decisions);
260 }
261 for job in waiting_jobs {
262 for (node_id, node) in nodes {
263 if self.can_backfill_job(job, node) {
264 decisions.push(SchedulingDecision {
265 job_id: job.id.clone(),
266 node_id: node_id.clone(),
267 estimated_start_time: Instant::now(),
268 resource_allocation: self
269 .calculate_resource_allocation(job, node_id, nodes),
270 });
271 break;
272 }
273 }
274 }
275 Ok(decisions)
276 }
277 pub fn reserve_resources(
279 &mut self,
280 reservation: ResourceReservation,
281 ) -> Result<(), DistributedError> {
282 self.resource_reservations
283 .insert(reservation.id.clone(), reservation);
284 Ok(())
285 }
286 fn group_related_jobs(&self, jobs: &[DistributedJob]) -> Vec<Vec<DistributedJob>> {
287 vec![jobs.to_vec()]
288 }
289 fn find_gang_assignment(
290 &self,
291 job_group: &[DistributedJob],
292 nodes: &HashMap<String, ClusterNode>,
293 ) -> Option<Vec<String>> {
294 if job_group.len() <= nodes.len() {
295 Some(nodes.keys().take(job_group.len()).cloned().collect())
296 } else {
297 None
298 }
299 }
300 fn can_backfill_job(&self, _job: &DistributedJob, _node: &ClusterNode) -> bool {
301 true
302 }
303 fn calculate_resource_allocation(
304 &self,
305 job: &DistributedJob,
306 _node_id: &str,
307 _nodes: &HashMap<String, ClusterNode>,
308 ) -> ResourceAllocation {
309 ResourceAllocation {
310 cpu_cores: job.requirements.min_cpu_cores,
311 memory_gb: job.requirements.min_memory_gb,
312 gpu_count: job.requirements.min_gpu_count,
313 storage_gb: job.requirements.min_storage_gb,
314 network_bandwidth: 100,
315 }
316 }
317}
318#[derive(Debug, Clone)]
319pub struct PartitionMove {
320 pub partition: usize,
321 pub from_node: String,
322 pub to_node: String,
323}
324#[derive(Debug, Clone)]
325pub struct CheckpointStats {
326 pub total_checkpoints: usize,
327 pub total_size_bytes: u64,
328 pub compressed_checkpoints: usize,
329 pub compression_ratio: f64,
330}
331pub struct JobScheduler {
333 scheduling_strategy: SchedulingStrategy,
334}
335impl JobScheduler {
336 pub fn new() -> Self {
338 Self {
339 scheduling_strategy: SchedulingStrategy::LeastLoaded,
340 }
341 }
342 pub fn find_suitable_node(
344 &self,
345 job: &DistributedJob,
346 nodes: &HashMap<String, ClusterNode>,
347 ) -> Option<String> {
348 let suitable_nodes: Vec<_> = nodes
349 .values()
350 .filter(|node| self.can_run_job(node, job))
351 .collect();
352 if suitable_nodes.is_empty() {
353 return None;
354 }
355 match self.scheduling_strategy {
356 SchedulingStrategy::LeastLoaded => suitable_nodes
357 .iter()
358 .min_by(|a, b| {
359 let load_a = a.load_metrics.cpu_usage + a.load_metrics.memory_usage;
360 let load_b = b.load_metrics.cpu_usage + b.load_metrics.memory_usage;
361 load_a
362 .partial_cmp(&load_b)
363 .unwrap_or(std::cmp::Ordering::Equal)
364 })
365 .map(|node| node.id.clone()),
366 SchedulingStrategy::RoundRobin => suitable_nodes.first().map(|node| node.id.clone()),
367 SchedulingStrategy::HighestCapacity => suitable_nodes
368 .iter()
369 .max_by_key(|node| node.capabilities.cpu_cores * node.capabilities.memory_gb)
370 .map(|node| node.id.clone()),
371 }
372 }
373 fn can_run_job(&self, node: &ClusterNode, job: &DistributedJob) -> bool {
375 node.status == NodeStatus::Available
376 && node.capabilities.cpu_cores >= job.requirements.min_cpu_cores
377 && node.capabilities.memory_gb >= job.requirements.min_memory_gb
378 && node.capabilities.gpu_count >= job.requirements.min_gpu_count
379 && node.capabilities.storage_gb >= job.requirements.min_storage_gb
380 }
381}
382pub struct CheckpointManager {
384 pub(crate) checkpoint_storage: HashMap<String, Checkpoint>,
385 #[allow(dead_code)]
386 checkpoint_interval: Duration,
387 compression_enabled: bool,
388}
389impl CheckpointManager {
390 pub fn new(checkpoint_interval: Duration) -> Self {
392 Self {
393 checkpoint_storage: HashMap::new(),
394 checkpoint_interval,
395 compression_enabled: true,
396 }
397 }
398 pub fn create_checkpoint(
400 &mut self,
401 job_id: &str,
402 state: JobState,
403 ) -> Result<String, DistributedError> {
404 let checkpoint_id = format!("{job_id}_{}", Instant::now().elapsed().as_millis());
405 let checkpoint = Checkpoint {
406 id: checkpoint_id.clone(),
407 job_id: job_id.to_string(),
408 state,
409 created_at: Instant::now(),
410 size_bytes: 1024,
411 compressed: self.compression_enabled,
412 };
413 self.checkpoint_storage
414 .insert(checkpoint_id.clone(), checkpoint);
415 Ok(checkpoint_id)
416 }
417 pub fn restore_checkpoint(&self, checkpoint_id: &str) -> Result<JobState, DistributedError> {
419 self.checkpoint_storage
420 .get(checkpoint_id)
421 .map(|checkpoint| checkpoint.state.clone())
422 .ok_or(DistributedError::JobNotFound)
423 }
424 pub fn cleanup_old_checkpoints(&mut self, retention_period: Duration) {
426 let cutoff = Instant::now() - retention_period;
427 self.checkpoint_storage
428 .retain(|_, checkpoint| checkpoint.created_at > cutoff);
429 }
430 pub fn get_checkpoint_stats(&self) -> CheckpointStats {
432 let total_checkpoints = self.checkpoint_storage.len();
433 let total_size: u64 = self.checkpoint_storage.values().map(|c| c.size_bytes).sum();
434 let compressed_checkpoints = self
435 .checkpoint_storage
436 .values()
437 .filter(|c| c.compressed)
438 .count();
439 CheckpointStats {
440 total_checkpoints,
441 total_size_bytes: total_size,
442 compressed_checkpoints,
443 compression_ratio: if total_checkpoints > 0 {
444 compressed_checkpoints as f64 / total_checkpoints as f64
445 } else {
446 0.0
447 },
448 }
449 }
450}
451#[derive(Debug, Clone)]
453pub struct ClusterNode {
454 pub id: String,
455 pub address: SocketAddr,
456 pub capabilities: NodeCapabilities,
457 pub status: NodeStatus,
458 pub last_heartbeat: Instant,
459 pub load_metrics: LoadMetrics,
460 pub job_history: Vec<JobExecution>,
461}
462#[derive(Debug, Clone)]
463pub struct LogEntry {
464 pub term: u64,
465 pub index: usize,
466 pub command: String,
467 pub data: Vec<u8>,
468}
469#[derive(Debug, Clone)]
470pub struct DistributedMessage {
471 pub id: String,
472 pub source: String,
473 pub destination: String,
474 pub message_type: MessageType,
475 pub data: Vec<u8>,
476 pub timestamp: Instant,
477 pub priority: MessagePriority,
478}
479#[derive(Debug, Clone)]
480pub struct JobState {
481 pub progress: f64,
482 pub intermediate_results: HashMap<String, Vec<u8>>,
483 pub runtime_state: Vec<u8>,
484}
485pub struct DataPartitioner {
487 partitioning_strategy: PartitioningStrategy,
488 partition_count: usize,
489 node_assignments: HashMap<usize, String>,
490 replication_factor: usize,
491}
492impl DataPartitioner {
493 pub fn new(
495 strategy: PartitioningStrategy,
496 partition_count: usize,
497 replication_factor: usize,
498 ) -> Self {
499 Self {
500 partitioning_strategy: strategy,
501 partition_count,
502 node_assignments: HashMap::new(),
503 replication_factor,
504 }
505 }
506 pub fn get_partition(&self, key: &str) -> usize {
508 match self.partitioning_strategy {
509 PartitioningStrategy::Hash => {
510 use std::collections::hash_map::DefaultHasher;
511 use std::hash::Hasher;
512 let mut hasher = DefaultHasher::new();
513 key.hash(&mut hasher);
514 (hasher.finish() as usize) % self.partition_count
515 }
516 PartitioningStrategy::Range => key.len() % self.partition_count,
517 PartitioningStrategy::Random => key.len() % self.partition_count,
518 }
519 }
520 pub fn get_partition_nodes(&self, partition: usize) -> Vec<String> {
522 let mut nodes = Vec::new();
523 if let Some(primary_node) = self.node_assignments.get(&partition) {
524 nodes.push(primary_node.clone());
525 for i in 1..self.replication_factor {
526 let replica_partition = (partition + i) % self.partition_count;
527 if let Some(replica_node) = self.node_assignments.get(&replica_partition) {
528 if !nodes.contains(replica_node) {
529 nodes.push(replica_node.clone());
530 }
531 }
532 }
533 }
534 nodes
535 }
536 pub fn assign_partition(&mut self, partition: usize, node_id: String) {
538 self.node_assignments.insert(partition, node_id);
539 }
540 pub fn rebalance_partitions(&mut self, available_nodes: &[String]) -> PartitioningResult {
542 let mut assignments_changed = 0;
543 let mut partitions_moved = Vec::new();
544 for partition in 0..self.partition_count {
545 let optimal_node = &available_nodes[partition % available_nodes.len()];
546 if let Some(current_node) = self.node_assignments.get(&partition) {
547 if current_node != optimal_node {
548 partitions_moved.push(PartitionMove {
549 partition,
550 from_node: current_node.clone(),
551 to_node: optimal_node.clone(),
552 });
553 self.node_assignments
554 .insert(partition, optimal_node.clone());
555 assignments_changed += 1;
556 }
557 } else {
558 self.node_assignments
559 .insert(partition, optimal_node.clone());
560 assignments_changed += 1;
561 }
562 }
563 PartitioningResult {
564 assignments_changed,
565 partitions_moved,
566 rebalance_time: Instant::now(),
567 }
568 }
569}
570#[derive(Debug, Clone)]
572pub struct ClusterStats {
573 pub total_nodes: usize,
574 pub available_nodes: usize,
575 pub busy_nodes: usize,
576 pub overloaded_nodes: usize,
577 pub unreachable_nodes: usize,
578 pub total_jobs: usize,
579 pub running_jobs: usize,
580 pub completed_jobs: usize,
581 pub failed_jobs: usize,
582 pub queued_jobs: usize,
583 pub total_cpu_cores: u32,
584 pub total_memory_gb: u32,
585 pub total_gpu_count: u32,
586 pub avg_cpu_usage: f64,
587 pub avg_memory_usage: f64,
588}
589pub struct MessageHandler {
590 pub handler_fn: MessageHandlerFn,
591}
592impl MessageHandler {
593 pub fn new<F>(f: F) -> Self
594 where
595 F: Fn(&DistributedMessage) -> Result<MessageResponse, DistributedError>
596 + Send
597 + Sync
598 + 'static,
599 {
600 Self {
601 handler_fn: Box::new(f),
602 }
603 }
604 pub fn handle(
605 &self,
606 message: &DistributedMessage,
607 ) -> Result<MessageResponse, DistributedError> {
608 (self.handler_fn)(message)
609 }
610}
611pub struct FaultDetector {
613 failure_history: HashMap<String, Vec<Instant>>,
614}
615impl FaultDetector {
616 pub fn new() -> Self {
618 Self {
619 failure_history: HashMap::new(),
620 }
621 }
622 pub fn handle_failure(&mut self, node_id: &str) -> Result<(), DistributedError> {
624 let failures = self.failure_history.entry(node_id.to_string()).or_default();
625 failures.push(Instant::now());
626 let cutoff = Instant::now() - Duration::from_secs(3600);
627 failures.retain(|&failure_time| failure_time > cutoff);
628 if failures.len() > 3 {
629 println!("Node {node_id} has too many failures, marking as problematic");
630 }
631 Ok(())
632 }
633 pub fn is_problematic(&self, node_id: &str) -> bool {
635 self.failure_history
636 .get(node_id)
637 .map(|failures| failures.len() > 3)
638 .unwrap_or(false)
639 }
640}
641#[derive(Debug, Clone)]
642pub enum MessageType {
643 JobSubmission,
644 JobResult,
645 Heartbeat,
646 ResourceUpdate,
647 ConsensusRequest,
648 DataPartition,
649 Custom(String),
650}
651pub struct LoadBalancer {
653 rebalance_threshold: f64,
654}
655impl LoadBalancer {
656 pub fn new() -> Self {
658 Self {
659 rebalance_threshold: 0.8,
660 }
661 }
662 pub fn rebalance(&self, nodes: &HashMap<String, ClusterNode>) -> Result<(), DistributedError> {
664 let overloaded_nodes: Vec<_> = nodes
665 .values()
666 .filter(|node| {
667 let total_load = node.load_metrics.cpu_usage + node.load_metrics.memory_usage;
668 total_load > self.rebalance_threshold * 2.0
669 })
670 .collect();
671 let underloaded_nodes: Vec<_> = nodes
672 .values()
673 .filter(|node| {
674 let total_load = node.load_metrics.cpu_usage + node.load_metrics.memory_usage;
675 total_load < self.rebalance_threshold
676 })
677 .collect();
678 println!(
679 "Rebalancing: {} overloaded nodes, {} underloaded nodes",
680 overloaded_nodes.len(),
681 underloaded_nodes.len()
682 );
683 Ok(())
684 }
685}
686#[derive(Debug, Clone, PartialEq)]
688pub enum NodeStatus {
689 Available,
690 Busy,
691 Overloaded,
692 Unreachable,
693 Maintenance,
694}
695#[derive(Debug, Clone, PartialEq)]
697pub enum JobType {
698 Training,
699 Inference,
700 DataProcessing,
701 ModelEvaluation,
702 Hyperparameter,
703 Custom(String),
704}
705#[derive(Debug, Clone)]
706pub struct Checkpoint {
707 pub id: String,
708 pub job_id: String,
709 pub state: JobState,
710 pub created_at: Instant,
711 pub size_bytes: u64,
712 pub compressed: bool,
713}
714#[derive(Debug, Clone)]
716pub struct LoadMetrics {
717 pub cpu_usage: f64,
718 pub memory_usage: f64,
719 pub gpu_usage: f64,
720 pub network_io: f64,
721 pub disk_io: f64,
722 pub active_jobs: u32,
723 pub queue_size: u32,
724}
725#[derive(Debug, Clone)]
727pub struct DistributedJob {
728 pub id: String,
729 pub name: String,
730 pub job_type: JobType,
731 pub priority: JobPriority,
732 pub requirements: ResourceRequirements,
733 pub created_at: Instant,
734 pub timeout: Duration,
735 pub retry_count: u32,
736 pub dependencies: Vec<String>,
737 pub metadata: HashMap<String, String>,
738}
739#[derive(Debug, Clone)]
741pub enum SchedulingStrategy {
742 LeastLoaded,
743 RoundRobin,
744 HighestCapacity,
745}
746#[derive(Debug, Clone)]
748pub struct ResourceUsage {
749 pub cpu_time: Duration,
750 pub memory_peak: u64,
751 pub gpu_time: Duration,
752 pub network_bytes: u64,
753 pub disk_bytes: u64,
754}
755#[derive(Debug, Clone)]
757pub struct NodeCapabilities {
758 pub cpu_cores: u32,
759 pub memory_gb: u32,
760 pub gpu_count: u32,
761 pub storage_gb: u32,
762 pub network_bandwidth_mbps: u32,
763 pub supported_tasks: HashSet<String>,
764}
765#[allow(dead_code)]
767pub struct ConsensusManager {
768 node_id: String,
769 pub(crate) state: ConsensusState,
770 term: u64,
771 voted_for: Option<String>,
772 pub(crate) log: Vec<LogEntry>,
773 commit_index: usize,
774 last_applied: usize,
775 peers: HashMap<String, PeerInfo>,
776}
777impl ConsensusManager {
778 pub fn new(node_id: String, peers: Vec<String>) -> Self {
780 let mut peer_map = HashMap::new();
781 for peer in peers {
782 peer_map.insert(
783 peer.clone(),
784 PeerInfo {
785 id: peer,
786 next_index: 0,
787 match_index: 0,
788 last_response: Instant::now(),
789 },
790 );
791 }
792 Self {
793 node_id,
794 state: ConsensusState::Follower,
795 term: 0,
796 voted_for: None,
797 log: Vec::new(),
798 commit_index: 0,
799 last_applied: 0,
800 peers: peer_map,
801 }
802 }
803 pub fn start_election(&mut self) -> Result<(), DistributedError> {
805 self.state = ConsensusState::Candidate;
806 self.term += 1;
807 self.voted_for = Some(self.node_id.clone());
808 println!(
809 "Node {} starting election for term {}",
810 self.node_id, self.term
811 );
812 Ok(())
813 }
814 pub fn handle_vote_request(&mut self, request: VoteRequest) -> VoteResponse {
816 let grant_vote = if request.term > self.term {
817 self.term = request.term;
818 self.voted_for = None;
819 self.state = ConsensusState::Follower;
820 true
821 } else if request.term == self.term
822 && (self.voted_for.is_none() || self.voted_for.as_ref() == Some(&request.candidate_id))
823 {
824 self.voted_for = Some(request.candidate_id.clone());
825 true
826 } else {
827 false
828 };
829 VoteResponse {
830 term: self.term,
831 vote_granted: grant_vote,
832 }
833 }
834 pub fn append_entry(&mut self, entry: LogEntry) -> Result<(), DistributedError> {
836 if self.state != ConsensusState::Leader {
837 return Err(DistributedError::SchedulingError("Not leader".to_string()));
838 }
839 self.log.push(entry);
840 Ok(())
841 }
842 pub fn get_state(&self) -> (ConsensusState, u64) {
844 (self.state.clone(), self.term)
845 }
846}
847#[derive(Debug, Clone)]
848pub struct PartitioningResult {
849 pub assignments_changed: usize,
850 pub partitions_moved: Vec<PartitionMove>,
851 pub rebalance_time: Instant,
852}
853#[derive(Debug, Clone)]
854pub enum PartitioningStrategy {
855 Hash,
856 Range,
857 Random,
858}
859#[derive(Debug, Clone)]
860pub enum SchedulingPolicy {
861 FIFO,
862 ShortestJobFirst,
863 GangScheduling,
864 Backfill,
865 PriorityBased,
866}
867pub struct DistributedCluster {
869 nodes: Arc<RwLock<HashMap<String, ClusterNode>>>,
870 jobs: Arc<RwLock<HashMap<String, DistributedJob>>>,
871 executions: Arc<RwLock<HashMap<String, JobExecution>>>,
872 pub(crate) job_queue: Arc<Mutex<Vec<DistributedJob>>>,
873 scheduler: Arc<Mutex<JobScheduler>>,
874 load_balancer: Arc<Mutex<LoadBalancer>>,
875 fault_detector: Arc<Mutex<FaultDetector>>,
876 config: ClusterConfig,
877}
878impl DistributedCluster {
879 pub fn new(config: ClusterConfig) -> Self {
881 Self {
882 nodes: Arc::new(RwLock::new(HashMap::new())),
883 jobs: Arc::new(RwLock::new(HashMap::new())),
884 executions: Arc::new(RwLock::new(HashMap::new())),
885 job_queue: Arc::new(Mutex::new(Vec::new())),
886 scheduler: Arc::new(Mutex::new(JobScheduler::new())),
887 load_balancer: Arc::new(Mutex::new(LoadBalancer::new())),
888 fault_detector: Arc::new(Mutex::new(FaultDetector::new())),
889 config,
890 }
891 }
892 pub fn register_node(&self, node: ClusterNode) -> Result<(), DistributedError> {
894 let mut nodes = self.nodes.write().expect("operation should succeed");
895 nodes.insert(node.id.clone(), node);
896 Ok(())
897 }
898 pub fn remove_node(&self, node_id: &str) -> Result<(), DistributedError> {
900 let mut nodes = self.nodes.write().expect("operation should succeed");
901 nodes
902 .remove(node_id)
903 .ok_or(DistributedError::NodeNotFound)?;
904 Ok(())
905 }
906 pub fn get_nodes(&self) -> Vec<ClusterNode> {
908 self.nodes
909 .read()
910 .expect("operation should succeed")
911 .values()
912 .cloned()
913 .collect()
914 }
915 pub fn get_available_nodes(&self) -> Vec<ClusterNode> {
917 self.nodes
918 .read()
919 .expect("operation should succeed")
920 .values()
921 .filter(|node| node.status == NodeStatus::Available)
922 .cloned()
923 .collect()
924 }
925 pub fn submit_job(&self, job: DistributedJob) -> Result<String, DistributedError> {
927 let job_id = job.id.clone();
928 self.jobs
929 .write()
930 .expect("operation should succeed")
931 .insert(job_id.clone(), job.clone());
932 self.job_queue
933 .lock()
934 .expect("operation should succeed")
935 .push(job);
936 self.schedule_jobs()?;
937 Ok(job_id)
938 }
939 pub fn schedule_jobs(&self) -> Result<(), DistributedError> {
941 let scheduler = self.scheduler.lock().expect("operation should succeed");
942 let mut queue = self.job_queue.lock().expect("operation should succeed");
943 let nodes = self.nodes.read().expect("operation should succeed");
944 queue.sort_by(|a, b| {
945 b.priority
946 .cmp(&a.priority)
947 .then_with(|| a.created_at.cmp(&b.created_at))
948 });
949 let mut scheduled_jobs = Vec::new();
950 for job in queue.iter() {
951 if let Some(node_id) = scheduler.find_suitable_node(job, &nodes) {
952 let execution = JobExecution {
953 job_id: job.id.clone(),
954 node_id: node_id.clone(),
955 start_time: Instant::now(),
956 end_time: None,
957 status: JobStatus::Running,
958 progress: 0.0,
959 result: None,
960 error: None,
961 resource_usage: None,
962 };
963 self.executions
964 .write()
965 .expect("operation should succeed")
966 .insert(job.id.clone(), execution);
967 scheduled_jobs.push(job.id.clone());
968 }
969 }
970 queue.retain(|job| !scheduled_jobs.contains(&job.id));
971 Ok(())
972 }
973 pub fn get_job_status(&self, job_id: &str) -> Option<JobStatus> {
975 self.executions
976 .read()
977 .expect("operation should succeed")
978 .get(job_id)
979 .map(|exec| exec.status.clone())
980 }
981 pub fn get_job_execution(&self, job_id: &str) -> Option<JobExecution> {
983 self.executions
984 .read()
985 .expect("operation should succeed")
986 .get(job_id)
987 .cloned()
988 }
989 pub fn cancel_job(&self, job_id: &str) -> Result<(), DistributedError> {
991 let mut executions = self.executions.write().expect("operation should succeed");
992 if let Some(execution) = executions.get_mut(job_id) {
993 execution.status = JobStatus::Cancelled;
994 execution.end_time = Some(Instant::now());
995 Ok(())
996 } else {
997 Err(DistributedError::JobNotFound)
998 }
999 }
1000 pub fn update_heartbeat(
1002 &self,
1003 node_id: &str,
1004 load_metrics: LoadMetrics,
1005 ) -> Result<(), DistributedError> {
1006 let mut nodes = self.nodes.write().expect("operation should succeed");
1007 if let Some(node) = nodes.get_mut(node_id) {
1008 node.last_heartbeat = Instant::now();
1009 node.load_metrics = load_metrics;
1010 node.status = self.determine_node_status(&node.load_metrics);
1011 Ok(())
1012 } else {
1013 Err(DistributedError::NodeNotFound)
1014 }
1015 }
1016 fn determine_node_status(&self, metrics: &LoadMetrics) -> NodeStatus {
1018 if metrics.cpu_usage > 0.9 || metrics.memory_usage > 0.9 {
1019 NodeStatus::Overloaded
1020 } else if metrics.cpu_usage > 0.7 || metrics.memory_usage > 0.7 {
1021 NodeStatus::Busy
1022 } else {
1023 NodeStatus::Available
1024 }
1025 }
1026 pub fn get_cluster_stats(&self) -> ClusterStats {
1028 let nodes = self.nodes.read().expect("operation should succeed");
1029 let jobs = self.jobs.read().expect("operation should succeed");
1030 let executions = self.executions.read().expect("operation should succeed");
1031 let total_nodes = nodes.len();
1032 let available_nodes = nodes
1033 .values()
1034 .filter(|n| n.status == NodeStatus::Available)
1035 .count();
1036 let busy_nodes = nodes
1037 .values()
1038 .filter(|n| n.status == NodeStatus::Busy)
1039 .count();
1040 let overloaded_nodes = nodes
1041 .values()
1042 .filter(|n| n.status == NodeStatus::Overloaded)
1043 .count();
1044 let total_jobs = jobs.len();
1045 let running_jobs = executions
1046 .values()
1047 .filter(|e| e.status == JobStatus::Running)
1048 .count();
1049 let completed_jobs = executions
1050 .values()
1051 .filter(|e| e.status == JobStatus::Completed)
1052 .count();
1053 let failed_jobs = executions
1054 .values()
1055 .filter(|e| e.status == JobStatus::Failed)
1056 .count();
1057 let total_cpu_cores: u32 = nodes.values().map(|n| n.capabilities.cpu_cores).sum();
1058 let total_memory_gb: u32 = nodes.values().map(|n| n.capabilities.memory_gb).sum();
1059 let total_gpu_count: u32 = nodes.values().map(|n| n.capabilities.gpu_count).sum();
1060 let avg_cpu_usage = if !nodes.is_empty() {
1061 nodes
1062 .values()
1063 .map(|n| n.load_metrics.cpu_usage)
1064 .sum::<f64>()
1065 / nodes.len() as f64
1066 } else {
1067 0.0
1068 };
1069 let avg_memory_usage = if !nodes.is_empty() {
1070 nodes
1071 .values()
1072 .map(|n| n.load_metrics.memory_usage)
1073 .sum::<f64>()
1074 / nodes.len() as f64
1075 } else {
1076 0.0
1077 };
1078 ClusterStats {
1079 total_nodes,
1080 available_nodes,
1081 busy_nodes,
1082 overloaded_nodes,
1083 unreachable_nodes: total_nodes - available_nodes - busy_nodes - overloaded_nodes,
1084 total_jobs,
1085 running_jobs,
1086 completed_jobs,
1087 failed_jobs,
1088 queued_jobs: self
1089 .job_queue
1090 .lock()
1091 .expect("operation should succeed")
1092 .len(),
1093 total_cpu_cores,
1094 total_memory_gb,
1095 total_gpu_count,
1096 avg_cpu_usage,
1097 avg_memory_usage,
1098 }
1099 }
1100 pub fn start_monitoring(&self) -> Result<(), DistributedError> {
1102 let nodes = Arc::clone(&self.nodes);
1103 let executions = Arc::clone(&self.executions);
1104 let config = self.config.clone();
1105 thread::spawn(move || loop {
1106 let now = Instant::now();
1107 let mut nodes_guard = nodes.write().expect("operation should succeed");
1108 for node in nodes_guard.values_mut() {
1109 if now.duration_since(node.last_heartbeat) > config.node_timeout {
1110 node.status = NodeStatus::Unreachable;
1111 }
1112 }
1113 let mut executions_guard = executions.write().expect("operation should succeed");
1114 for execution in executions_guard.values_mut() {
1115 if execution.status == JobStatus::Running
1116 && now.duration_since(execution.start_time) > config.job_timeout
1117 {
1118 execution.status = JobStatus::Timeout;
1119 execution.end_time = Some(now);
1120 }
1121 }
1122 drop(nodes_guard);
1123 drop(executions_guard);
1124 thread::sleep(config.heartbeat_interval);
1125 });
1126 Ok(())
1127 }
1128 pub fn rebalance_workload(&self) -> Result<(), DistributedError> {
1130 let load_balancer = self.load_balancer.lock().expect("operation should succeed");
1131 let nodes = self.nodes.read().expect("operation should succeed");
1132 load_balancer.rebalance(&nodes)?;
1133 Ok(())
1134 }
1135 pub fn handle_node_failure(&self, node_id: &str) -> Result<(), DistributedError> {
1137 let mut fault_detector = self
1138 .fault_detector
1139 .lock()
1140 .expect("operation should succeed");
1141 let mut executions = self.executions.write().expect("operation should succeed");
1142 for execution in executions.values_mut() {
1143 if execution.node_id == node_id && execution.status == JobStatus::Running {
1144 execution.status = JobStatus::Failed;
1145 execution.end_time = Some(Instant::now());
1146 execution.error = Some("Node failure".to_string());
1147 }
1148 }
1149 fault_detector.handle_failure(node_id)?;
1150 Ok(())
1151 }
1152}
1153#[derive(Debug, Clone)]
1154pub struct ResourceAllocation {
1155 pub cpu_cores: u32,
1156 pub memory_gb: u32,
1157 pub gpu_count: u32,
1158 pub storage_gb: u32,
1159 pub network_bandwidth: u32,
1160}
1161#[derive(Debug, Clone)]
1163pub struct JobExecution {
1164 pub job_id: String,
1165 pub node_id: String,
1166 pub start_time: Instant,
1167 pub end_time: Option<Instant>,
1168 pub status: JobStatus,
1169 pub progress: f64,
1170 pub result: Option<String>,
1171 pub error: Option<String>,
1172 pub resource_usage: Option<ResourceUsage>,
1173}
1174#[derive(Debug, Clone)]
1176pub struct ResourceRequirements {
1177 pub min_cpu_cores: u32,
1178 pub min_memory_gb: u32,
1179 pub min_gpu_count: u32,
1180 pub min_storage_gb: u32,
1181 pub preferred_node_tags: HashSet<String>,
1182 pub exclusive_access: bool,
1183}