1use async_trait::async_trait;
25use daggy::Walker;
26use daggy::{petgraph::visit::Topo, Dag, NodeIndex};
27use log::{error, info, warn};
28use parking_lot::Mutex;
29use std::borrow::Borrow;
30use std::sync::Arc;
31use std::sync::Weak;
32use std::time::Duration;
33use tokio::sync::watch;
34
35#[cfg(unix)]
36use crate::server::ListenFds;
37use crate::server::RuntimeOpts;
38use crate::server::ShutdownWatch;
39
40pub mod background;
41pub mod listening;
42
43pub struct ServiceReadyNotifier {
63 sender: watch::Sender<bool>,
64}
65
66impl Drop for ServiceReadyNotifier {
67 fn drop(&mut self) {
70 let _ = self.sender.send(true);
72 }
73}
74
75impl ServiceReadyNotifier {
76 pub fn new(sender: watch::Sender<bool>) -> Self {
80 Self { sender }
81 }
82
83 pub fn notify_ready(self) {
87 drop(self);
89 }
90}
91
92pub type ServiceReadyWatch = watch::Receiver<bool>;
94
95#[derive(Debug, Clone)]
111pub struct ServiceHandle {
112 pub(crate) id: NodeIndex,
113 name: String,
114 ready_watch: ServiceReadyWatch,
115 dependencies: Weak<Mutex<DependencyGraph>>,
116}
117
118#[derive(Debug, Clone)]
120pub(crate) struct ServiceDependency {
121 pub name: String,
122 pub ready_watch: ServiceReadyWatch,
123}
124
125impl ServiceHandle {
126 pub(crate) fn new(
128 id: NodeIndex,
129 name: String,
130 ready_watch: ServiceReadyWatch,
131 dependencies: &Arc<Mutex<DependencyGraph>>,
132 ) -> Self {
133 Self {
134 id,
135 name,
136 ready_watch,
137 dependencies: Arc::downgrade(dependencies),
138 }
139 }
140
141 #[cfg(test)]
142 fn get_dependencies(&self) -> Vec<ServiceDependency> {
143 let Some(deps_lock) = self.dependencies.upgrade() else {
144 return Vec::new();
145 };
146
147 let deps = deps_lock.lock();
148 deps.get_dependencies(self.id)
149 }
150
151 pub fn name(&self) -> &str {
153 &self.name
154 }
155
156 #[allow(dead_code)]
158 pub(crate) fn ready_watch(&self) -> ServiceReadyWatch {
159 self.ready_watch.clone()
160 }
161
162 pub fn add_dependency(&self, dependency: impl Borrow<ServiceHandle>) {
177 let Some(deps_lock) = self.dependencies.upgrade() else {
178 warn!("Attempted to add a dependency after the dependency tree was dropped");
179 return;
180 };
181
182 let mut deps = deps_lock.lock();
183 if let Err(e) = deps.add_dependency(self.id, dependency.borrow().id) {
184 error!("Error creating dependency edge: {e}");
185 }
186 }
187
188 pub fn add_dependencies<'a, D>(&self, dependencies: impl IntoIterator<Item = D>)
204 where
205 D: Borrow<ServiceHandle> + 'a,
206 {
207 for dependency in dependencies {
208 self.add_dependency(dependency);
209 }
210 }
211}
212
213pub(crate) struct DependencyGraph {
215 dag: Dag<ServiceDependency, ()>,
217}
218
219impl DependencyGraph {
220 pub(crate) fn new() -> Self {
222 Self { dag: Dag::new() }
223 }
224
225 pub(crate) fn add_node(&mut self, name: String, ready_watch: ServiceReadyWatch) -> NodeIndex {
229 self.dag.add_node(ServiceDependency { name, ready_watch })
230 }
231 pub(crate) fn add_dependency(
236 &mut self,
237 dependent_service_node_idx: NodeIndex,
238 dependency_service_node_idx: NodeIndex,
239 ) -> Result<(), String> {
240 if let Err(cycle) =
243 self.dag
244 .add_edge(dependency_service_node_idx, dependent_service_node_idx, ())
245 {
246 return Err(format!(
247 "Circular service dependency detected between {} and {} creating cycle: {cycle}",
248 self.dag[dependency_service_node_idx].name,
249 self.dag[dependent_service_node_idx].name
250 ));
251 }
252
253 Ok(())
254 }
255
256 pub(crate) fn topological_sort(&self) -> Result<Vec<(NodeIndex, ServiceDependency)>, String> {
261 let mut sorted = Vec::new();
263 let mut topo = Topo::new(&self.dag);
264
265 while let Some(service_id) = topo.next(&self.dag) {
266 sorted.push((service_id, self.dag[service_id].clone()));
267 }
268
269 Ok(sorted)
270 }
271
272 pub(crate) fn get_dependencies(&self, service_id: NodeIndex) -> Vec<ServiceDependency> {
273 self.dag
274 .parents(service_id)
275 .iter(&self.dag)
276 .map(|(_, n)| self.dag[n].clone())
277 .collect()
278 }
279}
280
281impl Default for DependencyGraph {
282 fn default() -> Self {
283 Self::new()
284 }
285}
286
287#[async_trait]
288pub trait ServiceWithDependents: Send + Sync {
289 async fn start_service(
306 &mut self,
307 #[cfg(unix)] fds: Option<ListenFds>,
308 shutdown: ShutdownWatch,
309 listeners_per_fd: usize,
310 ready_notifier: ServiceReadyNotifier,
311 );
312
313 fn name(&self) -> &str;
317
318 fn threads(&self) -> Option<usize> {
322 None
323 }
324
325 fn runtime_opts_override(&self, global: &RuntimeOpts) -> Option<RuntimeOpts> {
330 let _ = global;
331 None
332 }
333
334 fn on_startup_delay(&self, time_waited: Duration) {
343 info!(
344 "Service {} spent {}ms waiting on dependencies",
345 self.name(),
346 time_waited.as_millis()
347 );
348 }
349
350 fn listen_addresses(&self) -> Option<Vec<String>> {
352 None
353 }
354}
355
356#[async_trait]
357impl<S> ServiceWithDependents for S
358where
359 S: Service,
360{
361 async fn start_service(
362 &mut self,
363 #[cfg(unix)] fds: Option<ListenFds>,
364 shutdown: ShutdownWatch,
365 listeners_per_fd: usize,
366 ready_notifier: ServiceReadyNotifier,
367 ) {
368 ready_notifier.notify_ready();
370
371 S::start_service(
372 self,
373 #[cfg(unix)]
374 fds,
375 shutdown,
376 listeners_per_fd,
377 )
378 .await
379 }
380
381 fn name(&self) -> &str {
382 S::name(self)
383 }
384
385 fn threads(&self) -> Option<usize> {
386 S::threads(self)
387 }
388
389 fn runtime_opts_override(&self, global: &RuntimeOpts) -> Option<RuntimeOpts> {
390 S::runtime_opts_override(self, global)
391 }
392
393 fn on_startup_delay(&self, time_waited: Duration) {
394 S::on_startup_delay(self, time_waited)
395 }
396
397 fn listen_addresses(&self) -> Option<Vec<String>> {
398 S::listen_addresses(self)
399 }
400}
401
402#[async_trait]
404pub trait Service: Sync + Send {
405 async fn start_service(
418 &mut self,
419 #[cfg(unix)] _fds: Option<ListenFds>,
420 _shutdown: ShutdownWatch,
421 _listeners_per_fd: usize,
422 ) {
423 }
425
426 fn name(&self) -> &str;
430
431 fn threads(&self) -> Option<usize> {
435 None
436 }
437
438 fn runtime_opts_override(&self, global: &RuntimeOpts) -> Option<RuntimeOpts> {
443 let _ = global;
444 None
445 }
446
447 fn on_startup_delay(&self, time_waited: Duration) {
456 info!(
457 "Service {} spent {}ms waiting on dependencies",
458 self.name(),
459 time_waited.as_millis()
460 );
461 }
462
463 fn listen_addresses(&self) -> Option<Vec<String>> {
468 None
469 }
470}
471
472#[cfg(test)]
473mod tests {
474 use super::*;
475
476 #[test]
477 fn test_service_handle_creation() {
478 let deps: Arc<Mutex<DependencyGraph>> = Arc::new(Mutex::new(DependencyGraph::new()));
479 let (tx, rx) = watch::channel(false);
480 let service_id = ServiceHandle::new(0.into(), "test_service".to_string(), rx, &deps);
481
482 assert_eq!(service_id.id, 0.into());
483 assert_eq!(service_id.name(), "test_service");
484
485 let watch_clone = service_id.ready_watch();
487 assert!(!*watch_clone.borrow());
488
489 tx.send(true).ok();
491 assert!(*watch_clone.borrow());
492 }
493
494 #[test]
495 fn test_service_handle_add_dependency() {
496 let graph: Arc<Mutex<DependencyGraph>> = Arc::new(Mutex::new(DependencyGraph::new()));
497 let (tx1, rx1) = watch::channel(false);
498 let (tx1_clone, rx1_clone) = (tx1.clone(), rx1.clone());
499 let (_tx2, rx2) = watch::channel(false);
500 let (_tx2_clone, rx2_clone) = (_tx2.clone(), rx2.clone());
501
502 let dep_node = {
504 let mut g = graph.lock();
505 g.add_node("dependency".to_string(), rx1)
506 };
507 let main_node = {
508 let mut g = graph.lock();
509 g.add_node("main".to_string(), rx2)
510 };
511
512 let dep_service = ServiceHandle::new(dep_node, "dependency".to_string(), rx1_clone, &graph);
513 let main_service = ServiceHandle::new(main_node, "main".to_string(), rx2_clone, &graph);
514
515 main_service.add_dependency(&dep_service);
517
518 let deps = main_service.get_dependencies();
520 assert_eq!(deps.len(), 1);
521 assert_eq!(deps[0].name, "dependency");
522
523 assert!(!*deps[0].ready_watch.borrow());
525 tx1_clone.send(true).ok();
526 assert!(*deps[0].ready_watch.borrow());
527 }
528
529 #[test]
530 fn test_service_handle_multiple_dependencies() {
531 let graph: Arc<Mutex<DependencyGraph>> = Arc::new(Mutex::new(DependencyGraph::new()));
532 let (_tx1, rx1) = watch::channel(false);
533 let rx1_clone = rx1.clone();
534 let (_tx2, rx2) = watch::channel(false);
535 let rx2_clone = rx2.clone();
536 let (_tx3, rx3) = watch::channel(false);
537 let rx3_clone = rx3.clone();
538
539 let dep1_node = {
541 let mut g = graph.lock();
542 g.add_node("dep1".to_string(), rx1)
543 };
544 let dep2_node = {
545 let mut g = graph.lock();
546 g.add_node("dep2".to_string(), rx2)
547 };
548 let main_node = {
549 let mut g = graph.lock();
550 g.add_node("main".to_string(), rx3)
551 };
552
553 let dep1 = ServiceHandle::new(dep1_node, "dep1".to_string(), rx1_clone, &graph);
554 let dep2 = ServiceHandle::new(dep2_node, "dep2".to_string(), rx2_clone, &graph);
555 let main_service = ServiceHandle::new(main_node, "main".to_string(), rx3_clone, &graph);
556
557 main_service.add_dependency(&dep1);
559 main_service.add_dependency(&dep2);
560
561 let deps = main_service.get_dependencies();
563 assert_eq!(deps.len(), 2);
564
565 let dep_names: Vec<&str> = deps.iter().map(|d| d.name.as_str()).collect();
566 assert!(dep_names.contains(&"dep1"));
567 assert!(dep_names.contains(&"dep2"));
568 }
569
570 #[test]
571 fn test_single_service_no_dependencies() {
572 let mut graph = DependencyGraph::new();
573 let (_tx, rx) = watch::channel(false);
574 let _node = graph.add_node("service1".to_string(), rx);
575
576 let order = graph.topological_sort().unwrap();
577 assert_eq!(order.len(), 1);
578 assert_eq!(order[0].1.name, "service1");
579 }
580
581 #[test]
582 fn test_simple_dependency_chain() {
583 let mut graph = DependencyGraph::new();
584 let (_tx1, rx1) = watch::channel(false);
585 let (_tx2, rx2) = watch::channel(false);
586 let (_tx3, rx3) = watch::channel(false);
587
588 let node1 = graph.add_node("service1".to_string(), rx1);
589 let node2 = graph.add_node("service2".to_string(), rx2);
590 let node3 = graph.add_node("service3".to_string(), rx3);
591
592 graph.add_dependency(node2, node1).unwrap();
594 graph.add_dependency(node3, node2).unwrap();
595
596 let order = graph.topological_sort().unwrap();
597 assert_eq!(order.len(), 3);
598 assert_eq!(order[0].1.name, "service1");
600 assert_eq!(order[1].1.name, "service2");
601 assert_eq!(order[2].1.name, "service3");
602 }
603
604 #[test]
605 fn test_diamond_dependency() {
606 let mut graph = DependencyGraph::new();
607 let (_tx1, rx1) = watch::channel(false);
608 let (_tx2, rx2) = watch::channel(false);
609 let (_tx3, rx3) = watch::channel(false);
610
611 let db = graph.add_node("db".to_string(), rx1);
612 let cache = graph.add_node("cache".to_string(), rx2);
613 let api = graph.add_node("api".to_string(), rx3);
614
615 graph.add_dependency(api, db).unwrap();
617 graph.add_dependency(api, cache).unwrap();
618
619 let order = graph.topological_sort().unwrap();
620 assert_eq!(order.len(), 3);
622 assert_eq!(order[2].1.name, "api");
623 let first_two: Vec<&str> = order[0..2].iter().map(|(_, d)| d.name.as_str()).collect();
624 assert!(first_two.contains(&"db"));
625 assert!(first_two.contains(&"cache"));
626 }
627
628 #[test]
629 #[should_panic(expected = "node indices out of bounds")]
630 fn test_missing_dependency() {
631 let mut graph = DependencyGraph::new();
632 let (_tx1, rx1) = watch::channel(false);
633
634 let node1 = graph.add_node("service1".to_string(), rx1);
635 let nonexistent = NodeIndex::new(999);
636
637 let _ = graph.add_dependency(node1, nonexistent);
639 }
640
641 #[test]
642 fn test_circular_dependency_self() {
643 let mut graph = DependencyGraph::new();
644 let (_tx1, rx1) = watch::channel(false);
645
646 let node1 = graph.add_node("service1".to_string(), rx1);
647
648 let result = graph.add_dependency(node1, node1);
650
651 assert!(result.is_err());
652 assert!(result.unwrap_err().contains("Circular"));
653 }
654
655 #[test]
656 fn test_circular_dependency_two_services() {
657 let mut graph = DependencyGraph::new();
658 let (_tx1, rx1) = watch::channel(false);
659 let (_tx2, rx2) = watch::channel(false);
660
661 let node1 = graph.add_node("service1".to_string(), rx1);
663 let node2 = graph.add_node("service2".to_string(), rx2);
664
665 graph.add_dependency(node1, node2).unwrap();
667 let result = graph.add_dependency(node2, node1);
668
669 assert!(result.is_err());
670 assert!(result.unwrap_err().contains("Circular"));
671 }
672
673 #[test]
674 fn test_circular_dependency_three_services() {
675 let mut graph = DependencyGraph::new();
676 let (_tx1, rx1) = watch::channel(false);
677 let (_tx2, rx2) = watch::channel(false);
678 let (_tx3, rx3) = watch::channel(false);
679
680 let node1 = graph.add_node("service1".to_string(), rx1);
682 let node2 = graph.add_node("service2".to_string(), rx2);
683 let node3 = graph.add_node("service3".to_string(), rx3);
684
685 graph.add_dependency(node1, node2).unwrap();
687 graph.add_dependency(node2, node3).unwrap();
688 let result = graph.add_dependency(node3, node1);
689
690 assert!(result.is_err());
691 assert!(result.unwrap_err().contains("Circular"));
692 }
693
694 #[test]
695 fn test_complex_valid_graph() {
696 let mut graph = DependencyGraph::new();
697 let (_tx1, rx1) = watch::channel(false);
698 let (_tx2, rx2) = watch::channel(false);
699 let (_tx3, rx3) = watch::channel(false);
700 let (_tx4, rx4) = watch::channel(false);
701 let (_tx5, rx5) = watch::channel(false);
702
703 let db = graph.add_node("db".to_string(), rx1);
709 let cache = graph.add_node("cache".to_string(), rx2);
710 let auth = graph.add_node("auth".to_string(), rx3);
711 let api = graph.add_node("api".to_string(), rx4);
712 let frontend = graph.add_node("frontend".to_string(), rx5);
713
714 graph.add_dependency(auth, db).unwrap();
715 graph.add_dependency(api, db).unwrap();
716 graph.add_dependency(api, cache).unwrap();
717 graph.add_dependency(api, auth).unwrap();
718 graph.add_dependency(frontend, api).unwrap();
719
720 let order = graph.topological_sort().unwrap();
721
722 let db_pos = order.iter().position(|(_, d)| d.name == "db").unwrap();
724 let cache_pos = order.iter().position(|(_, d)| d.name == "cache").unwrap();
725 let auth_pos = order.iter().position(|(_, d)| d.name == "auth").unwrap();
726 let api_pos = order.iter().position(|(_, d)| d.name == "api").unwrap();
727 let frontend_pos = order
728 .iter()
729 .position(|(_, d)| d.name == "frontend")
730 .unwrap();
731
732 assert!(db_pos < auth_pos);
733 assert!(auth_pos < api_pos);
734 assert!(db_pos < api_pos);
735 assert!(cache_pos < api_pos);
736 assert!(api_pos < frontend_pos);
737 }
738}