Skip to main content

harmos_runtime/operations/manager/
residents.rs

1/* ***
2 *
3 * Harmos
4 *
5 * SPDX-License-Identifier: Apache-2.0
6 *
7 * ***
8 */
9
10// The resident kinds of the operation manager: registering observers and
11// services, activating them over shared resources, reporting their instances
12// and retiring them in the runtime's shutdown order.
13
14/* === Modules === */
15
16/* === Re-Exports === */
17
18/* === Imports === */
19
20// Standard Library Imports
21use std::collections::BTreeMap;
22use std::sync::Arc;
23// Third Party Imports
24use tokio::sync::watch;
25// Local Imports
26use super::super::caller::Identity;
27use super::super::handler::Outcome;
28use super::super::resident::{self, Activation, Definition, Key, Kind, Lease, Snapshot};
29use super::super::{OperationDeclaration, OperationInstance, OperationInstanceId, Response};
30use super::OperationManager;
31use crate::observers::{Backend, ObserverBackend, ObserverRegistration};
32use crate::services::{Resident, ServiceDefinition};
33use crate::{Application, Result};
34
35/* === Constants === */
36
37/* === Macros === */
38
39/* === Type Aliases === */
40
41/* === Structs === */
42
43/* === Enums === */
44
45/* === Traits === */
46
47/* === Implementations === */
48
49impl OperationManager {
50    // Register an observer visible at once, activated over any resources of
51    // its backend's resource type.
52    pub fn register_observer<B: ObserverBackend>(&self, backend: B) -> Result<()> {
53        let definition = backend.definition()?;
54        self.resident.register(Definition::Observer(definition), Arc::new(Backend(backend)), None, false, false)
55    }
56
57    // Reserve observers bound to these resources; they become visible when the
58    // registration commits.
59    pub fn stage_observers<B: ObserverBackend>(
60        &self, resources: &Arc<B::Resources>, backends: impl IntoIterator<Item = B>,
61    ) -> Result<ObserverRegistration> {
62        self.stage_scoped(
63            resources,
64            backends,
65            #[cfg(any(feature = "plugin", feature = "plugin-guest", feature = "plugin-sidecar"))]
66            None,
67        )
68    }
69
70    #[cfg(any(feature = "plugin", feature = "plugin-guest", feature = "plugin-sidecar"))]
71    pub fn stage_plugin_observers<B: ObserverBackend>(
72        &self, resources: &Arc<B::Resources>, plugin: &crate::plugin::PluginScope, backends: impl IntoIterator<Item = B>,
73    ) -> Result<ObserverRegistration> {
74        self.stage_scoped(resources, backends, Some(plugin.clone()))
75    }
76
77    fn stage_scoped<B: ObserverBackend>(
78        &self, resources: &Arc<B::Resources>, backends: impl IntoIterator<Item = B>,
79        #[cfg(any(feature = "plugin", feature = "plugin-guest", feature = "plugin-sidecar"))] plugin: Option<crate::plugin::PluginScope>,
80    ) -> Result<ObserverRegistration> {
81        let scope = self.resident.scope(Arc::clone(resources))?;
82        let entries = backends
83            .into_iter()
84            .map(|backend| {
85                let definition = backend.definition()?;
86                let runner: Arc<dyn resident::Runner> = Arc::new(Backend(backend));
87                Ok((Definition::Observer(definition), runner))
88            })
89            .collect::<Result<Vec<_>>>()?;
90        self.resident.stage(
91            &scope,
92            entries,
93            #[cfg(any(feature = "plugin", feature = "plugin-guest", feature = "plugin-sidecar"))]
94            plugin,
95        )
96    }
97
98    // Register an authored observer that runs in process; `factory` binds its
99    // host for each activation.
100    #[cfg(feature = "plugin-sidecar")]
101    pub fn register_native_observer<R, H, F>(&self, instance: harmos_sidecar::Instance<R, H>, factory: F) -> Result<()>
102    where
103        R: Send + Sync + 'static,
104        H: Send + Sync + 'static,
105        F: Fn(Arc<R>) -> H + Send + Sync + 'static,
106    {
107        self.register_observer(crate::observers::Native { instance, factory: Arc::new(factory) })
108    }
109
110    #[cfg(feature = "plugin-sidecar")]
111    pub fn stage_native_observers<R, H, F>(
112        &self, resources: &Arc<R>, instances: impl IntoIterator<Item = harmos_sidecar::Instance<R, H>>, factory: F,
113    ) -> Result<ObserverRegistration>
114    where
115        R: Send + Sync + 'static,
116        H: Send + Sync + 'static,
117        F: Fn(Arc<R>) -> H + Send + Sync + 'static,
118    {
119        let factory = Arc::new(factory);
120        self.stage_observers(
121            resources,
122            instances.into_iter().map(|instance| crate::observers::Native { instance, factory: Arc::clone(&factory) }),
123        )
124    }
125
126    // Register a service whose resident each activation creates from the
127    // resources it is activated over.
128    pub fn register_service<A, R, F, T>(&self, definition: ServiceDefinition, factory: F) -> Result<()>
129    where
130        A: Application,
131        R: Send + Sync + 'static,
132        F: Fn(Arc<R>) -> T + Send + Sync + 'static,
133        T: Resident<A>,
134    {
135        let runner = crate::services::Typed::new(definition.clone(), factory);
136        self.resident.register(Definition::Service(definition), Arc::new(runner), None, false, false)
137    }
138
139    // Start a resident that runs once and belongs to the runtime: declared
140    // services and infrastructure such as storage workers.
141    pub(crate) fn start_service<A: Application>(&self, service: crate::services::Once<A>, internal: bool, reserved: bool) -> Result<Lease> {
142        let definition = Definition::Service(service.definition());
143        self.resident.start_once(self, definition, Arc::new(service), internal, reserved)
144    }
145
146    pub(crate) fn hold_resident(&self, lease: Lease) {
147        self.resident.hold(lease);
148    }
149
150    pub(crate) fn resident_instance<O: OperationDeclaration<Response = ()>>(lease: Lease) -> OperationInstance<O> {
151        let id = lease.id();
152        let generation = lease.generation();
153        let control = lease.lifecycle();
154        let settled = lease.settlement();
155        OperationInstance {
156            id,
157            generation,
158            outcome: Outcome::Resident {
159                lease: Some(lease),
160                settled: std::sync::Mutex::new(Box::pin(async move { settled.await.map(|()| Response::new(())) })),
161            },
162            control,
163            released: false,
164        }
165    }
166
167    pub(in crate::operations) async fn activate_resident<O, R>(
168        &self, id: &str, resources: Arc<R>, holder: Identity,
169    ) -> Result<OperationInstance<O>>
170    where
171        O: OperationDeclaration<Response = ()>,
172        R: Send + Sync + 'static,
173    {
174        let scope = self.resident.scope(resources)?;
175        let lease = self.resident.activate(self, id, &scope, std::any::TypeId::of::<R>(), holder, false).await?;
176        Ok(Self::resident_instance(lease))
177    }
178
179    // Activate the resident `id` over `resources`. Activations sharing the same
180    // resources share its instance; each handle holds one lease.
181    pub async fn submit_with_resources_at<R: Send + Sync + 'static>(
182        &self, id: &str, resources: Arc<R>,
183    ) -> Result<OperationInstance<Activation>> {
184        self.activate_resident::<Activation, R>(id, resources, Identity::Host).await
185    }
186
187    // Activate the resident `id` over the runtime's own resources.
188    pub async fn submit_resident(&self, id: &str) -> Result<OperationInstance<Activation>> {
189        let root = self.resident.root();
190        let lease = self.resident.activate(self, id, &root, std::any::TypeId::of::<()>(), Identity::Host, false).await?;
191        Ok(Self::resident_instance(lease))
192    }
193
194    pub fn resident_definition(&self, id: &str) -> Option<Definition> {
195        self.resident.definition(id)
196    }
197
198    pub fn resident_definitions(&self) -> Vec<Definition> {
199        self.resident.definitions()
200    }
201
202    pub fn resident(&self, key: &Key) -> Option<Snapshot> {
203        self.resident.status(key)
204    }
205
206    pub fn residents(&self) -> BTreeMap<Key, Snapshot> {
207        self.resident.statuses()
208    }
209
210    pub fn watch_residents(&self) -> watch::Receiver<BTreeMap<Key, Snapshot>> {
211        self.resident.subscribe()
212    }
213
214    // The key of a running resident instance.
215    pub fn resident_key(&self, instance: OperationInstanceId) -> Option<Key> {
216        self.resident.statuses().into_iter().find_map(|(key, snapshot)| (snapshot.instance == instance).then_some(key))
217    }
218
219    // The resources the runtime's own residents share.
220    pub fn root_resources(&self) -> Arc<()> {
221        self.resident.root_resources()
222    }
223
224    // Stop every resident over these resources and forget them with the
225    // definitions bound to them.
226    pub async fn retire_resources<R: Send + Sync + 'static>(&self, resources: &Arc<R>) -> Result<()> {
227        let Some(scope) = self.resident.scope_of(resources) else {
228            return Ok(());
229        };
230        self.resident.retire_scope(&scope).await?.into_outcome().map_err(|source| crate::Error::TaskFailed { domain: "resident", source })
231    }
232
233    #[cfg(feature = "plugin-sidecar")]
234    pub(crate) async fn retire_scope_of<R: Send + Sync + 'static>(&self, resources: &Arc<R>) -> Result<Option<resident::Retirement>> {
235        let Some(scope) = self.resident.scope_of(resources) else {
236            return Ok(None);
237        };
238        self.resident.retire_scope(&scope).await.map(Some)
239    }
240
241    pub async fn unregister_resident(&self, id: &str) -> Result<()> {
242        self.resident.unregister(id).await
243    }
244
245    pub(crate) fn resident_identity(&self) -> Arc<()> {
246        self.resident.identity()
247    }
248
249    pub(crate) fn handles<A: Application>(&self) -> crate::services::Handles<A> {
250        self.instances.handles_of::<A>().clone()
251    }
252
253    pub(crate) fn close_resident_admission(&self) {
254        self.resident.close_admission();
255    }
256
257    // Stop every observer instance and wait for each to settle. A cleanup that
258    // fails stays owned by its instance; stopping again retries it.
259    pub async fn stop_observers(&self) -> Result<()> {
260        self.resident.stop_kind(Kind::Observer).await
261    }
262
263    pub(crate) async fn shutdown_observers(&self) -> Result<()> {
264        self.resident.shutdown_observers().await
265    }
266
267    pub(crate) async fn shutdown_application_services(&self) -> Result<()> {
268        self.resident.shutdown_application().await
269    }
270
271    // Close resident admission, stop every observer and service including
272    // infrastructure, and forget their definitions once they settle.
273    pub async fn shutdown_residents(&self) -> Result<()> {
274        self.resident.shutdown().await
275    }
276
277    pub(crate) fn observer_cleanup_pending(&self) -> bool {
278        self.resident.cleanup_pending(|kind, _| kind == Kind::Observer)
279    }
280
281    pub(crate) fn service_cleanup_pending(&self, infrastructure: bool) -> bool {
282        self.resident.cleanup_pending(|kind, internal| kind == Kind::Service && (infrastructure || !internal))
283    }
284}
285
286/* === Public Functions === */
287
288/* === Private Functions === */
289
290/* === Tests === */