nidus_core/lifecycle/
mod.rs1use async_trait::async_trait;
4
5use crate::{NidusError, Result};
6
7#[async_trait]
9pub trait LifecycleHook: Send + Sync + 'static {
10 async fn on_startup(&self) -> Result<()> {
12 Ok(())
13 }
14
15 async fn on_shutdown(&self) -> Result<()> {
17 Ok(())
18 }
19}
20
21#[derive(Default)]
23pub struct LifecycleRunner {
24 hooks: Vec<Box<dyn LifecycleHook>>,
25}
26
27impl LifecycleRunner {
28 pub fn new() -> Self {
30 Self::default()
31 }
32
33 pub fn hook<H>(mut self, hook: H) -> Self
35 where
36 H: LifecycleHook,
37 {
38 self.hooks.push(Box::new(hook));
39 self
40 }
41
42 pub async fn startup(&self) -> Result<()> {
44 let span = tracing::info_span!("lifecycle.startup", hook_count = self.hooks.len());
45 let _entered = span.enter();
46 let mut started: Vec<usize> = Vec::new();
47
48 tracing::debug!(hook_count = self.hooks.len(), "lifecycle startup begin");
49 for (index, hook) in self.hooks.iter().enumerate() {
50 tracing::debug!(hook_index = index, "lifecycle startup hook begin");
51 if let Err(source) = hook.on_startup().await {
52 tracing::error!(
53 hook_index = index,
54 error = %source,
55 "lifecycle startup hook failed"
56 );
57 let mut rollback_errors = Vec::new();
58 for started_index in started.into_iter().rev() {
59 tracing::debug!(
60 hook_index = started_index,
61 "lifecycle startup rollback hook begin"
62 );
63 if let Err(error) = self.hooks[started_index].on_shutdown().await {
64 tracing::error!(
65 hook_index = started_index,
66 error = %error,
67 "lifecycle startup rollback hook failed"
68 );
69 rollback_errors.push(error);
70 } else {
71 tracing::debug!(
72 hook_index = started_index,
73 "lifecycle startup rollback hook complete"
74 );
75 }
76 }
77
78 return Err(NidusError::LifecycleStartup {
79 source: Box::new(source),
80 rollback_errors,
81 });
82 }
83 tracing::debug!(hook_index = index, "lifecycle startup hook complete");
84 started.push(index);
85 }
86 tracing::debug!(hook_count = self.hooks.len(), "lifecycle startup complete");
87 Ok(())
88 }
89
90 pub async fn shutdown(&self) -> Result<()> {
92 let span = tracing::info_span!("lifecycle.shutdown", hook_count = self.hooks.len());
93 let _entered = span.enter();
94 tracing::debug!(hook_count = self.hooks.len(), "lifecycle shutdown begin");
95 for (index, hook) in self.hooks.iter().enumerate().rev() {
96 tracing::debug!(hook_index = index, "lifecycle shutdown hook begin");
97 hook.on_shutdown().await?;
98 tracing::debug!(hook_index = index, "lifecycle shutdown hook complete");
99 }
100 tracing::debug!(hook_count = self.hooks.len(), "lifecycle shutdown complete");
101 Ok(())
102 }
103
104 pub(crate) fn empty() -> Self {
105 Self::new()
106 }
107}