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