harmos_runtime/observers/
driver.rs1use std::any::TypeId;
22use std::sync::atomic::{AtomicBool, Ordering};
23use std::sync::{Arc, Mutex};
24use tokio::sync::watch;
26use super::ObserverBackend;
28use crate::Error;
29#[cfg(feature = "plugin-sidecar")]
30use crate::Result;
31use crate::operations::resident::{self, Completion, Outcome, Report, Runner, Settlement, Start};
32use crate::operations::{OperationInstanceId, OperationPhase};
33use crate::supervision::{self, Task, instance::Instance};
34
35pub(crate) struct Backend<B>(pub(crate) B);
44
45pub(crate) struct ChildSettlement {
46 pending: Mutex<Vec<Arc<Instance>>>,
47 lifecycle: Arc<Instance>,
48 outcome: Mutex<Option<Outcome>>,
49 backend_returned: AtomicBool,
50 backend_cleanup: AtomicBool,
51}
52
53pub struct ObserverActivation {
54 report: Report,
55 stopping: watch::Receiver<bool>,
56
57 #[cfg(feature = "plugin-sidecar")]
58 children: Arc<ChildSettlement>,
59}
60
61impl<B: ObserverBackend> Runner for Backend<B> {
68 fn resources(&self) -> TypeId {
69 TypeId::of::<B::Resources>()
70 }
71
72 fn spawn(self: Arc<Self>, start: Start) -> (Task<Outcome>, Arc<dyn Settlement>) {
73 let Start { resources, lifecycle, stopping, report, .. } = start;
74 let children = Arc::new(ChildSettlement {
75 pending: Mutex::new(Vec::new()),
76 lifecycle,
77 outcome: Mutex::new(None),
78 backend_returned: AtomicBool::new(false),
79 backend_cleanup: AtomicBool::new(false),
80 });
81 let completion = Arc::clone(&children);
82 let task = Task::with_cancellation(stopping, move |stopping| async move {
83 let admitted = completion.lifecycle.initialize_attempt(0);
84 report.publish();
85 let activation = ObserverActivation {
86 report: report.clone(),
87 stopping: stopping.subscribe(),
88 #[cfg(feature = "plugin-sidecar")]
89 children: Arc::clone(&completion),
90 };
91 let outcome = supervision::joined(async move {
94 if !admitted {
95 return Ok(());
96 }
97 let resources = resources.downcast::<B::Resources>().map_err(|_| Error::InvalidInput {
98 field: "observer scope",
99 reason: "resource type differs from the registered backend",
100 })?;
101 self.0.run(resources, activation).await
102 })
103 .await;
104 let outcome = match outcome {
105 Ok(outcome) => {
106 completion.backend_returned.store(true, Ordering::Release);
107 outcome.map_err(Arc::new)
108 }
109 Err(source) => Err(Arc::new(Error::TaskFailed { domain: "observer backend", source: Arc::new(source) })),
110 };
111 *completion.outcome.lock().expect("observer outcome is healthy") = Some(outcome.clone());
112 if !completion.backend_returned.load(Ordering::Acquire) {
113 report.complete(&outcome);
114 }
115 let outcome = completion.finish(false).await;
116 report.complete(&outcome);
117 outcome
118 });
119 (task, children)
120 }
121}
122
123impl Settlement for ChildSettlement {
124 fn lifecycle(&self) -> &Arc<Instance> {
125 &self.lifecycle
126 }
127
128 fn retry(self: Arc<Self>, report: Report) -> Option<Completion> {
129 let retry = self.outcome.lock().expect("observer outcome is healthy").is_some() && !self.lifecycle.is_settled();
130 retry.then(|| {
131 Box::pin(async move {
132 let outcome = self.finish(true).await;
133 report.complete(&outcome);
134 outcome
135 }) as Completion
136 })
137 }
138}
139
140impl ChildSettlement {
141 #[cfg(feature = "plugin-sidecar")]
144 pub(crate) fn submit(
145 &self, retirement: bool, submit: impl FnOnce() -> Result<crate::operations::EncodedInvocation>,
146 ) -> Result<crate::operations::EncodedInvocation> {
147 let mut pending = self.pending.lock().expect("observer children are healthy");
148 pending.retain(|control| !control.is_settled());
149 if pending.len() >= 64 || (!retirement && self.outcome.lock().expect("observer outcome is healthy").is_some()) {
150 return Err(Error::ResourceExhausted { resource: "observer physical children" });
151 }
152 let invocation = submit()?;
153 pending.push(Arc::clone(&invocation.control));
154 Ok(invocation)
155 }
156
157 #[cfg(feature = "plugin-sidecar")]
159 pub(crate) fn forget(&self, control: &Arc<Instance>) {
160 self.pending.lock().expect("observer children are healthy").retain(|pending| !Arc::ptr_eq(pending, control));
161 }
162
163 async fn finish(&self, recover: bool) -> Outcome {
164 let outcome = self
165 .outcome
166 .lock()
167 .expect("observer outcome is healthy")
168 .as_ref()
169 .expect("backend outcome recorded before child settlement")
170 .clone();
171 if self.lifecycle.status().outcome().is_none() {
172 let terminal = match &outcome {
173 Ok(()) if self.lifecycle.status().cancellation_requested() => supervision::instance::Outcome::Cancelled,
174 Ok(()) => supervision::instance::Outcome::Succeeded,
175 Err(_) => supervision::instance::Outcome::Failed,
176 };
177 self.lifecycle.complete(terminal);
178 }
179 if !self.backend_cleanup.load(Ordering::Acquire) {
180 self.lifecycle.transition(OperationPhase::Finalizing);
181 }
182 let pending = self.pending.lock().expect("observer children are healthy").clone();
183 if !self.backend_returned.load(Ordering::Acquire) {
184 for control in &pending {
185 control.cancel();
186 }
187 }
188 let joining = pending
189 .into_iter()
190 .map(|control| async move { control.settle().await.map_err(|source| Arc::new(Error::OperationSettlement { source })) });
191 if let Err(failure) = supervision::settle_all(joining).await {
192 self.lifecycle.interrupted(failure.clone());
193 return Err(failure);
194 }
195 self.pending.lock().expect("observer children are healthy").clear();
196 if self.backend_returned.load(Ordering::Acquire) {
197 if self.backend_cleanup.load(Ordering::Acquire) && !self.lifecycle.is_settled() {
198 if !recover {
199 return Err(self.lifecycle.status().cleanup_failure().cloned().expect("retained backend failure"));
200 }
201 self.lifecycle.settle().await?;
202 } else {
203 self.lifecycle.finished();
204 }
205 } else if let Err(failure) = &outcome {
206 self.lifecycle.interrupted(failure.clone());
207 }
208 outcome
209 }
210}
211
212impl ObserverActivation {
213 pub fn key(&self) -> &resident::Key {
214 self.report.key()
215 }
216
217 pub fn id(&self) -> OperationInstanceId {
218 self.report.id()
219 }
220
221 pub fn ready(&self) {
223 self.report.transition(OperationPhase::Running);
224 }
225
226 pub fn readiness(&self) -> impl Fn() + Send + Sync + 'static {
228 let report = self.report.clone();
229 move || report.transition(OperationPhase::Running)
230 }
231
232 pub fn begin_stopping(&self) {
234 self.report.quiesce();
235 self.report.transition(OperationPhase::Stopping);
236 }
237
238 #[cfg(feature = "plugin-sidecar")]
239 pub(crate) fn cleaning_up(&self) {
240 self.report.transition(OperationPhase::Finalizing);
241 }
242
243 pub async fn stopping(&mut self) {
244 while !*self.stopping.borrow_and_update() {
245 if self.stopping.changed().await.is_err() {
246 break;
247 }
248 }
249 }
250
251 #[cfg(feature = "plugin-sidecar")]
253 pub(crate) fn retain<R: supervision::cleanup::Resource>(&self, resource: R, failure: Arc<Error>) {
254 self.children.backend_cleanup.store(true, Ordering::Release);
255 self.children.lifecycle.retain(resource, failure);
256 self.report.publish();
257 }
258
259 #[cfg(feature = "plugin-sidecar")]
260 pub(crate) fn depend_on(&self, provider: &Arc<Instance>) -> Result<()> {
261 self.children.lifecycle.depend_on(provider, false)
262 }
263
264 #[cfg(feature = "plugin-sidecar")]
265 pub(crate) fn child_settlement(&self) -> Arc<ChildSettlement> {
266 Arc::clone(&self.children)
267 }
268}
269
270