1use super::supervision::{SupervisionParams, supervise};
8use super::types::{DepSetupReceivers, PrerequisiteFuture, SupervisionResult, TaskEntry};
9use super::validation::validate_dependencies;
10use crate::contracts::{SupervisedTask, SupervisorEventListener};
11use crate::enums::{ControlMessage, HealthStatus, SupervisorEvent, TaskConfig};
12use crate::error::SupervisorError;
13use crate::persistence::TaskStateStore;
14use std::collections::{HashMap, HashSet};
15use std::fmt;
16use std::future::Future;
17use std::sync::Arc;
18use std::time::Duration;
19use tokio::sync::{RwLock, Semaphore, broadcast, watch};
20use tokio::task::JoinHandle;
21use tracing::{error, info, warn};
22
23#[cfg(feature = "cron")]
24use foxtive_cron::Cron;
25
26pub struct TaskRuntime {
39 pub(super) tasks: HashMap<&'static str, TaskEntry>,
40 pub(super) handles: HashMap<&'static str, JoinHandle<SupervisionResult>>,
41 pub(super) prerequisites: Vec<(&'static str, PrerequisiteFuture)>,
43 pub(super) setup_signals: HashMap<&'static str, watch::Sender<Option<Result<(), String>>>>,
45 pub(super) event_tx: broadcast::Sender<SupervisorEvent>,
47 pub(super) listeners: Vec<Arc<dyn SupervisorEventListener>>,
49 pub(super) state_store: Option<Arc<dyn TaskStateStore>>,
51 pub(super) global_concurrency_limit: Option<Arc<Semaphore>>,
53 pub(crate) task_concurrency_limits: HashMap<&'static str, Arc<Semaphore>>,
55 pub(super) task_configs: HashMap<&'static str, Arc<RwLock<TaskConfig>>>,
57 #[cfg(feature = "cron")]
58 #[allow(dead_code)]
59 pub(super) cron: Option<Arc<tokio::sync::Mutex<Cron>>>,
60}
61
62impl fmt::Debug for TaskRuntime {
63 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
64 f.debug_struct("TaskRuntime")
65 .field("tasks_count", &self.tasks.len())
66 .field("handles_count", &self.handles.len())
67 .field("prerequisites_count", &self.prerequisites.len())
68 .finish()
69 }
70}
71
72impl TaskRuntime {
73 pub fn new() -> Self {
75 let (event_tx, _) = broadcast::channel(1024);
76 #[allow(unused_mut)]
77 let mut runtime = Self {
78 tasks: HashMap::new(),
79 handles: HashMap::new(),
80 prerequisites: Vec::new(),
81 setup_signals: HashMap::new(),
82 event_tx,
83 listeners: Vec::new(),
84 state_store: None,
85 global_concurrency_limit: None,
86 task_concurrency_limits: HashMap::new(),
87 task_configs: HashMap::new(),
88 #[cfg(feature = "cron")]
89 cron: None,
90 };
91 runtime
92 }
93
94 pub fn with_global_concurrency_limit(&mut self, limit: usize) -> &mut Self {
101 self.global_concurrency_limit = Some(Arc::new(Semaphore::new(limit)));
102 self
103 }
104
105 pub fn register<T: SupervisedTask + 'static>(&mut self, task: T) -> &mut Self {
112 let (setup_tx, _) = watch::channel(None);
113 let (control_tx, _) = broadcast::channel(10);
114 let id = task.id();
115
116 if let Some(limit) = task.concurrency_limit() {
118 self.task_concurrency_limits
119 .insert(id, Arc::new(Semaphore::new(limit)));
120 }
121
122 let config = TaskConfig::from_task(&task);
124 self.task_configs.insert(id, Arc::new(RwLock::new(config)));
125
126 self.setup_signals.insert(id, setup_tx.clone());
127 self.tasks.insert(
128 id,
129 TaskEntry {
130 task: Arc::new(task),
131 setup_tx,
132 control_tx,
133 },
134 );
135 self
137 }
138
139 pub fn register_many<T: SupervisedTask + 'static>(&mut self, tasks: Vec<T>) -> &mut Self {
141 for task in tasks {
142 self.register(task);
143 }
144 self
145 }
146
147 pub fn register_boxed(&mut self, task: Box<dyn SupervisedTask>) -> &mut Self {
151 let (setup_tx, _) = watch::channel(None);
152 let (control_tx, _) = broadcast::channel(10);
153 let id = task.id();
154
155 if let Some(limit) = task.concurrency_limit() {
156 self.task_concurrency_limits
157 .insert(id, Arc::new(Semaphore::new(limit)));
158 }
159
160 let config = TaskConfig::from_task(task.as_ref());
162 self.task_configs.insert(id, Arc::new(RwLock::new(config)));
163
164 self.setup_signals.insert(id, setup_tx.clone());
165 self.tasks.insert(
166 id,
167 TaskEntry {
168 task: Arc::from(task),
169 setup_tx,
170 control_tx,
171 },
172 );
173 self
175 }
176
177 pub fn register_arc(&mut self, task: Arc<dyn SupervisedTask>) -> &mut Self {
181 let (setup_tx, _) = watch::channel(None);
182 let (control_tx, _) = broadcast::channel(10);
183 let id = task.id();
184
185 if let Some(limit) = task.concurrency_limit() {
186 self.task_concurrency_limits
187 .insert(id, Arc::new(Semaphore::new(limit)));
188 }
189
190 let config = TaskConfig::from_task(task.as_ref());
192 self.task_configs.insert(id, Arc::new(RwLock::new(config)));
193
194 self.setup_signals.insert(id, setup_tx.clone());
195 self.tasks.insert(
196 id,
197 TaskEntry {
198 task,
199 setup_tx,
200 control_tx,
201 },
202 );
203 self
205 }
206
207 pub fn add_task<T: SupervisedTask + 'static>(
217 &mut self,
218 task: T,
219 ) -> Result<(), SupervisorError> {
220 let id = task.id();
221 if self.tasks.contains_key(id) {
222 return Err(SupervisorError::InternalError(format!(
223 "Task {} already exists",
224 id
225 )));
226 }
227
228 self.register(task);
229
230 let tasks_vec: Vec<&TaskEntry> = self.tasks.values().collect();
232 validate_dependencies(&tasks_vec)?;
233
234 let entry = self.tasks.get(id).unwrap();
235
236 let mut dep_receivers = Vec::new();
238 let active_deps = entry.task.active_dependencies();
239 for dep_id in &active_deps {
240 if let Some(sender) = self.setup_signals.get(dep_id) {
241 dep_receivers.push((*dep_id, sender.subscribe()));
242 } else {
243 return Err(SupervisorError::dependency_validation(
244 id,
245 dep_id,
246 crate::error::ValidationError::UnknownTaskId,
247 ));
248 }
249 }
250
251 let task_limit = self.task_concurrency_limits.get(id).cloned();
252 let task_config = self.task_configs.get(id).cloned();
253
254 let params = SupervisionParams {
255 task: entry.task.clone(),
256 setup_tx: entry.setup_tx.clone(),
257 control_rx: entry.control_tx.subscribe(),
258 event_tx: self.event_tx.clone(),
259 dep_receivers,
260 state_store: self.state_store.clone(),
261 global_semaphore: self.global_concurrency_limit.clone(),
262 task_semaphore: task_limit,
263 task_config,
264 };
265
266 let handle = supervise(params);
267 self.handles.insert(id, handle);
268
269 let name = entry.task.name();
271 let _ = self.event_tx.send(SupervisorEvent::TaskRegistered {
272 id: id.to_string(),
273 name,
274 });
275
276 Ok(())
277 }
278
279 pub fn restart_task(&self, id: &str) -> Result<(), SupervisorError> {
286 if let Some(entry) = self.tasks.get(id) {
287 let _ = entry.control_tx.send(ControlMessage::Restart);
288 Ok(())
289 } else {
290 Err(SupervisorError::UnknownTask(id.to_string()))
291 }
292 }
293
294 pub fn pause_task(&self, id: &str) -> Result<(), SupervisorError> {
302 if let Some(entry) = self.tasks.get(id) {
303 let _ = entry.control_tx.send(ControlMessage::Pause);
304 Ok(())
305 } else {
306 Err(SupervisorError::UnknownTask(id.to_string()))
307 }
308 }
309
310 pub fn resume_task(&self, id: &str) -> Result<(), SupervisorError> {
318 if let Some(entry) = self.tasks.get(id) {
319 let _ = entry.control_tx.send(ControlMessage::Resume);
320 Ok(())
321 } else {
322 Err(SupervisorError::UnknownTask(id.to_string()))
323 }
324 }
325
326 pub fn reset_circuit_breaker(&self, id: &str) -> Result<(), SupervisorError> {
331 if let Some(entry) = self.tasks.get(id) {
332 let _ = entry.control_tx.send(ControlMessage::ResetCircuitBreaker);
333 Ok(())
334 } else {
335 Err(SupervisorError::UnknownTask(id.to_string()))
336 }
337 }
338
339 pub async fn update_restart_policy(
353 &self,
354 id: &str,
355 new_policy: crate::enums::RestartPolicy,
356 ) -> Result<(), SupervisorError> {
357 let config_lock = self
358 .task_configs
359 .get(id)
360 .ok_or_else(|| SupervisorError::UnknownTask(id.to_string()))?;
361
362 Self::validate_restart_policy(&new_policy)?;
364
365 let mut config = config_lock.write().await;
366 let old_policy = config.restart_policy.clone();
367 config.restart_policy = new_policy.clone();
368
369 if let Some(entry) = self.tasks.get(id) {
371 let name = entry.task.name();
372 let _ = self.event_tx.send(SupervisorEvent::TaskConfigUpdated {
373 id: id.to_string(),
374 name,
375 field: "restart_policy".to_string(),
376 old_value: format!("{:?}", old_policy),
377 new_value: format!("{:?}", new_policy),
378 });
379 }
380
381 info!(task_id = %id, ?old_policy, ?new_policy, "Updated restart policy");
382 Ok(())
383 }
384
385 pub async fn update_backoff_strategy(
397 &self,
398 id: &str,
399 new_strategy: crate::enums::BackoffStrategy,
400 ) -> Result<(), SupervisorError> {
401 let config_lock = self
402 .task_configs
403 .get(id)
404 .ok_or_else(|| SupervisorError::UnknownTask(id.to_string()))?;
405
406 Self::validate_backoff_strategy(&new_strategy)?;
408
409 let mut config = config_lock.write().await;
410 let old_strategy = config.backoff_strategy.clone();
411 config.backoff_strategy = new_strategy.clone();
412
413 if let Some(entry) = self.tasks.get(id) {
415 let name = entry.task.name();
416 let _ = self.event_tx.send(SupervisorEvent::TaskConfigUpdated {
417 id: id.to_string(),
418 name,
419 field: "backoff_strategy".to_string(),
420 old_value: format!("{:?}", old_strategy),
421 new_value: format!("{:?}", new_strategy),
422 });
423 }
424
425 info!(task_id = %id, ?old_strategy, ?new_strategy, "Updated backoff strategy");
426 Ok(())
427 }
428
429 pub async fn set_task_enabled(&self, id: &str, enabled: bool) -> Result<(), SupervisorError> {
441 let config_lock = self
442 .task_configs
443 .get(id)
444 .ok_or_else(|| SupervisorError::UnknownTask(id.to_string()))?;
445
446 let mut config = config_lock.write().await;
447 let old_enabled = config.enabled;
448 config.enabled = enabled;
449
450 if let Some(entry) = self.tasks.get(id) {
452 let name = entry.task.name();
453 let _ = self.event_tx.send(SupervisorEvent::TaskConfigUpdated {
454 id: id.to_string(),
455 name,
456 field: "enabled".to_string(),
457 old_value: old_enabled.to_string(),
458 new_value: enabled.to_string(),
459 });
460 }
461
462 info!(task_id = %id, old_enabled = old_enabled, new_enabled = enabled, "Updated task enabled status");
463 Ok(())
464 }
465
466 pub async fn get_task_config(&self, id: &str) -> Option<TaskConfig> {
474 let config_lock = self.task_configs.get(id)?;
475 let config = config_lock.read().await;
476 Some(config.clone())
477 }
478
479 pub async fn is_task_enabled(&self, id: &str) -> bool {
487 if let Some(config_lock) = self.task_configs.get(id) {
488 let config = config_lock.read().await;
489 config.enabled
490 } else {
491 false
492 }
493 }
494
495 pub fn start_group(&mut self, group_id: &str) -> usize {
508 let mut started_count = 0;
509
510 let group_task_ids: Vec<&'static str> = self
512 .tasks
513 .iter()
514 .filter(|(_, entry)| entry.task.group_id() == Some(group_id))
515 .map(|(id, _)| *id)
516 .collect();
517
518 for task_id in group_task_ids {
519 if !self.handles.contains_key(task_id) {
520 if let Err(e) = self.add_task_by_id(task_id) {
522 error!(task_id = %task_id, error = ?e, "Failed to start task in group");
523 } else {
524 started_count += 1;
525 }
526 }
527 }
528
529 info!(group_id = %group_id, started = started_count, "Started task group");
530 started_count
531 }
532
533 pub async fn stop_group(&mut self, group_id: &str) -> usize {
543 let mut stopped_count = 0;
544
545 let group_task_ids: Vec<&'static str> = self
547 .tasks
548 .iter()
549 .filter(|(_, entry)| entry.task.group_id() == Some(group_id))
550 .map(|(id, _)| *id)
551 .collect();
552
553 for task_id in group_task_ids {
554 if let Some(handle) = self.handles.remove(task_id) {
555 if let Some(entry) = self.tasks.get(task_id) {
556 let _ = entry.control_tx.send(ControlMessage::Stop);
557 }
558
559 match tokio::time::timeout(Duration::from_secs(5), handle).await {
561 Ok(_) => stopped_count += 1,
562 Err(_) => {
563 warn!(task_id = %task_id, "Task in group did not stop gracefully, aborting");
564 stopped_count += 1; }
566 }
567 }
568 }
569
570 info!(group_id = %group_id, stopped = stopped_count, "Stopped task group");
571 stopped_count
572 }
573
574 pub fn restart_group(&self, group_id: &str) -> usize {
584 let mut restarted_count = 0;
585
586 for (task_id, entry) in &self.tasks {
587 if entry.task.group_id() == Some(group_id) && self.handles.contains_key(task_id) {
588 let _ = entry.control_tx.send(ControlMessage::Restart);
589 restarted_count += 1;
590 }
591 }
592
593 info!(group_id = %group_id, restarted = restarted_count, "Restarted task group");
594 restarted_count
595 }
596
597 pub fn list_group_tasks(&self, group_id: &str) -> Vec<String> {
605 self.tasks
606 .iter()
607 .filter(|(_, entry)| entry.task.group_id() == Some(group_id))
608 .map(|(id, _)| id.to_string())
609 .collect()
610 }
611
612 pub async fn get_group_health(&self, group_id: &str) -> HealthStatus {
626 let mut has_healthy = false;
627 let mut has_degraded = false;
628 let mut has_unhealthy = false;
629 let mut task_count = 0;
630
631 for entry in self.tasks.values() {
632 if entry.task.group_id() == Some(group_id) {
633 task_count += 1;
634 let health = entry.task.health_check().await;
635 match health {
636 HealthStatus::Healthy => has_healthy = true,
637 HealthStatus::Degraded { .. } => has_degraded = true,
638 HealthStatus::Unhealthy { .. } => has_unhealthy = true,
639 HealthStatus::Unknown => {} }
641 }
642 }
643
644 if task_count == 0 {
645 return HealthStatus::Unknown;
646 }
647
648 if has_unhealthy {
650 HealthStatus::Unhealthy {
651 reason: "One or more tasks in group are unhealthy".to_string(),
652 }
653 } else if has_degraded {
654 HealthStatus::Degraded {
655 reason: "One or more tasks in group are degraded".to_string(),
656 }
657 } else if has_healthy {
658 HealthStatus::Healthy
659 } else {
660 HealthStatus::Unknown
661 }
662 }
663
664 pub async fn get_group_health_details(&self, group_id: &str) -> Vec<TaskSummary> {
672 let mut summaries = Vec::new();
673
674 for (task_id, entry) in &self.tasks {
675 if entry.task.group_id() == Some(group_id) {
676 summaries.push(TaskSummary {
677 id: task_id.to_string(),
678 name: entry.task.name(),
679 health: entry.task.health_check().await,
680 });
681 }
682 }
683
684 summaries
685 }
686
687 fn add_task_by_id(&mut self, task_id: &'static str) -> Result<(), SupervisorError> {
689 let entry = self.tasks.get(task_id).unwrap();
690
691 let mut dep_receivers = Vec::new();
693 let active_deps = entry.task.active_dependencies();
694 for dep_id in &active_deps {
695 if let Some(sender) = self.setup_signals.get(dep_id) {
696 dep_receivers.push((*dep_id, sender.subscribe()));
697 } else {
698 return Err(SupervisorError::dependency_validation(
699 task_id,
700 dep_id,
701 crate::error::ValidationError::UnknownTaskId,
702 ));
703 }
704 }
705
706 let task_limit = self.task_concurrency_limits.get(task_id).cloned();
707 let task_config = self.task_configs.get(task_id).cloned();
708
709 let params = SupervisionParams {
710 task: entry.task.clone(),
711 setup_tx: entry.setup_tx.clone(),
712 control_rx: entry.control_tx.subscribe(),
713 event_tx: self.event_tx.clone(),
714 dep_receivers,
715 state_store: self.state_store.clone(),
716 global_semaphore: self.global_concurrency_limit.clone(),
717 task_semaphore: task_limit,
718 task_config,
719 };
720
721 let handle = supervise(params);
722 self.handles.insert(task_id, handle);
723
724 let name = entry.task.name();
725 let _ = self.event_tx.send(SupervisorEvent::TaskRegistered {
726 id: task_id.to_string(),
727 name,
728 });
729
730 Ok(())
731 }
732
733 pub async fn remove_task(
742 &mut self,
743 id: &str,
744 ) -> Result<Option<SupervisionResult>, SupervisorError> {
745 if let Some(entry) = self.tasks.remove(id) {
746 let _ = entry.control_tx.send(ControlMessage::Stop);
747 let name = entry.task.name();
748 self.setup_signals.remove(id);
749 let _ = self.event_tx.send(SupervisorEvent::TaskRemoved {
750 id: id.to_string(),
751 name,
752 });
753 if let Some(handle) = self.handles.remove(id) {
754 match handle.await {
755 Ok(res) => Ok(Some(res)),
756 Err(_) => Err(SupervisorError::InternalError(format!(
757 "Task {} panicked during removal",
758 id
759 ))),
760 }
761 } else {
762 Ok(None)
763 }
764 } else {
765 Err(SupervisorError::UnknownTask(id.to_string()))
766 }
767 }
768
769 pub async fn get_task_info(&self, id: &str) -> Result<TaskSummary, SupervisorError> {
776 if let Some(entry) = self.tasks.get(id) {
777 let health = entry.task.health_check().await;
778 Ok(TaskSummary {
779 id: id.to_string(),
780 name: entry.task.name(),
781 health,
782 })
783 } else {
784 Err(SupervisorError::UnknownTask(id.to_string()))
785 }
786 }
787
788 pub async fn list_tasks(&self) -> Vec<TaskSummary> {
790 let mut summaries = Vec::new();
791 for (id, entry) in &self.tasks {
792 summaries.push(TaskSummary {
793 id: id.to_string(),
794 name: entry.task.name(),
795 health: entry.task.health_check().await,
796 });
797 }
798 summaries
799 }
800
801 pub fn with_state_store(&mut self, store: Arc<dyn TaskStateStore>) -> &mut Self {
807 self.state_store = Some(store);
808 self
809 }
810
811 pub fn subscribe(&self) -> broadcast::Receiver<SupervisorEvent> {
818 self.event_tx.subscribe()
819 }
820
821 pub fn add_listener(&mut self, listener: Arc<dyn SupervisorEventListener>) -> &mut Self {
825 self.listeners.push(listener);
826 self
827 }
828
829 pub fn add_prerequisite(&mut self, name: &'static str, fut: PrerequisiteFuture) -> &mut Self {
835 self.prerequisites.push((name, fut));
836 self
837 }
838
839 pub fn add_prerequisite_fn<F, Fut>(&mut self, name: &'static str, f: F) -> &mut Self
843 where
844 F: FnOnce() -> Fut + Send + 'static,
845 Fut: Future<Output = Result<(), anyhow::Error>> + Send + 'static,
846 {
847 self.add_prerequisite(name, Box::pin(async move { f().await }))
848 }
849
850 pub async fn start_all(&mut self) -> Result<(), SupervisorError> {
860 let mut event_rx = self.event_tx.subscribe();
862 let listeners = self.listeners.clone();
863 tokio::spawn(async move {
864 loop {
865 match event_rx.recv().await {
866 Ok(event) => {
867 for listener in &listeners {
868 listener.on_event(event.clone()).await;
869 }
870 }
871 Err(broadcast::error::RecvError::Lagged(n)) => {
872 warn!(
873 missed_events = n,
874 "Event channel lagged, skipping missed events to prevent listener backlog"
875 );
876 continue;
878 }
879 Err(broadcast::error::RecvError::Closed) => {
880 info!("Event channel closed, stopping event distribution");
881 break;
882 }
883 }
884 }
885 });
886
887 if !self.prerequisites.is_empty() {
889 info!(
890 "[Supervisor] Running {} prerequisites in parallel...",
891 self.prerequisites.len()
892 );
893
894 let prereq_futures: Vec<_> = self
895 .prerequisites
896 .drain(..)
897 .map(|(name, fut)| async move {
898 info!("[Supervisor] Awaiting prerequisite '{name}'...");
899 fut.await.map_err(|e| (name, e))
900 })
901 .collect();
902
903 let results = futures_util::future::try_join_all(prereq_futures)
904 .await
905 .map_err(|(name, e)| SupervisorError::prerequisite_failed(name, e))?;
906
907 info!("[Supervisor] All {} prerequisites satisfied", results.len());
908 }
909
910 if self.tasks.is_empty() {
911 warn!("[Supervisor] No tasks registered");
912 return Ok(());
913 }
914
915 let tasks_vec: Vec<&TaskEntry> = self.tasks.values().collect();
917 validate_dependencies(&tasks_vec)?;
918
919 info!(
920 "[Supervisor] Starting {} supervised tasks...",
921 self.tasks.len()
922 );
923
924 let mut sorted_ids: Vec<&'static str> = self.tasks.keys().copied().collect();
926 sorted_ids.sort_by(|a, b| {
927 let task_a = &self.tasks[a].task;
928 let task_b = &self.tasks[b].task;
929 task_b.priority().cmp(&task_a.priority())
930 });
931
932 for id in sorted_ids {
934 let entry = &self.tasks[id];
935 let name = entry.task.name();
937 let _ = self.event_tx.send(SupervisorEvent::TaskRegistered {
938 id: id.to_string(),
939 name,
940 });
941
942 let active_deps = entry.task.active_dependencies();
944 let dep_receivers: DepSetupReceivers = active_deps
945 .iter()
946 .map(|dep_id| {
947 let sender = self.setup_signals[dep_id].clone();
948 (*dep_id, sender.subscribe())
949 })
950 .collect();
951
952 let task_limit = self.task_concurrency_limits.get(id).cloned();
953 let task_config = self.task_configs.get(id).cloned();
954
955 let params = SupervisionParams {
956 task: entry.task.clone(),
957 setup_tx: entry.setup_tx.clone(),
958 control_rx: entry.control_tx.subscribe(),
959 event_tx: self.event_tx.clone(),
960 dep_receivers,
961 state_store: self.state_store.clone(),
962 global_semaphore: self.global_concurrency_limit.clone(),
963 task_semaphore: task_limit,
964 task_config,
965 };
966
967 let handle = supervise(params);
968 self.handles.insert(id, handle);
969 }
970
971 info!("[Supervisor] All tasks started");
972 Ok(())
973 }
974
975 pub fn start_one<T: SupervisedTask + 'static>(task: T) -> JoinHandle<SupervisionResult> {
980 let (setup_tx, _) = watch::channel(None);
981 let (control_tx, _) = broadcast::channel(10);
982 let (event_tx, _) = broadcast::channel(1);
983 let params = SupervisionParams {
984 task: Arc::new(task),
985 setup_tx,
986 control_rx: control_tx.subscribe(),
987 event_tx,
988 dep_receivers: vec![],
989 state_store: None,
990 global_semaphore: None,
991 task_semaphore: None,
992 task_config: None,
993 };
994 supervise(params)
995 }
996
997 pub async fn wait_any(&mut self) -> SupervisionResult {
1003 if self.handles.is_empty() {
1004 warn!("[Supervisor] No tasks to wait for");
1005 return SupervisionResult {
1006 task_name: "none".to_string(),
1007 task_id: "none".to_string(),
1008 total_attempts: 0,
1009 final_status: crate::enums::SupervisionStatus::ManuallyStopped,
1010 };
1011 }
1012
1013 let mut handle_vec: Vec<(&'static str, JoinHandle<SupervisionResult>)> =
1015 self.handles.drain().collect();
1016
1017 use futures_util::future::select_all;
1019
1020 let mut futures = Vec::new();
1021 for (id, handle) in handle_vec.iter_mut() {
1022 let id_clone = *id;
1023 futures.push(Box::pin(async move {
1024 let result = handle.await;
1025 (id_clone, result)
1026 }));
1027 }
1028
1029 let ((finished_id, result), _idx, _remaining) = select_all(futures).await;
1030
1031 for (i, (id, handle)) in handle_vec.into_iter().enumerate() {
1033 if i != _idx {
1034 self.handles.insert(id, handle);
1035 }
1036 }
1037
1038 match result {
1039 Ok(supervision_result) => {
1040 error!(
1041 "[Supervisor] Task '{}' (id: {}) terminated: {:?}",
1042 supervision_result.task_name,
1043 supervision_result.task_id,
1044 supervision_result.final_status
1045 );
1046 supervision_result
1047 }
1048 Err(join_err) => {
1049 error!("[Supervisor] Task {} panicked: {:?}", finished_id, join_err);
1050 SupervisionResult {
1051 task_name: "unknown".to_string(),
1052 task_id: finished_id.to_string(),
1053 total_attempts: 0,
1054 final_status: crate::enums::SupervisionStatus::ManuallyStopped,
1055 }
1056 }
1057 }
1058 }
1059
1060 pub async fn wait_all(&mut self) -> Vec<SupervisionResult> {
1064 let mut results = Vec::new();
1065 while !self.handles.is_empty() {
1066 results.push(self.wait_any().await);
1067 }
1068 results
1069 }
1070
1071 pub async fn shutdown(mut self) {
1078 info!("[Supervisor] Shutting down {} tasks...", self.tasks.len());
1079 let _ = self
1080 .event_tx
1081 .send(SupervisorEvent::SupervisorShutdownStarted);
1082
1083 let shutdown_order = self.calculate_shutdown_order();
1086
1087 for id in shutdown_order {
1088 if let Some(entry) = self.tasks.get(id) {
1089 let name = entry.task.name();
1090 let timeout = entry.task.shutdown_timeout();
1091 info!(task_id = %id, "Signalling task '{}' to stop...", name);
1092
1093 let _ = entry.control_tx.send(ControlMessage::Stop);
1094
1095 if let Some(handle) = self.handles.remove(id) {
1096 match tokio::time::timeout(timeout, handle).await {
1098 Ok(res) => {
1099 match res {
1100 Ok(_supervision_res) => {
1101 info!(task_id = %id, "Task '{}' supervision completed.", name);
1102 match tokio::time::timeout(timeout, entry.task.on_shutdown())
1104 .await
1105 {
1106 Ok(_) => {
1107 info!(task_id = %id, "Task '{}' on_shutdown completed.", name)
1108 }
1109 Err(_) => {
1110 warn!(task_id = %id, "Task '{}' on_shutdown timed out after {:?}.", name, timeout)
1111 }
1112 }
1113 }
1114 Err(e) => {
1115 error!(task_id = %id, "Task '{}' panicked during shutdown: {:?}", name, e)
1116 }
1117 }
1118 }
1119 Err(_) => {
1120 warn!(task_id = %id, "Task '{}' did not stop within timeout {:?}. Task will continue in background.", name, timeout);
1121 }
1124 }
1125 }
1126 }
1127 }
1128
1129 for (_id, handle) in self.handles.drain() {
1131 handle.abort();
1132 }
1133
1134 let _ = self
1135 .event_tx
1136 .send(SupervisorEvent::SupervisorShutdownCompleted);
1137 info!("[Supervisor] All tasks shut down");
1138 }
1139
1140 fn calculate_shutdown_order(&self) -> Vec<&'static str> {
1143 let mut order = Vec::new();
1144 let mut visited = HashSet::new();
1145
1146 let mut dependents: HashMap<&'static str, Vec<&'static str>> = HashMap::new();
1148 for (id, entry) in &self.tasks {
1149 let active_deps = entry.task.active_dependencies();
1150 for dep_id in &active_deps {
1151 dependents.entry(dep_id).or_default().push(*id);
1152 }
1153 }
1154
1155 fn visit(
1157 id: &'static str,
1158 dependents: &HashMap<&'static str, Vec<&'static str>>,
1159 visited: &mut HashSet<&'static str>,
1160 order: &mut Vec<&'static str>,
1161 ) {
1162 if visited.contains(id) {
1163 return;
1164 }
1165
1166 if let Some(deps) = dependents.get(id) {
1168 for dep in deps {
1169 visit(dep, dependents, visited, order);
1170 }
1171 }
1172
1173 visited.insert(id);
1174 order.push(id);
1175 }
1176
1177 for id in self.tasks.keys() {
1178 visit(id, &dependents, &mut visited, &mut order);
1179 }
1180
1181 order
1182 }
1183
1184 pub fn task_count(&self) -> usize {
1186 self.handles.len()
1187 }
1188
1189 fn validate_restart_policy(
1193 _policy: &crate::enums::RestartPolicy,
1194 ) -> Result<(), SupervisorError> {
1195 Ok(())
1200 }
1201
1202 fn validate_backoff_strategy(
1204 strategy: &crate::enums::BackoffStrategy,
1205 ) -> Result<(), SupervisorError> {
1206 match strategy {
1208 crate::enums::BackoffStrategy::Fixed(duration) => {
1209 if duration.as_secs() > 3600 {
1210 return Err(SupervisorError::InternalError(
1211 "Fixed backoff delay cannot exceed 1 hour".to_string(),
1212 ));
1213 }
1214 }
1215 crate::enums::BackoffStrategy::Exponential { initial, max } => {
1216 if initial.as_secs() > 3600 || max.as_secs() > 3600 {
1217 return Err(SupervisorError::InternalError(
1218 "Exponential backoff delays cannot exceed 1 hour".to_string(),
1219 ));
1220 }
1221 if initial > max {
1222 return Err(SupervisorError::InternalError(
1223 "Initial backoff cannot be greater than max backoff".to_string(),
1224 ));
1225 }
1226 }
1227 crate::enums::BackoffStrategy::Linear {
1228 initial,
1229 increment: _,
1230 max,
1231 } => {
1232 if initial.as_secs() > 3600 || max.as_secs() > 3600 {
1233 return Err(SupervisorError::InternalError(
1234 "Linear backoff delays cannot exceed 1 hour".to_string(),
1235 ));
1236 }
1237 if initial > max {
1238 return Err(SupervisorError::InternalError(
1239 "Initial backoff cannot be greater than max backoff".to_string(),
1240 ));
1241 }
1242 }
1243 crate::enums::BackoffStrategy::Fibonacci { initial, max } => {
1244 if initial.as_secs() > 3600 || max.as_secs() > 3600 {
1245 return Err(SupervisorError::InternalError(
1246 "Fibonacci backoff delays cannot exceed 1 hour".to_string(),
1247 ));
1248 }
1249 if initial > max {
1250 return Err(SupervisorError::InternalError(
1251 "Initial backoff cannot be greater than max backoff".to_string(),
1252 ));
1253 }
1254 }
1255 crate::enums::BackoffStrategy::Custom(_) => {
1256 }
1259 }
1260 Ok(())
1261 }
1262}
1263
1264#[derive(Debug, Clone)]
1266pub struct TaskSummary {
1267 pub id: String,
1268 pub name: String,
1269 pub health: HealthStatus,
1270}
1271
1272impl Default for TaskRuntime {
1273 fn default() -> Self {
1274 Self::new()
1275 }
1276}