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}