witty_actors/command.rs
1// Copyright (C) 2023 Quickwit, Inc.
2//
3// Quickwit is offered under the AGPL v3.0 and as commercial software.
4// For commercial licensing, contact us at hello@quickwit.io.
5//
6// AGPL:
7// This program is free software: you can redistribute it and/or modify
8// it under the terms of the GNU Affero General Public License as
9// published by the Free Software Foundation, either version 3 of the
10// License, or (at your option) any later version.
11//
12// This program is distributed in the hope that it will be useful,
13// but WITHOUT ANY WARRANTY; without even the implied warranty of
14// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15// GNU Affero General Public License for more details.
16//
17// You should have received a copy of the GNU Affero General Public License
18// along with this program. If not, see <http://www.gnu.org/licenses/>.
19
20use async_trait::async_trait;
21
22use crate::{Actor, ActorContext, ActorExitStatus, Handler};
23
24/// Commands are messages that can be send to control the behavior of an actor.
25///
26/// They are similar to UNIX signals.
27///
28/// They are treated with a higher priority than regular actor messages.
29#[derive(Debug)]
30pub enum Command {
31 /// Temporarily pauses the actor. A paused actor only checks
32 /// on its high priority channel and still shows "progress". It appears as
33 /// healthy to the supervisor.
34 ///
35 /// Scheduled message are still processed.
36 ///
37 /// Semantically, it is similar to SIGSTOP.
38 Pause,
39
40 /// Resume a paused actor. If the actor was not paused this command
41 /// has no effects.
42 ///
43 /// Semantically, it is similar to SIGCONT.
44 Resume,
45
46 /// Stops the actor with a success exit status code.
47 ///
48 /// Upstream `actors` that terminates should send the `ExitWithSuccess`
49 /// command to downstream actors to inform them that there are no more
50 /// incoming messages.
51 ///
52 /// It is similar to `Quit`, except for the resulting exit status.
53 ExitWithSuccess,
54
55 /// Asks the actor to gracefully shutdown.
56 ///
57 /// The actor will stop processing messages and its finalize function will
58 /// be called.
59 ///
60 /// The exit status is then `ActorExitStatus::Quit`.
61 ///
62 /// This is the equivalent of sending SIGINT/Ctrl-C to a process.
63 Quit,
64
65 /// Nudging is a No-op message.
66 ///
67 /// Its only effect is to wake-up actors that are stuck waiting
68 /// for a message.
69 ///
70 /// This is useful to kill actors properly or for tests.
71 /// Actors stuck waiting for a message do not have any timeout to
72 /// check for their killswitch signal.
73 ///
74 ///
75 /// Note: Historically, actors used to have a timeout, then
76 /// the wake up logic worked using a Kill command.
77 /// However, after the introduction of supervision, it became common
78 /// to recycle a mailbox.
79 ///
80 /// After a panic for instance, the supervisor of an actor might kill
81 /// it by activating its killswitch and sending a Kill message.
82 ///
83 /// The respawned actor would receive its predecessor mailbox and
84 /// possibly end up process a Kill message as its first message.
85 Nudge,
86}
87
88#[async_trait]
89impl<A: Actor> Handler<Command> for A {
90 type Reply = ();
91
92 /// and its exit status will be the one defined in the error.
93 async fn handle(
94 &mut self,
95 command: Command,
96 ctx: &ActorContext<Self>,
97 ) -> Result<Self::Reply, ActorExitStatus> {
98 match command {
99 Command::Pause => {
100 ctx.pause();
101 Ok(())
102 }
103 Command::ExitWithSuccess => Err(ActorExitStatus::Success),
104 Command::Quit => Err(ActorExitStatus::Quit),
105 Command::Nudge => Ok(()),
106 Command::Resume => {
107 ctx.resume();
108 Ok(())
109 }
110 }
111 }
112}
113
114/// Asks the actor to update its ObservableState.
115///
116/// The observation is then available using the `ActorHandler::last_observation()`
117/// method.
118#[derive(Debug)]
119pub(crate) struct Observe;
120
121#[async_trait]
122impl<A: Actor> Handler<Observe> for A {
123 type Reply = A::ObservableState;
124
125 async fn handle(
126 &mut self,
127 _observe: Observe,
128 ctx: &ActorContext<Self>,
129 ) -> Result<Self::Reply, ActorExitStatus> {
130 Ok(ctx.observe(self))
131 }
132}