feat: add a very high amount of parallel processes
The library is not intended to handle this many tasks created so fast, and as such it is not optimized for. Generally we only allow a single modifier to the process manager at once. However, even then it can start and run a lot of tasks at once. Note that these processes, are comparable to pods / wasm containers / etc. And as such it wouldn't be realistic to run so many in parallel. Signed-off-by: kjuulh <contact@kjuulh.io>
This commit is contained in:
@@ -14,10 +14,14 @@ use tracing::{debug, info, warn};
|
||||
|
||||
pub use error::{BoxError, Error, ProcessFailure, ProcessFailureKind, ProcessResult};
|
||||
pub use handle_id::HandleID;
|
||||
pub use process::{Process, ProcessBuilder};
|
||||
pub use process::{Process, ProcessBuilder, ProcessState};
|
||||
pub use process_handler::ProcessHandler;
|
||||
pub use shutdown_config::ShutdownConfig;
|
||||
|
||||
/// Re-export tokio watch receiver for state subscriptions
|
||||
pub use tokio::sync::watch::Receiver as StateReceiver;
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct ProcessManager {
|
||||
inner: Arc<Mutex<Inner>>,
|
||||
}
|
||||
@@ -58,6 +62,57 @@ impl ProcessManager {
|
||||
pub async fn kill_process(&self, handle_id: &HandleID) -> Result<Option<()>, Error> {
|
||||
self.inner.lock().await.kill_process(handle_id).await
|
||||
}
|
||||
|
||||
/// Returns the current state of a process, or None if the process doesn't exist
|
||||
pub async fn get_process_state(&self, handle_id: &HandleID) -> Option<ProcessState> {
|
||||
self.inner.lock().await.get_process_state(handle_id)
|
||||
}
|
||||
|
||||
/// Returns a receiver that can be used to watch for state changes of a process.
|
||||
///
|
||||
/// Returns None if the process doesn't exist.
|
||||
///
|
||||
/// This is useful for building reconcilers that need to react to state changes.
|
||||
pub async fn subscribe_process_state(
|
||||
&self,
|
||||
handle_id: &HandleID,
|
||||
) -> Option<StateReceiver<ProcessState>> {
|
||||
self.inner.lock().await.subscribe_process_state(handle_id)
|
||||
}
|
||||
|
||||
/// Returns the state of all processes managed by this manager
|
||||
pub async fn get_all_process_states(&self) -> Vec<(HandleID, ProcessState)> {
|
||||
self.inner.lock().await.get_all_process_states()
|
||||
}
|
||||
|
||||
/// Returns a list of all process handle IDs
|
||||
pub async fn list_processes(&self) -> Vec<HandleID> {
|
||||
self.inner.lock().await.list_processes()
|
||||
}
|
||||
|
||||
/// Returns a list of processes that are in the errored state
|
||||
pub async fn get_errored_processes(&self) -> Vec<HandleID> {
|
||||
self.inner
|
||||
.lock()
|
||||
.await
|
||||
.get_processes_in_state(ProcessState::Errored)
|
||||
}
|
||||
|
||||
/// Returns a list of processes that are stopped
|
||||
pub async fn get_stopped_processes(&self) -> Vec<HandleID> {
|
||||
self.inner
|
||||
.lock()
|
||||
.await
|
||||
.get_processes_in_state(ProcessState::Stopped)
|
||||
}
|
||||
|
||||
/// Returns a list of processes that are currently running
|
||||
pub async fn get_running_processes(&self) -> Vec<HandleID> {
|
||||
self.inner
|
||||
.lock()
|
||||
.await
|
||||
.get_processes_in_state(ProcessState::Running)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
@@ -113,4 +168,34 @@ impl Inner {
|
||||
|
||||
Ok(None)
|
||||
}
|
||||
|
||||
fn get_process_state(&self, handle_id: &HandleID) -> Option<ProcessState> {
|
||||
self.handles.get(handle_id).map(|p| p.state())
|
||||
}
|
||||
|
||||
fn subscribe_process_state(
|
||||
&self,
|
||||
handle_id: &HandleID,
|
||||
) -> Option<tokio::sync::watch::Receiver<ProcessState>> {
|
||||
self.handles.get(handle_id).map(|p| p.subscribe_state())
|
||||
}
|
||||
|
||||
fn get_all_process_states(&self) -> Vec<(HandleID, ProcessState)> {
|
||||
self.handles
|
||||
.iter()
|
||||
.map(|(id, p)| (id.clone(), p.state()))
|
||||
.collect()
|
||||
}
|
||||
|
||||
fn list_processes(&self) -> Vec<HandleID> {
|
||||
self.handles.keys().cloned().collect()
|
||||
}
|
||||
|
||||
fn get_processes_in_state(&self, state: ProcessState) -> Vec<HandleID> {
|
||||
self.handles
|
||||
.iter()
|
||||
.filter(|(_, p)| p.state() == state)
|
||||
.map(|(id, _)| id.clone())
|
||||
.collect()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -251,7 +251,38 @@ impl Process {
|
||||
.map_err(|_| Error::RunnerTerminated)
|
||||
}
|
||||
|
||||
pub(crate) async fn wait_for(&self, desired: ProcessState) {
|
||||
/// Returns the current state of the process
|
||||
pub fn state(&self) -> ProcessState {
|
||||
*self.state.borrow()
|
||||
}
|
||||
|
||||
/// Returns a receiver that can be used to watch for state changes.
|
||||
///
|
||||
/// This is useful for building reconcilers that need to react to state changes.
|
||||
///
|
||||
/// # Example
|
||||
/// ```ignore
|
||||
/// let mut state_rx = process.subscribe_state();
|
||||
/// loop {
|
||||
/// let state = *state_rx.borrow_and_update();
|
||||
/// match state {
|
||||
/// ProcessState::Errored => {
|
||||
/// // Restart the process
|
||||
/// process.start().await?;
|
||||
/// }
|
||||
/// _ => {}
|
||||
/// }
|
||||
/// if state_rx.changed().await.is_err() {
|
||||
/// break; // Process was dropped
|
||||
/// }
|
||||
/// }
|
||||
/// ```
|
||||
pub fn subscribe_state(&self) -> watch::Receiver<ProcessState> {
|
||||
self.state.clone()
|
||||
}
|
||||
|
||||
/// Wait until the process reaches the desired state
|
||||
pub async fn wait_for(&self, desired: ProcessState) {
|
||||
let mut rx = self.state.clone();
|
||||
while *rx.borrow_and_update() != desired {
|
||||
if rx.changed().await.is_err() {
|
||||
@@ -260,6 +291,21 @@ impl Process {
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns true if the process is currently running
|
||||
pub fn is_running(&self) -> bool {
|
||||
self.state() == ProcessState::Running
|
||||
}
|
||||
|
||||
/// Returns true if the process has errored
|
||||
pub fn is_errored(&self) -> bool {
|
||||
self.state() == ProcessState::Errored
|
||||
}
|
||||
|
||||
/// Returns true if the process is stopped
|
||||
pub fn is_stopped(&self) -> bool {
|
||||
self.state() == ProcessState::Stopped
|
||||
}
|
||||
|
||||
pub fn handle_id(&self) -> &HandleID {
|
||||
&self.handle_id
|
||||
}
|
||||
@@ -414,10 +460,15 @@ enum ProcessCommand {
|
||||
Kill { response: oneshot::Sender<()> },
|
||||
}
|
||||
|
||||
#[derive(PartialEq)]
|
||||
pub(crate) enum ProcessState {
|
||||
/// The current state of a process
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||
pub enum ProcessState {
|
||||
/// Process has been created but not yet started
|
||||
Pending,
|
||||
/// Process is currently running
|
||||
Running,
|
||||
/// Process encountered an error and stopped
|
||||
Errored,
|
||||
/// Process was stopped (either manually or completed successfully)
|
||||
Stopped,
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user