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