1use std::time::Duration;
13
14use tokio::time::Instant;
15
16use crate::behavior::{
17 Actions, Address, Behavior, Births, Create, Delivery, Recipient, SendAlgebra, SendProduct,
18 ServiceSends, User, UserEvent,
19};
20use crate::deadlined::{TimeEvent, TimeReached};
21use crate::shutdown::{ShutdownEvent, ShutdownRequested};
22use crate::verdict::{Never, Step};
23use crate::watching::{PeerEvent, PeerStopped};
24use crate::{Become, Crash, Exit, RestartDenial, SupervisionFailureReason};
25
26#[derive(Debug, Clone, Copy, PartialEq, Eq)]
27pub enum Strategy {
28 OneForOne,
29 OneForAll,
30 RestForOne,
31}
32
33#[derive(Debug, Clone, Copy, PartialEq, Eq)]
34pub enum RestartPolicy {
35 Permanent,
36 Transient,
37 Temporary,
38}
39
40#[must_use]
41pub const fn restart_one() -> Strategy {
42 Strategy::OneForOne
43}
44
45#[must_use]
46pub const fn restart_all() -> Strategy {
47 Strategy::OneForAll
48}
49
50#[must_use]
51pub const fn restart_rest() -> Strategy {
52 Strategy::RestForOne
53}
54
55#[derive(Debug, Clone, PartialEq, Eq)]
56pub struct ChildStopped<A: Address> {
57 pub nonce: A::Nonce,
58 pub outcome: Result<Exit<A>, Crash>,
59 pub at: Instant,
60}
61
62#[derive(Debug, Clone, Copy, PartialEq, Eq)]
63pub struct ObserveChild<A: Address> {
64 pub nonce: A::Nonce,
65}
66
67#[derive(Debug, Clone, PartialEq, Eq)]
71pub struct ReportWorkerStopped<A: Address> {
72 pub outcome: Result<Exit<A>, Crash>,
73 pub at: Instant,
74}
75
76#[derive(Debug, Clone, PartialEq, Eq)]
78pub struct WorkerStopped<A: Address> {
79 pub proxy: A::Nonce,
80 pub outcome: Result<Exit<A>, Crash>,
81 pub at: Instant,
82}
83
84#[derive(Debug, Clone, Copy, PartialEq, Eq)]
91pub struct SupervisionFailure<A: Address> {
92 pub child: A::Nonce,
94 pub outcome: Result<Exit<A>, Crash>,
96 pub reason: SupervisionFailureReason,
98}
99
100impl<A: Address> SupervisionFailure<A> {
101 #[must_use]
104 pub const fn into_exit(self) -> Exit<A> {
105 Exit::SupervisionFailed(self.reason)
106 }
107}
108
109pub type SupervisionFailureReaction<B> =
116 fn(
117 &mut B,
118 &SupervisionFailure<<B as Behavior>::Addr>,
119 ) -> Result<Become<<B as Behavior>::Addr>, <B as Behavior>::Error>;
120
121pub fn retire_on_supervision_failure<B: Behavior>(
130 _behavior: &mut B,
131 _failure: &SupervisionFailure<B::Addr>,
132) -> Result<Become<B::Addr>, B::Error> {
133 Ok(Step::Continue)
134}
135
136pub fn stop_on_supervision_failure<B: Behavior>(
146 _behavior: &mut B,
147 failure: &SupervisionFailure<B::Addr>,
148) -> Result<Become<B::Addr>, B::Error> {
149 Ok(Step::Stop(failure.into_exit()))
150}
151
152pub trait WorkerEvent<A: Address>: Sized {
154 fn worker_stopped(event: WorkerStopped<A>) -> Option<Self>;
155}
156
157#[derive(Clone, PartialEq, Eq)]
158pub enum SupervisionEvent<E, A: Address> {
159 Inner(E),
160 ChildStopped(ChildStopped<A>),
161 WorkerStopped(WorkerStopped<A>),
162}
163
164pub trait ChildEvent<A: Address>: Sized {
165 fn child_stopped(event: ChildStopped<A>) -> Option<Self>;
166}
167
168impl<E, A: Address> ChildEvent<A> for SupervisionEvent<E, A> {
169 fn child_stopped(event: ChildStopped<A>) -> Option<Self> {
170 Some(Self::ChildStopped(event))
171 }
172}
173
174impl<E, A: Address> WorkerEvent<A> for SupervisionEvent<E, A> {
175 fn worker_stopped(event: WorkerStopped<A>) -> Option<Self> {
176 Some(Self::WorkerStopped(event))
177 }
178}
179
180impl<E: UserEvent, A: Address> UserEvent for SupervisionEvent<E, A> {
181 type Addr = E::Addr;
182 type Message = E::Message;
183
184 fn user(from: Self::Addr, message: Self::Message) -> Self {
185 Self::Inner(E::user(from, message))
186 }
187
188 fn into_user(self) -> Result<User<Self::Addr, Self::Message>, Self> {
189 match self {
190 Self::Inner(event) => event.into_user().map_err(Self::Inner),
191 stopped @ (Self::ChildStopped(_) | Self::WorkerStopped(_)) => Err(stopped),
192 }
193 }
194}
195
196impl<E: TimeEvent, A: Address> TimeEvent for SupervisionEvent<E, A> {
197 fn time_reached(event: TimeReached) -> Option<Self> {
198 E::time_reached(event).map(Self::Inner)
199 }
200}
201
202impl<E: PeerEvent<A>, A: Address> PeerEvent<A> for SupervisionEvent<E, A> {
203 fn peer_stopped(event: PeerStopped<A>) -> Option<Self> {
204 E::peer_stopped(event).map(Self::Inner)
205 }
206}
207
208impl<E: ShutdownEvent, A: Address> ShutdownEvent for SupervisionEvent<E, A> {
209 fn shutdown_requested(event: ShutdownRequested) -> Option<Self> {
210 E::shutdown_requested(event).map(Self::Inner)
211 }
212}
213
214#[derive(Debug)]
220pub enum ProxyCommand<C: Behavior> {
221 Forward(C::Msg),
223 Replace(C),
225}
226
227pub type SupervisorSends<A, Sends, C> = SendProduct<
228 Sends,
229 SendProduct<ServiceSends<ObserveChild<A>>, Vec<Delivery<A, ProxyCommand<C>>>>,
230>;
231
232pub type SupervisorActions<B, C> = Actions<
233 <B as Behavior>::Addr,
234 <B as Behavior>::Ph,
235 SupervisorSends<<B as Behavior>::Addr, <B as Behavior>::Sends, C>,
236 Births<Proxy<C>>,
237>;
238
239pub struct Proxy<C: Behavior<Ph = Never>> {
246 worker: Option<C>,
247 generation: u64,
248 worker_alive: bool,
249 pending: Option<C>,
250}
251
252impl<C: Behavior<Ph = Never>> Proxy<C> {
253 #[must_use]
254 pub fn new(worker: C) -> Self {
255 Self {
256 worker: Some(worker),
257 generation: 0,
258 worker_alive: false,
259 pending: None,
260 }
261 }
262}
263
264impl<C> Behavior for Proxy<C>
265where
266 C: Behavior<Ph = Never> + Send,
267 C::Addr: Send,
268 <C::Addr as Address>::Nonce: From<u64> + Send,
269 C::Msg: Send,
270 C: Send,
271{
272 type Addr = C::Addr;
273 type Msg = ProxyCommand<C>;
274 type Event = SupervisionEvent<User<C::Addr, ProxyCommand<C>>, C::Addr>;
275 type Sends = SendProduct<
276 Vec<Delivery<C::Addr, C::Msg>>,
277 SendProduct<
278 ServiceSends<ObserveChild<C::Addr>>,
279 ServiceSends<ReportWorkerStopped<C::Addr>>,
280 >,
281 >;
282 type Ph = Never;
283 type Error = Never;
284 type Birth = Births<C>;
285 type Effect = Actions<C::Addr, Never, Self::Sends, Births<C>>;
286 type Done = Exit<C::Addr>;
287
288 async fn init(&mut self) -> Result<Self::Effect, Never> {
289 let child = self.worker.take().expect("a proxy initializes once");
290 self.worker_alive = true;
291 Ok(Actions {
292 sends: SendProduct {
293 inner: Vec::new(),
294 own: SendProduct {
295 inner: ServiceSends::one(ObserveChild {
296 nonce: <C::Addr as Address>::Nonce::from(self.generation),
297 }),
298 own: ServiceSends::empty(),
299 },
300 },
301 creates: vec![Create {
302 nonce: <C::Addr as Address>::Nonce::from(self.generation),
303 child,
304 }],
305 become_: Step::Continue,
306 })
307 }
308
309 async fn step(&mut self, event: Self::Event) -> Result<Self::Effect, Never> {
310 let SupervisionEvent::Inner(event) = event else {
311 return match event {
312 SupervisionEvent::ChildStopped(event)
313 if event.nonce == <C::Addr as Address>::Nonce::from(self.generation) =>
314 {
315 self.worker_alive = false;
316 let report = ReportWorkerStopped {
317 outcome: event.outcome,
318 at: event.at,
319 };
320 let creates = self.pending.take().map_or_else(Vec::new, |child| {
321 self.generation = self
322 .generation
323 .checked_add(1)
324 .expect("proxy generation exhausted");
325 self.worker_alive = true;
326 vec![Create {
327 nonce: <C::Addr as Address>::Nonce::from(self.generation),
328 child,
329 }]
330 });
331 let observes = creates
332 .iter()
333 .map(|create| ObserveChild {
334 nonce: create.nonce,
335 })
336 .collect();
337 Ok(Actions {
338 sends: SendProduct {
339 inner: Vec::new(),
340 own: SendProduct {
341 inner: ServiceSends::new(observes),
342 own: ServiceSends::one(report),
343 },
344 },
345 creates,
346 become_: Step::Continue,
347 })
348 }
349 SupervisionEvent::ChildStopped(_) | SupervisionEvent::WorkerStopped(_) => {
350 Ok(Actions::cont())
351 }
352 SupervisionEvent::Inner(_) => unreachable!(),
353 };
354 };
355 match event.message {
356 ProxyCommand::Forward(message) => Ok(Actions {
357 sends: SendProduct {
358 inner: self
359 .worker_alive
360 .then(|| {
361 Delivery::new(
362 Recipient::child(<C::Addr as Address>::Nonce::from(
363 self.generation,
364 )),
365 message,
366 )
367 })
368 .into_iter()
369 .collect(),
370 own: SendProduct {
371 inner: ServiceSends::empty(),
372 own: ServiceSends::empty(),
373 },
374 },
375 creates: Vec::new(),
376 become_: Step::Continue,
377 }),
378 ProxyCommand::Replace(child) => {
379 if self.worker_alive {
380 self.pending = Some(child);
381 return Ok(Actions::cont());
382 }
383 self.generation = self
384 .generation
385 .checked_add(1)
386 .expect("proxy generation exhausted");
387 self.worker_alive = true;
388 let nonce = <C::Addr as Address>::Nonce::from(self.generation);
389 Ok(Actions {
390 sends: SendProduct {
391 inner: Vec::new(),
392 own: SendProduct {
393 inner: ServiceSends::one(ObserveChild { nonce }),
394 own: ServiceSends::empty(),
395 },
396 },
397 creates: vec![Create { nonce, child }],
398 become_: Step::Continue,
399 })
400 }
401 }
402 }
403}
404
405struct Slot {
406 alive: bool,
407 sequence: u64,
408}
409
410enum ReplacementDecision<A: Address, C: Behavior<Addr = A>> {
411 Retire,
412 Replace(Vec<Delivery<A, ProxyCommand<C>>>),
413 Failed(SupervisionFailure<A>),
414}
415
416pub struct Supervising<B: Behavior, C: Behavior<Ph = Never, Addr = B::Addr>> {
417 inner: B,
418 slots: Vec<(<B::Addr as Address>::Nonce, Slot)>,
419 configured_count: usize,
420 next_sequence: u64,
421 build: fn(usize) -> C,
422 strategy: Strategy,
423 policy: RestartPolicy,
424 max_restarts: u32,
425 window: Duration,
426 restarts: Vec<Instant>,
427 on_failure: SupervisionFailureReaction<B>,
428}
429
430impl<B, C> Supervising<B, C>
431where
432 B: Behavior<Birth = Births<C>>,
433 C: Behavior<Ph = Never, Addr = B::Addr>,
434{
435 #[allow(clippy::too_many_arguments, reason = "hidden by Spec")]
436 #[must_use]
441 pub fn new(
442 inner: B,
443 nonces: fn(usize) -> <B::Addr as Address>::Nonce,
444 count: usize,
445 build: fn(usize) -> C,
446 strategy: Strategy,
447 policy: RestartPolicy,
448 max_restarts: u32,
449 window: Duration,
450 ) -> Self {
451 let slots = (0..count)
452 .map(|index| {
453 (
454 nonces(index),
455 Slot {
456 alive: true,
457 sequence: u64::try_from(index).expect("fleet index fits u64"),
458 },
459 )
460 })
461 .collect();
462 Self {
463 inner,
464 slots,
465 configured_count: count,
466 next_sequence: u64::try_from(count).expect("fleet size fits u64"),
467 build,
468 strategy,
469 policy,
470 max_restarts,
471 window,
472 restarts: Vec::new(),
473 on_failure: retire_on_supervision_failure::<B>,
474 }
475 }
476
477 #[must_use]
478 pub fn with_strategy(mut self, strategy: Strategy) -> Self {
479 self.strategy = strategy;
480 self
481 }
482
483 #[must_use]
484 pub fn with_policy(mut self, policy: RestartPolicy) -> Self {
485 self.policy = policy;
486 self
487 }
488
489 #[must_use]
490 pub fn with_budget(mut self, max: u32, window: Duration) -> Self {
491 self.max_restarts = max;
492 self.window = window;
493 self
494 }
495
496 #[must_use]
497 pub fn with_failure_reaction(mut self, reaction: SupervisionFailureReaction<B>) -> Self {
499 self.on_failure = reaction;
500 self
501 }
502
503 fn position(&self, nonce: <B::Addr as Address>::Nonce) -> Option<usize> {
504 self.slots.iter().position(|(known, _)| *known == nonce)
505 }
506
507 #[must_use]
508 pub fn is_alive(&self, nonce: <B::Addr as Address>::Nonce) -> bool {
513 self.slots[self.position(nonce).expect("unknown supervised nonce")]
514 .1
515 .alive
516 }
517
518 #[must_use]
519 pub fn child_count(&self) -> usize {
520 self.slots.len()
521 }
522
523 #[must_use]
524 pub fn restarts_in_window(&self) -> usize {
525 self.restarts.len()
526 }
527
528 fn replacement_decision(
529 &mut self,
530 event: &WorkerStopped<B::Addr>,
531 ) -> ReplacementDecision<B::Addr, C> {
532 let dead = self
533 .position(event.proxy)
534 .expect("unknown supervised nonce");
535 let eligible = match self.policy {
536 RestartPolicy::Permanent => true,
537 RestartPolicy::Transient => {
538 !matches!(&event.outcome, Ok(Exit::Normal | Exit::Collected))
539 }
540 RestartPolicy::Temporary => false,
541 };
542 if !eligible {
543 self.slots[dead].1.alive = false;
544 return ReplacementDecision::Retire;
545 }
546 if self.window != Duration::MAX {
547 self.restarts.retain(|stamp| {
548 event
549 .at
550 .checked_duration_since(*stamp)
551 .is_none_or(|age| age <= self.window)
552 });
553 }
554 let sequence = self.slots[dead].1.sequence;
555 let candidates: Vec<usize> = match self.strategy {
556 Strategy::OneForOne => vec![dead],
557 Strategy::OneForAll => self
558 .slots
559 .iter()
560 .enumerate()
561 .filter_map(|(index, (_, slot))| slot.alive.then_some(index))
562 .collect(),
563 Strategy::RestForOne => self
564 .slots
565 .iter()
566 .enumerate()
567 .filter_map(|(index, (_, slot))| {
568 (slot.alive && slot.sequence >= sequence).then_some(index)
569 })
570 .collect(),
571 };
572 if self.restarts.len() + candidates.len() > self.max_restarts as usize {
573 self.slots[dead].1.alive = false;
574 return ReplacementDecision::Failed(SupervisionFailure {
575 child: event.proxy,
576 outcome: event.outcome,
577 reason: SupervisionFailureReason::RestartDenied(RestartDenial::BudgetExceeded {
578 restarts_in_window: self.restarts.len(),
579 replacements_requested: candidates.len(),
580 maximum_restarts: self.max_restarts,
581 }),
582 });
583 }
584 self.restarts
585 .resize(self.restarts.len() + candidates.len(), event.at);
586 ReplacementDecision::Replace(
587 candidates
588 .into_iter()
589 .map(|index| {
590 self.slots[index].1.alive = true;
591 Delivery::new(
592 Recipient::child(self.slots[index].0),
593 ProxyCommand::Replace((self.build)(index)),
594 )
595 })
596 .collect(),
597 )
598 }
599
600 fn react_to_failure(
601 &mut self,
602 failure: &SupervisionFailure<B::Addr>,
603 ) -> Result<Become<B::Addr, B::Ph>, B::Error> {
604 Ok(match (self.on_failure)(&mut self.inner, failure)? {
605 Step::Continue => Step::Continue,
606 Step::Goto(never) => match never {},
607 Step::Stop(exit) => Step::Stop(exit),
608 })
609 }
610
611 fn wrap(
612 &mut self,
613 actions: Actions<B::Addr, B::Ph, B::Sends, Births<C>>,
614 ) -> SupervisorActions<B, C> {
615 let born: Vec<_> = actions.creates.iter().map(|create| create.nonce).collect();
616 for create in &actions.creates {
617 assert!(
618 self.position(create.nonce).is_none(),
619 "a child birth nonce must be fresh"
620 );
621 self.slots.push((
622 create.nonce,
623 Slot {
624 alive: true,
625 sequence: self.next_sequence,
626 },
627 ));
628 self.next_sequence = self
629 .next_sequence
630 .checked_add(1)
631 .expect("birth sequence exhausted");
632 }
633 Actions {
634 sends: SendProduct {
635 inner: actions.sends,
636 own: SendProduct {
637 inner: ServiceSends::new(
638 born.into_iter()
639 .map(|nonce| ObserveChild { nonce })
640 .collect(),
641 ),
642 own: Vec::new(),
643 },
644 },
645 creates: actions
646 .creates
647 .into_iter()
648 .map(|create| Create {
649 nonce: create.nonce,
650 child: Proxy::new(create.child),
651 })
652 .collect(),
653 become_: actions.become_,
654 }
655 }
656}
657
658impl<B, C, A, Ph, Sends> Behavior for Supervising<B, C>
659where
660 A: Address + Send,
661 Sends: SendAlgebra,
662 B: Behavior<
663 Addr = A,
664 Ph = Ph,
665 Sends = Sends,
666 Birth = Births<C>,
667 Effect = Actions<A, Ph, Sends, Births<C>>,
668 Done = Exit<A>,
669 > + Send,
670 B::Event: ChildEvent<B::Addr> + Send,
671 A::Nonce: From<u64> + Send,
672 B::Msg: Send,
673 C: Behavior<Ph = Never, Addr = B::Addr> + Send,
674{
675 type Addr = A;
676 type Msg = B::Msg;
677 type Event = SupervisionEvent<B::Event, B::Addr>;
678 type Sends = SupervisorSends<A, Sends, C>;
679 type Ph = Ph;
680 type Error = B::Error;
681 type Birth = Births<Proxy<C>>;
682 type Effect = Actions<A, Ph, Self::Sends, Births<Proxy<C>>>;
683 type Done = Exit<A>;
684
685 async fn init(&mut self) -> Result<Self::Effect, B::Error> {
686 let actions = self.inner.init().await?;
687 let mut actions = self.wrap(actions);
688 actions
689 .creates
690 .extend(self.slots[..self.configured_count].iter().enumerate().map(
691 |(index, (nonce, _))| Create {
692 nonce: *nonce,
693 child: Proxy::new((self.build)(index)),
694 },
695 ));
696 actions.sends.own.inner.extend(
697 self.slots[..self.configured_count]
698 .iter()
699 .map(|(nonce, _)| ObserveChild { nonce: *nonce }),
700 );
701 Ok(actions)
702 }
703
704 async fn step(&mut self, event: Self::Event) -> Result<Self::Effect, B::Error> {
705 match event {
706 SupervisionEvent::WorkerStopped(event) => {
707 let decision = self.replacement_decision(&event);
708 match decision {
709 ReplacementDecision::Retire => Ok(Actions::cont()),
710 ReplacementDecision::Replace(replacements) => Ok(Actions {
711 sends: SendProduct {
712 inner: B::Sends::empty(),
713 own: SendProduct {
714 inner: ServiceSends::empty(),
715 own: replacements,
716 },
717 },
718 creates: Vec::new(),
719 become_: Step::Continue,
720 }),
721 ReplacementDecision::Failed(failure) => {
722 Ok(Actions::just(self.react_to_failure(&failure)?))
723 }
724 }
725 }
726 SupervisionEvent::ChildStopped(event) => {
727 let dead = self
728 .position(event.nonce)
729 .expect("unknown supervised nonce");
730 self.slots[dead].1.alive = false;
731 let failure = SupervisionFailure {
732 child: event.nonce,
733 outcome: event.outcome,
734 reason: SupervisionFailureReason::StableChildStopped,
735 };
736 Ok(Actions::just(self.react_to_failure(&failure)?))
737 }
738 SupervisionEvent::Inner(event) => {
739 let actions = self.inner.step(event).await?;
740 Ok(self.wrap(actions))
741 }
742 }
743 }
744}