1use std::time::Duration;
5
6use tokio::time::Instant;
7
8use crate::behavior::{
9 Actions, Address, Behavior, Births, Create, Delivery, Recipient, SendAlgebra, SendProduct,
10 ServiceSends, User, UserEvent,
11};
12use crate::deadlined::{TimeEvent, TimeReached};
13use crate::shutdown::{ShutdownEvent, ShutdownRequested};
14use crate::verdict::{Never, Step};
15use crate::watching::{PeerEvent, PeerStopped};
16use crate::{Crash, Exit};
17
18#[derive(Debug, Clone, Copy, PartialEq, Eq)]
19pub enum Strategy {
20 OneForOne,
21 OneForAll,
22 RestForOne,
23}
24
25#[derive(Debug, Clone, Copy, PartialEq, Eq)]
26pub enum RestartPolicy {
27 Permanent,
28 Transient,
29 Temporary,
30}
31
32#[must_use]
33pub const fn restart_one() -> Strategy {
34 Strategy::OneForOne
35}
36
37#[must_use]
38pub const fn restart_all() -> Strategy {
39 Strategy::OneForAll
40}
41
42#[must_use]
43pub const fn restart_rest() -> Strategy {
44 Strategy::RestForOne
45}
46
47#[derive(Debug, Clone, PartialEq, Eq)]
48pub struct ChildStopped<A: Address> {
49 pub nonce: A::Nonce,
50 pub outcome: Result<Exit<A>, Crash>,
51 pub at: Instant,
52}
53
54#[derive(Debug, Clone, Copy, PartialEq, Eq)]
55pub struct ObserveChild<A: Address> {
56 pub nonce: A::Nonce,
57}
58
59#[derive(Debug, Clone, PartialEq, Eq)]
63pub struct ReportWorkerStopped<A: Address> {
64 pub outcome: Result<Exit<A>, Crash>,
65 pub at: Instant,
66}
67
68#[derive(Debug, Clone, PartialEq, Eq)]
70pub struct WorkerStopped<A: Address> {
71 pub proxy: A::Nonce,
72 pub outcome: Result<Exit<A>, Crash>,
73 pub at: Instant,
74}
75
76pub trait WorkerEvent<A: Address>: Sized {
78 fn worker_stopped(event: WorkerStopped<A>) -> Option<Self>;
79}
80
81#[derive(Clone, PartialEq, Eq)]
82pub enum SupervisionEvent<E, A: Address> {
83 Inner(E),
84 ChildStopped(ChildStopped<A>),
85 WorkerStopped(WorkerStopped<A>),
86}
87
88pub trait ChildEvent<A: Address>: Sized {
89 fn child_stopped(event: ChildStopped<A>) -> Option<Self>;
90}
91
92impl<E, A: Address> ChildEvent<A> for SupervisionEvent<E, A> {
93 fn child_stopped(event: ChildStopped<A>) -> Option<Self> {
94 Some(Self::ChildStopped(event))
95 }
96}
97
98impl<E, A: Address> WorkerEvent<A> for SupervisionEvent<E, A> {
99 fn worker_stopped(event: WorkerStopped<A>) -> Option<Self> {
100 Some(Self::WorkerStopped(event))
101 }
102}
103
104impl<E: UserEvent, A: Address> UserEvent for SupervisionEvent<E, A> {
105 type Addr = E::Addr;
106 type Message = E::Message;
107
108 fn user(from: Self::Addr, message: Self::Message) -> Self {
109 Self::Inner(E::user(from, message))
110 }
111
112 fn into_user(self) -> Result<User<Self::Addr, Self::Message>, Self> {
113 match self {
114 Self::Inner(event) => event.into_user().map_err(Self::Inner),
115 stopped @ (Self::ChildStopped(_) | Self::WorkerStopped(_)) => Err(stopped),
116 }
117 }
118}
119
120impl<E: TimeEvent, A: Address> TimeEvent for SupervisionEvent<E, A> {
121 fn time_reached(event: TimeReached) -> Option<Self> {
122 E::time_reached(event).map(Self::Inner)
123 }
124}
125
126impl<E: PeerEvent<A>, A: Address> PeerEvent<A> for SupervisionEvent<E, A> {
127 fn peer_stopped(event: PeerStopped<A>) -> Option<Self> {
128 E::peer_stopped(event).map(Self::Inner)
129 }
130}
131
132impl<E: ShutdownEvent, A: Address> ShutdownEvent for SupervisionEvent<E, A> {
133 fn shutdown_requested(event: ShutdownRequested) -> Option<Self> {
134 E::shutdown_requested(event).map(Self::Inner)
135 }
136}
137
138#[derive(Debug)]
144pub enum ProxyCommand<C: Behavior> {
145 Forward(C::Msg),
147 Replace(C),
149}
150
151pub type SupervisorSends<A, Sends, C> = SendProduct<
152 Sends,
153 SendProduct<ServiceSends<ObserveChild<A>>, Vec<Delivery<A, ProxyCommand<C>>>>,
154>;
155
156pub type SupervisorActions<B, C> = Actions<
157 <B as Behavior>::Addr,
158 <B as Behavior>::Ph,
159 SupervisorSends<<B as Behavior>::Addr, <B as Behavior>::Sends, C>,
160 Births<Proxy<C>>,
161>;
162
163pub struct Proxy<C: Behavior<Ph = Never>> {
170 worker: Option<C>,
171 generation: u64,
172 worker_alive: bool,
173 pending: Option<C>,
174}
175
176impl<C: Behavior<Ph = Never>> Proxy<C> {
177 #[must_use]
178 pub fn new(worker: C) -> Self {
179 Self {
180 worker: Some(worker),
181 generation: 0,
182 worker_alive: false,
183 pending: None,
184 }
185 }
186}
187
188impl<C> Behavior for Proxy<C>
189where
190 C: Behavior<Ph = Never> + Send,
191 C::Addr: Send,
192 <C::Addr as Address>::Nonce: From<u64> + Send,
193 C::Msg: Send,
194 C: Send,
195{
196 type Addr = C::Addr;
197 type Msg = ProxyCommand<C>;
198 type Event = SupervisionEvent<User<C::Addr, ProxyCommand<C>>, C::Addr>;
199 type Sends = SendProduct<
200 Vec<Delivery<C::Addr, C::Msg>>,
201 SendProduct<
202 ServiceSends<ObserveChild<C::Addr>>,
203 ServiceSends<ReportWorkerStopped<C::Addr>>,
204 >,
205 >;
206 type Ph = Never;
207 type Error = Never;
208 type Birth = Births<C>;
209 type Effect = Actions<C::Addr, Never, Self::Sends, Births<C>>;
210 type Done = Exit<C::Addr>;
211
212 async fn init(&mut self) -> Result<Self::Effect, Never> {
213 let child = self.worker.take().expect("a proxy initializes once");
214 self.worker_alive = true;
215 Ok(Actions {
216 sends: SendProduct {
217 inner: Vec::new(),
218 own: SendProduct {
219 inner: ServiceSends::one(ObserveChild {
220 nonce: <C::Addr as Address>::Nonce::from(self.generation),
221 }),
222 own: ServiceSends::empty(),
223 },
224 },
225 creates: vec![Create {
226 nonce: <C::Addr as Address>::Nonce::from(self.generation),
227 child,
228 }],
229 become_: Step::Continue,
230 })
231 }
232
233 async fn step(&mut self, event: Self::Event) -> Result<Self::Effect, Never> {
234 let SupervisionEvent::Inner(event) = event else {
235 return match event {
236 SupervisionEvent::ChildStopped(event)
237 if event.nonce == <C::Addr as Address>::Nonce::from(self.generation) =>
238 {
239 self.worker_alive = false;
240 let report = ReportWorkerStopped {
241 outcome: event.outcome,
242 at: event.at,
243 };
244 let creates = self.pending.take().map_or_else(Vec::new, |child| {
245 self.generation = self
246 .generation
247 .checked_add(1)
248 .expect("proxy generation exhausted");
249 self.worker_alive = true;
250 vec![Create {
251 nonce: <C::Addr as Address>::Nonce::from(self.generation),
252 child,
253 }]
254 });
255 let observes = creates
256 .iter()
257 .map(|create| ObserveChild {
258 nonce: create.nonce,
259 })
260 .collect();
261 Ok(Actions {
262 sends: SendProduct {
263 inner: Vec::new(),
264 own: SendProduct {
265 inner: ServiceSends::new(observes),
266 own: ServiceSends::one(report),
267 },
268 },
269 creates,
270 become_: Step::Continue,
271 })
272 }
273 SupervisionEvent::ChildStopped(_) | SupervisionEvent::WorkerStopped(_) => {
274 Ok(Actions::cont())
275 }
276 SupervisionEvent::Inner(_) => unreachable!(),
277 };
278 };
279 match event.message {
280 ProxyCommand::Forward(message) => Ok(Actions {
281 sends: SendProduct {
282 inner: self
283 .worker_alive
284 .then(|| {
285 Delivery::new(
286 Recipient::child(<C::Addr as Address>::Nonce::from(
287 self.generation,
288 )),
289 message,
290 )
291 })
292 .into_iter()
293 .collect(),
294 own: SendProduct {
295 inner: ServiceSends::empty(),
296 own: ServiceSends::empty(),
297 },
298 },
299 creates: Vec::new(),
300 become_: Step::Continue,
301 }),
302 ProxyCommand::Replace(child) => {
303 if self.worker_alive {
304 self.pending = Some(child);
305 return Ok(Actions::cont());
306 }
307 self.generation = self
308 .generation
309 .checked_add(1)
310 .expect("proxy generation exhausted");
311 self.worker_alive = true;
312 let nonce = <C::Addr as Address>::Nonce::from(self.generation);
313 Ok(Actions {
314 sends: SendProduct {
315 inner: Vec::new(),
316 own: SendProduct {
317 inner: ServiceSends::one(ObserveChild { nonce }),
318 own: ServiceSends::empty(),
319 },
320 },
321 creates: vec![Create { nonce, child }],
322 become_: Step::Continue,
323 })
324 }
325 }
326 }
327}
328
329struct Slot {
330 alive: bool,
331 sequence: u64,
332}
333
334pub struct Supervising<B: Behavior, C: Behavior<Ph = Never, Addr = B::Addr>> {
335 inner: B,
336 slots: Vec<(<B::Addr as Address>::Nonce, Slot)>,
337 configured_count: usize,
338 next_sequence: u64,
339 build: fn(usize) -> C,
340 strategy: Strategy,
341 policy: RestartPolicy,
342 max_restarts: u32,
343 window: Duration,
344 restarts: Vec<Instant>,
345}
346
347impl<B, C> Supervising<B, C>
348where
349 B: Behavior<Birth = Births<C>>,
350 C: Behavior<Ph = Never, Addr = B::Addr>,
351{
352 #[allow(clippy::too_many_arguments, reason = "hidden by Spec")]
353 #[must_use]
358 pub fn new(
359 inner: B,
360 nonces: fn(usize) -> <B::Addr as Address>::Nonce,
361 count: usize,
362 build: fn(usize) -> C,
363 strategy: Strategy,
364 policy: RestartPolicy,
365 max_restarts: u32,
366 window: Duration,
367 ) -> Self {
368 let slots = (0..count)
369 .map(|index| {
370 (
371 nonces(index),
372 Slot {
373 alive: true,
374 sequence: u64::try_from(index).expect("fleet index fits u64"),
375 },
376 )
377 })
378 .collect();
379 Self {
380 inner,
381 slots,
382 configured_count: count,
383 next_sequence: u64::try_from(count).expect("fleet size fits u64"),
384 build,
385 strategy,
386 policy,
387 max_restarts,
388 window,
389 restarts: Vec::new(),
390 }
391 }
392
393 #[must_use]
394 pub fn with_strategy(mut self, strategy: Strategy) -> Self {
395 self.strategy = strategy;
396 self
397 }
398
399 #[must_use]
400 pub fn with_policy(mut self, policy: RestartPolicy) -> Self {
401 self.policy = policy;
402 self
403 }
404
405 #[must_use]
406 pub fn with_budget(mut self, max: u32, window: Duration) -> Self {
407 self.max_restarts = max;
408 self.window = window;
409 self
410 }
411
412 fn position(&self, nonce: <B::Addr as Address>::Nonce) -> Option<usize> {
413 self.slots.iter().position(|(known, _)| *known == nonce)
414 }
415
416 #[must_use]
417 pub fn is_alive(&self, nonce: <B::Addr as Address>::Nonce) -> bool {
422 self.slots[self.position(nonce).expect("unknown supervised nonce")]
423 .1
424 .alive
425 }
426
427 #[must_use]
428 pub fn child_count(&self) -> usize {
429 self.slots.len()
430 }
431
432 #[must_use]
433 pub fn restarts_in_window(&self) -> usize {
434 self.restarts.len()
435 }
436
437 fn replacements(
438 &mut self,
439 event: &WorkerStopped<B::Addr>,
440 ) -> Vec<Delivery<B::Addr, ProxyCommand<C>>> {
441 let dead = self
442 .position(event.proxy)
443 .expect("unknown supervised nonce");
444 let eligible = match self.policy {
445 RestartPolicy::Permanent => true,
446 RestartPolicy::Transient => {
447 !matches!(&event.outcome, Ok(Exit::Normal | Exit::Collected))
448 }
449 RestartPolicy::Temporary => false,
450 };
451 if !eligible {
452 self.slots[dead].1.alive = false;
453 return Vec::new();
454 }
455 if self.window != Duration::MAX {
456 self.restarts.retain(|stamp| {
457 event
458 .at
459 .checked_duration_since(*stamp)
460 .is_none_or(|age| age <= self.window)
461 });
462 }
463 let sequence = self.slots[dead].1.sequence;
464 let candidates: Vec<usize> = match self.strategy {
465 Strategy::OneForOne => vec![dead],
466 Strategy::OneForAll => self
467 .slots
468 .iter()
469 .enumerate()
470 .filter_map(|(index, (_, slot))| slot.alive.then_some(index))
471 .collect(),
472 Strategy::RestForOne => self
473 .slots
474 .iter()
475 .enumerate()
476 .filter_map(|(index, (_, slot))| {
477 (slot.alive && slot.sequence >= sequence).then_some(index)
478 })
479 .collect(),
480 };
481 if self.restarts.len() + candidates.len() > self.max_restarts as usize {
482 self.slots[dead].1.alive = false;
483 return Vec::new();
484 }
485 self.restarts
486 .resize(self.restarts.len() + candidates.len(), event.at);
487 candidates
488 .into_iter()
489 .map(|index| {
490 self.slots[index].1.alive = true;
491 Delivery::new(
492 Recipient::child(self.slots[index].0),
493 ProxyCommand::Replace((self.build)(index)),
494 )
495 })
496 .collect()
497 }
498
499 fn wrap(
500 &mut self,
501 actions: Actions<B::Addr, B::Ph, B::Sends, Births<C>>,
502 ) -> SupervisorActions<B, C> {
503 let born: Vec<_> = actions.creates.iter().map(|create| create.nonce).collect();
504 for create in &actions.creates {
505 assert!(
506 self.position(create.nonce).is_none(),
507 "a child birth nonce must be fresh"
508 );
509 self.slots.push((
510 create.nonce,
511 Slot {
512 alive: true,
513 sequence: self.next_sequence,
514 },
515 ));
516 self.next_sequence = self
517 .next_sequence
518 .checked_add(1)
519 .expect("birth sequence exhausted");
520 }
521 Actions {
522 sends: SendProduct {
523 inner: actions.sends,
524 own: SendProduct {
525 inner: ServiceSends::new(
526 born.into_iter()
527 .map(|nonce| ObserveChild { nonce })
528 .collect(),
529 ),
530 own: Vec::new(),
531 },
532 },
533 creates: actions
534 .creates
535 .into_iter()
536 .map(|create| Create {
537 nonce: create.nonce,
538 child: Proxy::new(create.child),
539 })
540 .collect(),
541 become_: actions.become_,
542 }
543 }
544}
545
546impl<B, C, A, Ph, Sends> Behavior for Supervising<B, C>
547where
548 A: Address + Send,
549 Sends: SendAlgebra,
550 B: Behavior<
551 Addr = A,
552 Ph = Ph,
553 Sends = Sends,
554 Birth = Births<C>,
555 Effect = Actions<A, Ph, Sends, Births<C>>,
556 Done = Exit<A>,
557 > + Send,
558 B::Event: ChildEvent<B::Addr> + Send,
559 A::Nonce: From<u64> + Send,
560 B::Msg: Send,
561 C: Behavior<Ph = Never, Addr = B::Addr> + Send,
562{
563 type Addr = A;
564 type Msg = B::Msg;
565 type Event = SupervisionEvent<B::Event, B::Addr>;
566 type Sends = SupervisorSends<A, Sends, C>;
567 type Ph = Ph;
568 type Error = B::Error;
569 type Birth = Births<Proxy<C>>;
570 type Effect = Actions<A, Ph, Self::Sends, Births<Proxy<C>>>;
571 type Done = Exit<A>;
572
573 async fn init(&mut self) -> Result<Self::Effect, B::Error> {
574 let actions = self.inner.init().await?;
575 let mut actions = self.wrap(actions);
576 actions
577 .creates
578 .extend(self.slots[..self.configured_count].iter().enumerate().map(
579 |(index, (nonce, _))| Create {
580 nonce: *nonce,
581 child: Proxy::new((self.build)(index)),
582 },
583 ));
584 actions.sends.own.inner.extend(
585 self.slots[..self.configured_count]
586 .iter()
587 .map(|(nonce, _)| ObserveChild { nonce: *nonce }),
588 );
589 Ok(actions)
590 }
591
592 async fn step(&mut self, event: Self::Event) -> Result<Self::Effect, B::Error> {
593 match event {
594 SupervisionEvent::WorkerStopped(event) => Ok(Actions {
595 sends: SendProduct {
596 inner: B::Sends::empty(),
597 own: SendProduct {
598 inner: ServiceSends::empty(),
599 own: self.replacements(&event),
600 },
601 },
602 creates: Vec::new(),
603 become_: Step::Continue,
604 }),
605 SupervisionEvent::ChildStopped(event) => {
606 let dead = self
607 .position(event.nonce)
608 .expect("unknown supervised nonce");
609 self.slots[dead].1.alive = false;
610 Ok(Actions::cont())
611 }
612 SupervisionEvent::Inner(event) => {
613 let actions = self.inner.step(event).await?;
614 Ok(self.wrap(actions))
615 }
616 }
617 }
618}