Skip to main content

apalis_core/monitor/
context.rs

1//! Monitor context and worker control.
2//!
3//! This module provides [`MonitorContext`], a shared handle for inspecting
4//! and controlling the workers supervised by a monitor.
5//!
6//! A monitor context provides access to the workers managed by the monitor and
7//! exposes operations for controlling their execution, including pausing,
8//! resuming, stopping, waking, and cancelling tasks.
9//!
10//! It also provides a non-blocking [`MonitorContext::shutdown`] operation for
11//! initiating shutdown of the monitor.
12//!
13//! # Worker control
14//!
15//! Workers are identified by name and can be controlled through the following
16//! methods:
17//!
18//! * [`MonitorContext::pause_worker`] pauses a worker.
19//! * [`MonitorContext::resume_worker`] resumes a paused worker.
20//! * [`MonitorContext::stop_worker`] stops a worker.
21//! * [`MonitorContext::wake_worker`] wakes a worker so it can continue polling.
22//!
23//! # Task cancellation
24//!
25//! [`MonitorContext::cancel_task`] cancels a task currently registered with a
26//! worker. The task is identified by both its worker name and task ID.
27//!
28//! # Shutdown
29//!
30//! [`MonitorContext::shutdown`] initiates monitor shutdown by signalling the
31//! monitor's shutdown handle. It does not wait for the supervised workers to
32//! finish stopping.
33//!
34//! # Errors
35//!
36//! Worker operations return [`WorkerError`] when the requested worker cannot
37//! be found or when the requested operation fails. Task cancellation may
38//! return a boxed error containing the underlying worker or task error.
39use crate::{
40    error::{WorkerError, WorkerStateError},
41    monitor::{MonitorError, shutdown::Shutdown},
42    worker::context::WorkerContext,
43};
44
45/// A handle to the monitor which allows probing
46/// Shared context for the monitor, providing access to the shutdown
47/// handle and the set of workers it supervises.
48#[derive(Debug, Clone)]
49pub struct MonitorContext {
50    /// Handle used to signal and coordinate shutdown of the monitor.
51    pub(super) shutdown: Shutdown,
52    /// The workers currently managed by this monitor.
53    pub(super) workers: Vec<WorkerContext>,
54}
55
56impl MonitorContext {
57    /// Returns a reference to all workers managed by this monitor.
58    #[must_use]
59    pub fn workers(&self) -> &Vec<WorkerContext> {
60        &self.workers
61    }
62
63    /// Initiates a shutdown of the monitor.
64    ///
65    /// This signals the shutdown handle but does not block waiting
66    /// for workers to finish.
67    pub fn shutdown(&self) -> Result<(), MonitorError> {
68        self.shutdown.start_shutdown();
69        // Setting the flag does not re-poll a parked worker; wake each one so it observes it.
70        for worker in &self.workers {
71            worker.wake();
72        }
73        Ok(())
74    }
75
76    /// Resumes the worker with the given `name`.
77    ///
78    /// # Errors
79    /// Returns [`WorkerError`] if no worker with `name` exists, or if
80    /// the resume itself fails.
81    pub fn resume_worker(&self, name: &str) -> Result<(), WorkerError> {
82        self.get_worker(name)?.resume()
83    }
84
85    /// Pauses the worker with the given `name`.
86    ///
87    /// # Errors
88    /// Returns [`WorkerError`] if no worker with `name` exists, or if
89    /// pausing fails.
90    pub fn pause_worker(&self, name: &str) -> Result<(), WorkerError> {
91        self.get_worker(name)?.pause()
92    }
93
94    /// Stops the worker with the given `name`.
95    ///
96    /// # Errors
97    /// Returns [`WorkerError`] if no worker with `name` exists, or if
98    /// stopping fails.
99    pub fn stop_worker(&self, name: &str) -> Result<(), WorkerError> {
100        self.get_worker(name)?.stop()
101    }
102
103    /// Wakes the worker with the given `name`.
104    ///
105    /// # Errors
106    /// Returns [`WorkerError`] if no worker with `name` exists.
107    pub fn wake_worker(&self, name: &str) -> Result<(), WorkerError> {
108        self.get_worker(name)?.wake();
109        Ok(())
110    }
111
112    /// Looks up a worker by name.
113    ///
114    /// # Errors
115    /// Returns [`WorkerError::StateError`] if no worker with `name`
116    /// exists among the managed workers.
117    pub fn get_worker(&self, name: &str) -> Result<&WorkerContext, WorkerError> {
118        self.workers
119            .iter()
120            .find(|w| w.name() == name)
121            .ok_or_else(|| {
122                WorkerError::StateError(WorkerStateError::InvalidState("NOT_FOUND".to_owned()))
123            })
124    }
125
126    /// Attempt to cancel a task
127    #[cfg(feature = "task-context")]
128    pub fn cancel_task(
129        &self,
130        worker: &str,
131        task_id: &str,
132    ) -> Result<(), crate::error::BoxDynError> {
133        let worker = self.get_worker(worker)?;
134        let context = worker.get_task(task_id)?;
135        worker.cancel_task(&context)?;
136        Ok(())
137    }
138}