1use std::time::Duration;
4
5use super::super::domain::{Fleet, RestartBudget};
6use super::super::policy::{
7 RestartPolicy, Strategy, SupervisionFailure, SupervisionFailureReaction,
8 retire_on_supervision_failure,
9};
10use super::super::protocol::{ProxyCommand, SupervisionEvent};
11use super::proxy::Proxy;
12use crate::behavior::{
13 Actions, Address, Behavior, Births, Create, Delivery, Recipient, SendAlgebra, ServiceSends,
14};
15use crate::next::{Never, Step};
16use crate::protocol::{
17 ChildEvent, CreationEvent, ObserveChild, WorkerCreationEvent, WorkerStopped,
18};
19use crate::{Become, Exit, SupervisionFailureReason};
20use crate::{Inner, Own, SendInput};
21
22pub struct SupervisorSends<A: Address, Sends, C: Behavior<Addr = A>> {
24 pub behavior: Sends,
25 pub child_observations: ServiceSends<ObserveChild<A::Nonce>>,
26 pub replacement_commands: Vec<Delivery<A, ProxyCommand<C>>>,
27}
28
29impl<A, Sends, C> SendAlgebra for SupervisorSends<A, Sends, C>
30where
31 A: Address,
32 Sends: SendAlgebra,
33 C: Behavior<Addr = A>,
34{
35 fn empty() -> Self {
36 Self {
37 behavior: Sends::empty(),
38 child_observations: ServiceSends::empty(),
39 replacement_commands: Vec::new(),
40 }
41 }
42
43 fn append(&mut self, other: Self) {
44 self.behavior.append(other.behavior);
45 self.child_observations.append(other.child_observations);
46 self.replacement_commands.extend(other.replacement_commands);
47 }
48}
49
50impl<A, Sends, C> SendInput<ObserveChild<A::Nonce>, Own> for SupervisorSends<A, Sends, C>
51where
52 A: Address,
53 C: Behavior<Addr = A>,
54{
55 fn emit(&mut self, input: ObserveChild<A::Nonce>) {
56 self.child_observations.send(input);
57 }
58}
59
60impl<A, Sends, C> SendInput<Delivery<A, ProxyCommand<C>>, Own> for SupervisorSends<A, Sends, C>
61where
62 A: Address,
63 C: Behavior<Addr = A>,
64{
65 fn emit(&mut self, input: Delivery<A, ProxyCommand<C>>) {
66 self.replacement_commands.push(input);
67 }
68}
69
70impl<A, Sends, C, Input, Path> SendInput<Input, Inner<Path>> for SupervisorSends<A, Sends, C>
71where
72 A: Address,
73 C: Behavior<Addr = A>,
74 Sends: SendInput<Input, Path>,
75{
76 fn emit(&mut self, input: Input) {
77 <Sends as SendInput<Input, Path>>::emit(&mut self.behavior, input);
78 }
79}
80
81pub type SupervisorActions<B, C> = Actions<
82 <B as Behavior>::Addr,
83 <B as Behavior>::Ph,
84 SupervisorSends<<B as Behavior>::Addr, <B as Behavior>::Sends, C>,
85 Births<Proxy<C>>,
86>;
87
88enum ReplacementDecision<A: Address, C: Behavior<Addr = A>> {
89 Retire,
90 Replace(Vec<Delivery<A, ProxyCommand<C>>>),
91 Failed(SupervisionFailure<A>),
92}
93
94pub struct Supervisor<B: Behavior, C: Behavior<Ph = Never, Addr = B::Addr>> {
95 inner: B,
96 fleet: Fleet<<B::Addr as Address>::Nonce>,
97 build: fn(usize) -> C,
98 strategy: Strategy,
99 policy: RestartPolicy,
100 budget: RestartBudget,
101 on_failure: SupervisionFailureReaction<B>,
102}
103
104impl<B, C> Supervisor<B, C>
105where
106 B: Behavior<Birth = Births<C>>,
107 C: Behavior<Ph = Never, Addr = B::Addr>,
108{
109 #[allow(clippy::too_many_arguments, reason = "hidden by Compose")]
110 #[must_use]
117 pub fn new(
118 inner: B,
119 nonces: fn(usize) -> <B::Addr as Address>::Nonce,
120 count: usize,
121 build: fn(usize) -> C,
122 strategy: Strategy,
123 policy: RestartPolicy,
124 max_restarts: u32,
125 window: Duration,
126 ) -> Self {
127 let fleet = Fleet::configured((0..count).map(nonces))
128 .unwrap_or_else(|_| panic!("configured child nonces must be fresh"));
129 Self {
130 inner,
131 fleet,
132 build,
133 strategy,
134 policy,
135 budget: RestartBudget::new(max_restarts, window),
136 on_failure: retire_on_supervision_failure::<B>,
137 }
138 }
139
140 #[must_use]
141 pub fn with_strategy(mut self, strategy: Strategy) -> Self {
142 self.strategy = strategy;
143 self
144 }
145
146 #[must_use]
147 pub fn with_policy(mut self, policy: RestartPolicy) -> Self {
148 self.policy = policy;
149 self
150 }
151
152 #[must_use]
153 pub fn with_budget(mut self, max: u32, window: Duration) -> Self {
154 self.budget = RestartBudget::new(max, window);
155 self
156 }
157
158 #[must_use]
159 pub fn with_failure_reaction(mut self, reaction: SupervisionFailureReaction<B>) -> Self {
161 self.on_failure = reaction;
162 self
163 }
164
165 #[must_use]
166 pub fn is_alive(&self, nonce: <B::Addr as Address>::Nonce) -> bool {
171 self.fleet
172 .is_available(nonce)
173 .unwrap_or_else(|_| panic!("unknown supervised nonce"))
174 }
175
176 #[must_use]
177 pub fn child_count(&self) -> usize {
178 self.fleet.len()
179 }
180
181 #[must_use]
182 pub fn restarts_in_window(&self) -> usize {
183 self.budget.admitted()
184 }
185
186 fn replacement_decision(
187 &mut self,
188 event: &WorkerStopped<B::Addr>,
189 ) -> ReplacementDecision<B::Addr, C> {
190 let eligible = match self.policy {
191 RestartPolicy::Permanent => true,
192 RestartPolicy::Transient => {
193 !matches!(&event.outcome, Ok(Exit::Normal | Exit::Collected))
194 }
195 RestartPolicy::Temporary => false,
196 };
197 if !eligible {
198 self.fleet
199 .retire(event.proxy)
200 .unwrap_or_else(|_| panic!("unknown supervised nonce"));
201 return ReplacementDecision::Retire;
202 }
203 let candidates = self
204 .fleet
205 .replacements(event.proxy, self.strategy)
206 .unwrap_or_else(|_| panic!("unknown supervised nonce"));
207 if let Err(reason) = self.budget.admit(event.at, candidates.len()) {
208 self.fleet
209 .retire(event.proxy)
210 .unwrap_or_else(|_| panic!("unknown supervised nonce"));
211 return ReplacementDecision::Failed(SupervisionFailure::new(
212 event.proxy,
213 event.outcome,
214 SupervisionFailureReason::RestartDenied(reason),
215 ));
216 }
217 for candidate in &candidates {
218 self.fleet
219 .replacement_requested(candidate.nonce)
220 .unwrap_or_else(|_| unreachable!("candidate belongs to fleet"));
221 }
222 ReplacementDecision::Replace(
223 candidates
224 .into_iter()
225 .map(|candidate| {
226 Delivery::new(
227 Recipient::child(candidate.nonce),
228 ProxyCommand::Replace((self.build)(candidate.index)),
229 )
230 })
231 .collect(),
232 )
233 }
234
235 fn react_to_failure(
236 &mut self,
237 failure: &SupervisionFailure<B::Addr>,
238 ) -> Result<Become<B::Addr, B::Ph>, B::Error> {
239 Ok(match (self.on_failure)(&mut self.inner, failure)? {
240 Step::Continue => Step::Continue,
241 Step::Goto(never) => match never {},
242 Step::Stop(exit) => Step::Stop(exit),
243 })
244 }
245
246 fn wrap(
247 &mut self,
248 actions: Actions<B::Addr, B::Ph, B::Sends, Births<C>>,
249 ) -> SupervisorActions<B, C> {
250 let born: Vec<_> = actions.creates.iter().map(|create| create.nonce).collect();
251 for create in &actions.creates {
252 self.fleet
253 .register(create.nonce)
254 .unwrap_or_else(|_| panic!("a child birth nonce must be fresh"));
255 }
256 Actions::new(
257 SupervisorSends {
258 behavior: actions.sends,
259 child_observations: ServiceSends::new(
260 born.into_iter().map(ObserveChild::new).collect(),
261 ),
262 replacement_commands: Vec::new(),
263 },
264 actions
265 .creates
266 .into_iter()
267 .map(|create| Create::new(create.nonce, Proxy::new(create.child), create.kind))
268 .collect(),
269 actions.become_,
270 )
271 }
272}
273
274impl<B, C, A, Ph, Sends> Behavior for Supervisor<B, C>
275where
276 A: Address,
277 Sends: SendAlgebra,
278 B: Behavior<Addr = A, Ph = Ph, Sends = Sends, Birth = Births<C>>,
279 B::Event: ChildEvent + CreationEvent + WorkerCreationEvent,
280 A::Nonce: From<u64>,
281 C: Behavior<Ph = Never, Addr = B::Addr>,
282{
283 type Addr = A;
284 type Msg = B::Msg;
285 type Event = SupervisionEvent<B::Event>;
286 type Sends = SupervisorSends<A, Sends, C>;
287 type Ph = Ph;
288 type Error = B::Error;
289 type Birth = Births<Proxy<C>>;
290
291 fn init(&mut self) -> Result<SupervisorActions<B, C>, B::Error> {
292 let actions = self.inner.init()?;
293 let mut actions = self.wrap(actions);
294 actions.creates.extend(
295 self.fleet
296 .configured_nonces()
297 .enumerate()
298 .map(|(index, nonce)| Create::birth(nonce, Proxy::new((self.build)(index)))),
299 );
300 actions
301 .sends
302 .child_observations
303 .extend(self.fleet.configured_nonces().map(ObserveChild::new));
304 Ok(actions)
305 }
306
307 fn transition(&mut self, event: Self::Event) -> Result<SupervisorActions<B, C>, B::Error> {
308 match event {
309 SupervisionEvent::WorkerStopped(event) => {
310 let decision = self.replacement_decision(&event);
311 match decision {
312 ReplacementDecision::Retire => Ok(Actions::cont()),
313 ReplacementDecision::Replace(replacements) => Ok(Actions::new(
314 SupervisorSends {
315 behavior: B::Sends::empty(),
316 child_observations: ServiceSends::empty(),
317 replacement_commands: replacements,
318 },
319 Vec::new(),
320 Step::Continue,
321 )),
322 ReplacementDecision::Failed(failure) => {
323 Ok(Actions::just(self.react_to_failure(&failure)?))
324 }
325 }
326 }
327 SupervisionEvent::ChildStopped(event) => {
328 self.fleet
329 .retire(event.nonce)
330 .unwrap_or_else(|_| panic!("unknown supervised nonce"));
331 let failure = SupervisionFailure::new(
332 event.nonce,
333 event.outcome,
334 SupervisionFailureReason::StableChildStopped,
335 );
336 Ok(Actions::just(self.react_to_failure(&failure)?))
337 }
338 SupervisionEvent::CreationResolved(event) => {
339 self.fleet.resolve_creation(event.nonce, event.result);
340 if let Some(event) = B::Event::creation_resolved(event) {
341 let actions = self.inner.transition(event)?;
342 Ok(self.wrap(actions))
343 } else {
344 Ok(Actions::cont())
345 }
346 }
347 SupervisionEvent::WorkerCreationResolved(event) => {
348 if let Some(event) = B::Event::worker_creation_resolved(event) {
352 let actions = self.inner.transition(event)?;
353 Ok(self.wrap(actions))
354 } else {
355 Ok(Actions::cont())
356 }
357 }
358 SupervisionEvent::Inner(event) => {
359 let actions = self.inner.transition(event)?;
360 Ok(self.wrap(actions))
361 }
362 }
363 }
364}