Skip to main content

behavior_actors/atomic/fixed_supervisor/
lifecycle.rs

1//! Lifecycle messages published by one fixed supervisor.
2
3use core::convert::Infallible;
4use core::num::NonZeroU64;
5
6use behavior::{
7    Behavior, BehaviorAddr, Delivery, EndpointAddress, EstablishedActor, EstablishedDelivery,
8    EstablishedRecipient, NoSends, Protocol, Recipient, SendEffects,
9};
10
11use crate::atomic::RoleName;
12use crate::{ChildStopped, DeliveryRoute, ProxyPhase, ReplyDeliveries, ReplyRoute, StableProxy};
13
14use super::ActivationPlan;
15use super::WorkerUnavailable;
16
17mod sealed {
18    pub trait FixedLifecycleRoute<Role, Worker, Plan> {}
19}
20
21enum FixedLifecycleChange<Worker, Plan>
22where
23    Worker: Behavior,
24    Plan: ActivationPlan,
25    BehaviorAddr<Worker>: EndpointAddress,
26    StableProxy<Worker, Plan>: Behavior<Protocol = Worker::Protocol>,
27{
28    Started {
29        proxy: EstablishedActor<StableProxy<Worker, Plan>>,
30    },
31    Restarted {
32        proxy: EstablishedActor<StableProxy<Worker, Plan>>,
33        recovery: NonZeroU64,
34    },
35    WorkerStoppedIneligible {
36        stopped: ChildStopped<BehaviorAddr<Worker>>,
37    },
38    WorkerStoppedAfterAdmission {
39        stopped: ChildStopped<BehaviorAddr<Worker>>,
40        recovery: NonZeroU64,
41    },
42    Unavailable {
43        sender: BehaviorAddr<Worker>,
44        phase: ProxyPhase,
45        command: <Worker::Protocol as Protocol>::Msg,
46    },
47    MemberRetired,
48}
49
50/// One lifecycle message published by a fixed supervisor.
51///
52/// The role is stored once even though the lifecycle change is exhaustive.
53/// [`FixedLifecycle::event`] exposes a borrowed application view without
54/// requiring `Role: Clone`.
55pub struct FixedLifecycle<Role, Worker, Plan>
56where
57    Worker: Behavior,
58    Plan: ActivationPlan,
59    BehaviorAddr<Worker>: EndpointAddress,
60    StableProxy<Worker, Plan>: Behavior<Protocol = Worker::Protocol>,
61{
62    role: RoleName<Role>,
63    change: FixedLifecycleChange<Worker, Plan>,
64}
65
66/// Borrowed exhaustive view of one fixed-supervisor lifecycle message.
67pub enum FixedLifecycleEvent<'a, Role, Worker, Plan>
68where
69    Worker: Behavior,
70    Plan: ActivationPlan,
71    BehaviorAddr<Worker>: EndpointAddress,
72    StableProxy<Worker, Plan>: Behavior<Protocol = Worker::Protocol>,
73{
74    /// One fixed role's first worker became ready.
75    Started {
76        role: &'a Role,
77        proxy: &'a EstablishedActor<StableProxy<Worker, Plan>>,
78    },
79    /// A replacement worker became ready after its predecessor stopped.
80    Restarted {
81        role: &'a Role,
82        proxy: &'a EstablishedActor<StableProxy<Worker, Plan>>,
83        recovery: NonZeroU64,
84    },
85    /// A worker stopped without automatic recovery.
86    WorkerStoppedIneligible {
87        role: &'a Role,
88        stopped: &'a ChildStopped<BehaviorAddr<Worker>>,
89    },
90    /// A worker stop entered one admitted recovery.
91    WorkerStoppedAfterAdmission {
92        role: &'a Role,
93        stopped: &'a ChildStopped<BehaviorAddr<Worker>>,
94        recovery: NonZeroU64,
95    },
96    /// A service command arrived while the role had no ready worker.
97    Unavailable {
98        role: &'a Role,
99        sender: &'a BehaviorAddr<Worker>,
100        phase: ProxyPhase,
101        command: &'a <Worker::Protocol as Protocol>::Msg,
102    },
103    /// The role no longer belongs to the live supervisor topology.
104    MemberRetired { role: &'a Role },
105}
106
107impl<Role, Worker, Plan> FixedLifecycle<Role, Worker, Plan>
108where
109    Worker: Behavior,
110    Plan: ActivationPlan,
111    BehaviorAddr<Worker>: EndpointAddress,
112    StableProxy<Worker, Plan>: Behavior<Protocol = Worker::Protocol>,
113{
114    pub(super) const fn started(
115        role: RoleName<Role>,
116        proxy: EstablishedActor<StableProxy<Worker, Plan>>,
117    ) -> Self {
118        Self {
119            role,
120            change: FixedLifecycleChange::Started { proxy },
121        }
122    }
123
124    pub(super) const fn restarted(
125        role: RoleName<Role>,
126        proxy: EstablishedActor<StableProxy<Worker, Plan>>,
127        recovery: NonZeroU64,
128    ) -> Self {
129        Self {
130            role,
131            change: FixedLifecycleChange::Restarted { proxy, recovery },
132        }
133    }
134
135    pub(super) const fn worker_stopped_ineligible(
136        role: RoleName<Role>,
137        stopped: ChildStopped<BehaviorAddr<Worker>>,
138    ) -> Self {
139        Self {
140            role,
141            change: FixedLifecycleChange::WorkerStoppedIneligible { stopped },
142        }
143    }
144
145    pub(super) const fn worker_stopped_after_admission(
146        role: RoleName<Role>,
147        stopped: ChildStopped<BehaviorAddr<Worker>>,
148        recovery: NonZeroU64,
149    ) -> Self {
150        Self {
151            role,
152            change: FixedLifecycleChange::WorkerStoppedAfterAdmission { stopped, recovery },
153        }
154    }
155
156    pub(super) fn unavailable(unavailable: WorkerUnavailable<Role, Worker>) -> Self {
157        let (role, sender, phase, command) = unavailable.into_parts();
158        Self {
159            role,
160            change: FixedLifecycleChange::Unavailable {
161                sender,
162                phase,
163                command,
164            },
165        }
166    }
167
168    pub(super) const fn member_retired(role: RoleName<Role>) -> Self {
169        Self {
170            role,
171            change: FixedLifecycleChange::MemberRetired,
172        }
173    }
174
175    /// Inspect the complete lifecycle change while borrowing its role and
176    /// affine payloads.
177    #[must_use]
178    pub fn event(&self) -> FixedLifecycleEvent<'_, Role, Worker, Plan> {
179        let role = self.role.role();
180        match &self.change {
181            FixedLifecycleChange::Started { proxy } => FixedLifecycleEvent::Started { role, proxy },
182            FixedLifecycleChange::Restarted { proxy, recovery } => FixedLifecycleEvent::Restarted {
183                role,
184                proxy,
185                recovery: *recovery,
186            },
187            FixedLifecycleChange::WorkerStoppedIneligible { stopped } => {
188                FixedLifecycleEvent::WorkerStoppedIneligible { role, stopped }
189            }
190            FixedLifecycleChange::WorkerStoppedAfterAdmission { stopped, recovery } => {
191                FixedLifecycleEvent::WorkerStoppedAfterAdmission {
192                    role,
193                    stopped,
194                    recovery: *recovery,
195                }
196            }
197            FixedLifecycleChange::Unavailable {
198                sender,
199                phase,
200                command,
201            } => FixedLifecycleEvent::Unavailable {
202                role,
203                sender,
204                phase: *phase,
205                command,
206            },
207            FixedLifecycleChange::MemberRetired => FixedLifecycleEvent::MemberRetired { role },
208        }
209    }
210}
211
212/// Static lifecycle delivery product selected by the configured capability.
213#[doc(hidden)]
214pub trait FixedLifecycleRoute<Role, Worker, Plan>:
215    sealed::FixedLifecycleRoute<Role, Worker, Plan> + Sized
216where
217    Worker: Behavior,
218    Plan: ActivationPlan,
219    BehaviorAddr<Worker>: EndpointAddress,
220    StableProxy<Worker, Plan>: Behavior<Protocol = Worker::Protocol>,
221{
222    type Sends: SendEffects;
223
224    fn deliver(self, lifecycle: FixedLifecycle<Role, Worker, Plan>) -> Self::Sends;
225}
226
227impl<Role, Worker, Plan> sealed::FixedLifecycleRoute<Role, Worker, Plan> for Infallible
228where
229    Worker: Behavior,
230    Plan: ActivationPlan,
231    BehaviorAddr<Worker>: EndpointAddress,
232    StableProxy<Worker, Plan>: Behavior<Protocol = Worker::Protocol>,
233{
234}
235
236impl<Role, Worker, Plan> FixedLifecycleRoute<Role, Worker, Plan> for Infallible
237where
238    Worker: Behavior,
239    Plan: ActivationPlan,
240    BehaviorAddr<Worker>: EndpointAddress,
241    StableProxy<Worker, Plan>: Behavior<Protocol = Worker::Protocol>,
242{
243    type Sends = NoSends;
244
245    fn deliver(self, _: FixedLifecycle<Role, Worker, Plan>) -> Self::Sends {
246        match self {}
247    }
248}
249
250impl<Role, Worker, Plan, P> sealed::FixedLifecycleRoute<Role, Worker, Plan> for Recipient<P>
251where
252    Worker: Behavior,
253    Plan: ActivationPlan,
254    BehaviorAddr<Worker>: EndpointAddress,
255    StableProxy<Worker, Plan>: Behavior<Protocol = Worker::Protocol>,
256    P: Protocol<Addr = BehaviorAddr<Worker>, Msg = FixedLifecycle<Role, Worker, Plan>>,
257{
258}
259
260impl<Role, Worker, Plan, P> FixedLifecycleRoute<Role, Worker, Plan> for Recipient<P>
261where
262    Worker: Behavior,
263    Plan: ActivationPlan,
264    BehaviorAddr<Worker>: EndpointAddress,
265    StableProxy<Worker, Plan>: Behavior<Protocol = Worker::Protocol>,
266    P: Protocol<Addr = BehaviorAddr<Worker>, Msg = FixedLifecycle<Role, Worker, Plan>>,
267{
268    type Sends = Vec<Delivery<P>>;
269
270    fn deliver(self, lifecycle: FixedLifecycle<Role, Worker, Plan>) -> Self::Sends {
271        DeliveryRoute::deliver(self, lifecycle)
272    }
273}
274
275impl<Role, Worker, Plan, P> sealed::FixedLifecycleRoute<Role, Worker, Plan>
276    for EstablishedRecipient<P>
277where
278    Worker: Behavior,
279    Plan: ActivationPlan,
280    BehaviorAddr<Worker>: EndpointAddress,
281    StableProxy<Worker, Plan>: Behavior<Protocol = Worker::Protocol>,
282    P: Protocol<Addr = BehaviorAddr<Worker>, Msg = FixedLifecycle<Role, Worker, Plan>>,
283{
284}
285
286impl<Role, Worker, Plan, P> FixedLifecycleRoute<Role, Worker, Plan> for EstablishedRecipient<P>
287where
288    Worker: Behavior,
289    Plan: ActivationPlan,
290    BehaviorAddr<Worker>: EndpointAddress,
291    StableProxy<Worker, Plan>: Behavior<Protocol = Worker::Protocol>,
292    P: Protocol<Addr = BehaviorAddr<Worker>, Msg = FixedLifecycle<Role, Worker, Plan>>,
293{
294    type Sends = Vec<EstablishedDelivery<P>>;
295
296    fn deliver(self, lifecycle: FixedLifecycle<Role, Worker, Plan>) -> Self::Sends {
297        DeliveryRoute::deliver(self, lifecycle)
298    }
299}
300
301impl<Role, Worker, Plan, P> sealed::FixedLifecycleRoute<Role, Worker, Plan> for ReplyRoute<P>
302where
303    Worker: Behavior,
304    Plan: ActivationPlan,
305    BehaviorAddr<Worker>: EndpointAddress,
306    StableProxy<Worker, Plan>: Behavior<Protocol = Worker::Protocol>,
307    P: Protocol<Addr = BehaviorAddr<Worker>, Msg = FixedLifecycle<Role, Worker, Plan>>,
308{
309}
310
311impl<Role, Worker, Plan, P> FixedLifecycleRoute<Role, Worker, Plan> for ReplyRoute<P>
312where
313    Worker: Behavior,
314    Plan: ActivationPlan,
315    BehaviorAddr<Worker>: EndpointAddress,
316    StableProxy<Worker, Plan>: Behavior<Protocol = Worker::Protocol>,
317    P: Protocol<Addr = BehaviorAddr<Worker>, Msg = FixedLifecycle<Role, Worker, Plan>>,
318{
319    type Sends = ReplyDeliveries<Delivery<P>, EstablishedDelivery<P>>;
320
321    fn deliver(self, lifecycle: FixedLifecycle<Role, Worker, Plan>) -> Self::Sends {
322        DeliveryRoute::deliver(self, lifecycle)
323    }
324}