rename worker to actor
This commit is contained in:
		@@ -21,12 +21,12 @@ pub enum SupervisorError {
 | 
			
		||||
    InvalidInput(String),
 | 
			
		||||
    /// Job operation error
 | 
			
		||||
    JobError(hero_job::JobError),
 | 
			
		||||
    /// Worker lifecycle management errors
 | 
			
		||||
    WorkerStartFailed(String, String),
 | 
			
		||||
    WorkerStopFailed(String, String),
 | 
			
		||||
    WorkerRestartFailed(String, String),
 | 
			
		||||
    WorkerStatusFailed(String, String),
 | 
			
		||||
    WorkerNotFound(String),
 | 
			
		||||
    /// Actor lifecycle management errors
 | 
			
		||||
    ActorStartFailed(String, String),
 | 
			
		||||
    ActorStopFailed(String, String),
 | 
			
		||||
    ActorRestartFailed(String, String),
 | 
			
		||||
    ActorStatusFailed(String, String),
 | 
			
		||||
    ActorNotFound(String),
 | 
			
		||||
    PingJobFailed(String, String),
 | 
			
		||||
    /// Zinit client operation error
 | 
			
		||||
    ZinitError(String),
 | 
			
		||||
@@ -73,23 +73,23 @@ impl std::fmt::Display for SupervisorError {
 | 
			
		||||
            SupervisorError::JobError(e) => {
 | 
			
		||||
                write!(f, "Job error: {}", e)
 | 
			
		||||
            }
 | 
			
		||||
            SupervisorError::WorkerStartFailed(worker, reason) => {
 | 
			
		||||
                write!(f, "Failed to start worker '{}': {}", worker, reason)
 | 
			
		||||
            SupervisorError::ActorStartFailed(actor, reason) => {
 | 
			
		||||
                write!(f, "Failed to start actor '{}': {}", actor, reason)
 | 
			
		||||
            }
 | 
			
		||||
            SupervisorError::WorkerStopFailed(worker, reason) => {
 | 
			
		||||
                write!(f, "Failed to stop worker '{}': {}", worker, reason)
 | 
			
		||||
            SupervisorError::ActorStopFailed(actor, reason) => {
 | 
			
		||||
                write!(f, "Failed to stop actor '{}': {}", actor, reason)
 | 
			
		||||
            }
 | 
			
		||||
            SupervisorError::WorkerRestartFailed(worker, reason) => {
 | 
			
		||||
                write!(f, "Failed to restart worker '{}': {}", worker, reason)
 | 
			
		||||
            SupervisorError::ActorRestartFailed(actor, reason) => {
 | 
			
		||||
                write!(f, "Failed to restart actor '{}': {}", actor, reason)
 | 
			
		||||
            }
 | 
			
		||||
            SupervisorError::WorkerStatusFailed(worker, reason) => {
 | 
			
		||||
                write!(f, "Failed to get status for worker '{}': {}", worker, reason)
 | 
			
		||||
            SupervisorError::ActorStatusFailed(actor, reason) => {
 | 
			
		||||
                write!(f, "Failed to get status for actor '{}': {}", actor, reason)
 | 
			
		||||
            }
 | 
			
		||||
            SupervisorError::WorkerNotFound(worker) => {
 | 
			
		||||
                write!(f, "Worker '{}' not found", worker)
 | 
			
		||||
            SupervisorError::ActorNotFound(actor) => {
 | 
			
		||||
                write!(f, "Actor '{}' not found", actor)
 | 
			
		||||
            }
 | 
			
		||||
            SupervisorError::PingJobFailed(worker, reason) => {
 | 
			
		||||
                write!(f, "Ping job failed for worker '{}': {}", worker, reason)
 | 
			
		||||
            SupervisorError::PingJobFailed(actor, reason) => {
 | 
			
		||||
                write!(f, "Ping job failed for actor '{}': {}", actor, reason)
 | 
			
		||||
            }
 | 
			
		||||
            SupervisorError::ZinitError(msg) => {
 | 
			
		||||
                write!(f, "Zinit error: {}", msg)
 | 
			
		||||
 
 | 
			
		||||
@@ -16,7 +16,7 @@ mod lifecycle;
 | 
			
		||||
 | 
			
		||||
pub use crate::error::SupervisorError;
 | 
			
		||||
pub use crate::job::JobBuilder;
 | 
			
		||||
pub use crate::lifecycle::WorkerConfig;
 | 
			
		||||
pub use crate::lifecycle::ActorConfig;
 | 
			
		||||
// Re-export types from hero_job for public API
 | 
			
		||||
pub use hero_job::{Job, JobStatus, ScriptType};
 | 
			
		||||
 | 
			
		||||
@@ -28,22 +28,22 @@ pub struct Supervisor {
 | 
			
		||||
 | 
			
		||||
pub struct SupervisorBuilder {
 | 
			
		||||
    redis_url: Option<String>,
 | 
			
		||||
    osis_worker: Option<String>,
 | 
			
		||||
    sal_worker: Option<String>,
 | 
			
		||||
    v_worker: Option<String>,
 | 
			
		||||
    python_worker: Option<String>,
 | 
			
		||||
    worker_env_vars: HashMap<String, String>,
 | 
			
		||||
    osis_actor: Option<String>,
 | 
			
		||||
    sal_actor: Option<String>,
 | 
			
		||||
    v_actor: Option<String>,
 | 
			
		||||
    python_actor: Option<String>,
 | 
			
		||||
    actor_env_vars: HashMap<String, String>,
 | 
			
		||||
    websocket_config: Option<WebSocketServerConfig>,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Helper struct to pass builder data to worker launch method
 | 
			
		||||
/// Helper struct to pass builder data to actor launch method
 | 
			
		||||
#[derive(Clone)]
 | 
			
		||||
struct SupervisorBuilderData {
 | 
			
		||||
    osis_worker: Option<String>,
 | 
			
		||||
    sal_worker: Option<String>,
 | 
			
		||||
    v_worker: Option<String>,
 | 
			
		||||
    python_worker: Option<String>,
 | 
			
		||||
    worker_env_vars: HashMap<String, String>,
 | 
			
		||||
    osis_actor: Option<String>,
 | 
			
		||||
    sal_actor: Option<String>,
 | 
			
		||||
    v_actor: Option<String>,
 | 
			
		||||
    python_actor: Option<String>,
 | 
			
		||||
    actor_env_vars: HashMap<String, String>,
 | 
			
		||||
    websocket_config: Option<WebSocketServerConfig>,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -52,10 +52,10 @@ struct SupervisorBuilderData {
 | 
			
		||||
pub struct SupervisorConfig {
 | 
			
		||||
    pub global: GlobalConfig,
 | 
			
		||||
    pub websocket_server: Option<WebSocketServerConfig>,
 | 
			
		||||
    pub osis_worker: Option<WorkerConfigToml>,
 | 
			
		||||
    pub sal_worker: Option<WorkerConfigToml>,
 | 
			
		||||
    pub v_worker: Option<WorkerConfigToml>,
 | 
			
		||||
    pub python_worker: Option<WorkerConfigToml>,
 | 
			
		||||
    pub osis_actor: Option<ActorConfigToml>,
 | 
			
		||||
    pub sal_actor: Option<ActorConfigToml>,
 | 
			
		||||
    pub v_actor: Option<ActorConfigToml>,
 | 
			
		||||
    pub python_actor: Option<ActorConfigToml>,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Global configuration section
 | 
			
		||||
@@ -64,12 +64,10 @@ pub struct GlobalConfig {
 | 
			
		||||
    pub redis_url: String,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Worker configuration section in TOML
 | 
			
		||||
/// Actor configuration section in TOML
 | 
			
		||||
#[derive(Debug, Deserialize, Serialize)]
 | 
			
		||||
pub struct WorkerConfigToml {
 | 
			
		||||
pub struct ActorConfigToml {
 | 
			
		||||
    pub binary_path: String,
 | 
			
		||||
    #[serde(default)]
 | 
			
		||||
    pub env_vars: HashMap<String, String>,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// WebSocket server configuration section in TOML
 | 
			
		||||
@@ -127,11 +125,11 @@ impl SupervisorBuilder {
 | 
			
		||||
    pub fn new() -> Self {
 | 
			
		||||
        Self {
 | 
			
		||||
            redis_url: None,
 | 
			
		||||
            osis_worker: None,
 | 
			
		||||
            sal_worker: None,
 | 
			
		||||
            v_worker: None,
 | 
			
		||||
            python_worker: None,
 | 
			
		||||
            worker_env_vars: HashMap::new(),
 | 
			
		||||
            osis_actor: None,
 | 
			
		||||
            sal_actor: None,
 | 
			
		||||
            v_actor: None,
 | 
			
		||||
            python_actor: None,
 | 
			
		||||
            actor_env_vars: HashMap::new(),
 | 
			
		||||
            websocket_config: None,
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
@@ -147,25 +145,21 @@ impl SupervisorBuilder {
 | 
			
		||||
        let mut builder = Self::new()
 | 
			
		||||
            .redis_url(&config.global.redis_url);
 | 
			
		||||
        
 | 
			
		||||
        // Configure workers based on TOML config
 | 
			
		||||
        if let Some(osis_config) = config.osis_worker {
 | 
			
		||||
            builder = builder.osis_worker(&osis_config.binary_path)
 | 
			
		||||
                .worker_env_vars(osis_config.env_vars);
 | 
			
		||||
        // Configure actors based on TOML config
 | 
			
		||||
        if let Some(osis_config) = config.osis_actor {
 | 
			
		||||
            builder = builder.osis_actor(&osis_config.binary_path);
 | 
			
		||||
        }
 | 
			
		||||
        
 | 
			
		||||
        if let Some(sal_config) = config.sal_worker {
 | 
			
		||||
            builder = builder.sal_worker(&sal_config.binary_path)
 | 
			
		||||
                .worker_env_vars(sal_config.env_vars);
 | 
			
		||||
        if let Some(sal_config) = config.sal_actor {
 | 
			
		||||
            builder = builder.sal_actor(&sal_config.binary_path);
 | 
			
		||||
        }
 | 
			
		||||
        
 | 
			
		||||
        if let Some(v_config) = config.v_worker {
 | 
			
		||||
            builder = builder.v_worker(&v_config.binary_path)
 | 
			
		||||
                .worker_env_vars(v_config.env_vars);
 | 
			
		||||
        if let Some(v_config) = config.v_actor {
 | 
			
		||||
            builder = builder.v_actor(&v_config.binary_path);
 | 
			
		||||
        }
 | 
			
		||||
        
 | 
			
		||||
        if let Some(python_config) = config.python_worker {
 | 
			
		||||
            builder = builder.python_worker(&python_config.binary_path)
 | 
			
		||||
                .worker_env_vars(python_config.env_vars);
 | 
			
		||||
        if let Some(python_config) = config.python_actor {
 | 
			
		||||
            builder = builder.python_actor(&python_config.binary_path);
 | 
			
		||||
        }
 | 
			
		||||
        
 | 
			
		||||
        // Store WebSocket configuration for later use
 | 
			
		||||
@@ -176,28 +170,28 @@ impl SupervisorBuilder {
 | 
			
		||||
        Ok(builder)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Validate that all configured worker binaries exist and are executable
 | 
			
		||||
    fn validate_worker_binaries(&self) -> Result<(), SupervisorError> {
 | 
			
		||||
        let workers = [
 | 
			
		||||
            ("OSIS", &self.osis_worker),
 | 
			
		||||
            ("SAL", &self.sal_worker),
 | 
			
		||||
            ("V", &self.v_worker),
 | 
			
		||||
            ("Python", &self.python_worker),
 | 
			
		||||
    /// Validate that all configured actor binaries exist and are executable
 | 
			
		||||
    fn validate_actor_binaries(&self) -> Result<(), SupervisorError> {
 | 
			
		||||
        let actors = [
 | 
			
		||||
            ("OSIS", &self.osis_actor),
 | 
			
		||||
            ("SAL", &self.sal_actor),
 | 
			
		||||
            ("V", &self.v_actor),
 | 
			
		||||
            ("Python", &self.python_actor),
 | 
			
		||||
        ];
 | 
			
		||||
 | 
			
		||||
        for (worker_type, binary_path) in workers {
 | 
			
		||||
        for (actor_type, binary_path) in actors {
 | 
			
		||||
            if let Some(path) = binary_path {
 | 
			
		||||
                let path_obj = Path::new(path);
 | 
			
		||||
                
 | 
			
		||||
                if !path_obj.exists() {
 | 
			
		||||
                    return Err(SupervisorError::ConfigError(
 | 
			
		||||
                        format!("{} worker binary does not exist: {}", worker_type, path)
 | 
			
		||||
                        format!("{} actor binary does not exist: {}", actor_type, path)
 | 
			
		||||
                    ));
 | 
			
		||||
                }
 | 
			
		||||
                
 | 
			
		||||
                if !path_obj.is_file() {
 | 
			
		||||
                    return Err(SupervisorError::ConfigError(
 | 
			
		||||
                        format!("{} worker path is not a file: {}", worker_type, path)
 | 
			
		||||
                        format!("{} actor path is not a file: {}", actor_type, path)
 | 
			
		||||
                    ));
 | 
			
		||||
                }
 | 
			
		||||
                
 | 
			
		||||
@@ -207,19 +201,19 @@ impl SupervisorBuilder {
 | 
			
		||||
                    use std::os::unix::fs::PermissionsExt;
 | 
			
		||||
                    let metadata = path_obj.metadata().map_err(|e| {
 | 
			
		||||
                        SupervisorError::ConfigError(
 | 
			
		||||
                            format!("Failed to read metadata for {} worker binary {}: {}", worker_type, path, e)
 | 
			
		||||
                            format!("Failed to read metadata for {} actor binary {}: {}", actor_type, path, e)
 | 
			
		||||
                        )
 | 
			
		||||
                    })?;
 | 
			
		||||
                    
 | 
			
		||||
                    let permissions = metadata.permissions();
 | 
			
		||||
                    if permissions.mode() & 0o111 == 0 {
 | 
			
		||||
                        return Err(SupervisorError::ConfigError(
 | 
			
		||||
                            format!("{} worker binary is not executable: {}", worker_type, path)
 | 
			
		||||
                            format!("{} actor binary is not executable: {}", actor_type, path)
 | 
			
		||||
                        ));
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
                
 | 
			
		||||
                info!("Validated {} worker binary: {}", worker_type, path);
 | 
			
		||||
                info!("Validated {} actor binary: {}", actor_type, path);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        
 | 
			
		||||
@@ -231,48 +225,48 @@ impl SupervisorBuilder {
 | 
			
		||||
        self
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub fn osis_worker(mut self, binary_path: &str) -> Self {
 | 
			
		||||
        self.osis_worker = Some(binary_path.to_string());
 | 
			
		||||
    pub fn osis_actor(mut self, binary_path: &str) -> Self {
 | 
			
		||||
        self.osis_actor = Some(binary_path.to_string());
 | 
			
		||||
        self
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub fn sal_worker(mut self, binary_path: &str) -> Self {
 | 
			
		||||
        self.sal_worker = Some(binary_path.to_string());
 | 
			
		||||
    pub fn sal_actor(mut self, binary_path: &str) -> Self {
 | 
			
		||||
        self.sal_actor = Some(binary_path.to_string());
 | 
			
		||||
        self
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub fn v_worker(mut self, binary_path: &str) -> Self {
 | 
			
		||||
        self.v_worker = Some(binary_path.to_string());
 | 
			
		||||
    pub fn v_actor(mut self, binary_path: &str) -> Self {
 | 
			
		||||
        self.v_actor = Some(binary_path.to_string());
 | 
			
		||||
        self
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub fn python_worker(mut self, binary_path: &str) -> Self {
 | 
			
		||||
        self.python_worker = Some(binary_path.to_string());
 | 
			
		||||
    pub fn python_actor(mut self, binary_path: &str) -> Self {
 | 
			
		||||
        self.python_actor = Some(binary_path.to_string());
 | 
			
		||||
        self
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub fn worker_env_var(mut self, key: &str, value: &str) -> Self {
 | 
			
		||||
        self.worker_env_vars.insert(key.to_string(), value.to_string());
 | 
			
		||||
    pub fn actor_env_var(mut self, key: &str, value: &str) -> Self {
 | 
			
		||||
        self.actor_env_vars.insert(key.to_string(), value.to_string());
 | 
			
		||||
        self
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub fn worker_env_vars(mut self, env_vars: HashMap<String, String>) -> Self {
 | 
			
		||||
        self.worker_env_vars.extend(env_vars);
 | 
			
		||||
    pub fn actor_env_vars(mut self, env_vars: HashMap<String, String>) -> Self {
 | 
			
		||||
        self.actor_env_vars.extend(env_vars);
 | 
			
		||||
        self
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Builds the final `Supervisor` instance synchronously.
 | 
			
		||||
    ///
 | 
			
		||||
    /// This method validates the configuration, checks worker binary existence,
 | 
			
		||||
    /// and creates the Redis client. Worker launching is deferred to the `start_workers()` method.
 | 
			
		||||
    /// This method validates the configuration, checks actor binary existence,
 | 
			
		||||
    /// and creates the Redis client. Actor launching is deferred to the `start_actors()` method.
 | 
			
		||||
    ///
 | 
			
		||||
    /// # Returns
 | 
			
		||||
    ///
 | 
			
		||||
    /// * `Ok(Supervisor)` - Successfully configured client with valid binaries
 | 
			
		||||
    /// * `Err(SupervisorError)` - Configuration, binary validation, or connection error
 | 
			
		||||
    pub async fn build(self) -> Result<Supervisor, SupervisorError> {
 | 
			
		||||
        // Validate that all configured worker binaries exist first
 | 
			
		||||
        Self::validate_worker_binaries(&self)?;
 | 
			
		||||
        // Validate that all configured actor binaries exist first
 | 
			
		||||
        Self::validate_actor_binaries(&self)?;
 | 
			
		||||
        
 | 
			
		||||
        let url = self.redis_url
 | 
			
		||||
            .unwrap_or_else(|| "redis://127.0.0.1/".to_string());
 | 
			
		||||
@@ -281,13 +275,13 @@ impl SupervisorBuilder {
 | 
			
		||||
        let zinit_client = ZinitClient::unix_socket("/tmp/zinit.sock").await
 | 
			
		||||
            .map_err(|e| SupervisorError::ZinitError(format!("Failed to create Zinit client: {}", e)))?;
 | 
			
		||||
        
 | 
			
		||||
        // Store builder data for later use in start_workers()
 | 
			
		||||
        // Store builder data for later use in start_actors()
 | 
			
		||||
        let builder_data = SupervisorBuilderData {
 | 
			
		||||
            osis_worker: self.osis_worker,
 | 
			
		||||
            sal_worker: self.sal_worker,
 | 
			
		||||
            v_worker: self.v_worker,
 | 
			
		||||
            python_worker: self.python_worker,
 | 
			
		||||
            worker_env_vars: self.worker_env_vars,
 | 
			
		||||
            osis_actor: self.osis_actor,
 | 
			
		||||
            sal_actor: self.sal_actor,
 | 
			
		||||
            v_actor: self.v_actor,
 | 
			
		||||
            python_actor: self.python_actor,
 | 
			
		||||
            actor_env_vars: self.actor_env_vars,
 | 
			
		||||
            websocket_config: self.websocket_config,
 | 
			
		||||
        };
 | 
			
		||||
        
 | 
			
		||||
@@ -302,10 +296,10 @@ impl SupervisorBuilder {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl Supervisor {
 | 
			
		||||
    /// Start all configured workers asynchronously.
 | 
			
		||||
    /// This method should be called after build() to launch the workers.
 | 
			
		||||
    pub async fn start_workers(&self) -> Result<(), SupervisorError> {
 | 
			
		||||
        info!("Starting Hero Supervisor workers...");
 | 
			
		||||
    /// Start all configured actors asynchronously.
 | 
			
		||||
    /// This method should be called after build() to launch the actors.
 | 
			
		||||
    pub async fn start_actors(&self) -> Result<(), SupervisorError> {
 | 
			
		||||
        info!("Starting Hero Supervisor actors...");
 | 
			
		||||
        
 | 
			
		||||
        // Test Zinit connection first
 | 
			
		||||
        info!("Testing Zinit connection at /tmp/zinit.sock...");
 | 
			
		||||
@@ -319,102 +313,102 @@ impl Supervisor {
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        
 | 
			
		||||
        // Clean up any existing worker services first
 | 
			
		||||
        info!("Cleaning up existing worker services...");
 | 
			
		||||
        self.cleanup_existing_workers().await?;
 | 
			
		||||
        // Clean up any existing actor services first
 | 
			
		||||
        info!("Cleaning up existing actor services...");
 | 
			
		||||
        self.cleanup_existing_actors().await?;
 | 
			
		||||
        
 | 
			
		||||
        // Launch configured workers if builder data is available
 | 
			
		||||
        // Launch configured actors if builder data is available
 | 
			
		||||
        if let Some(builder_data) = &self.builder_data {
 | 
			
		||||
            info!("Launching configured workers...");
 | 
			
		||||
            self.launch_configured_workers(builder_data).await?;
 | 
			
		||||
            info!("Launching configured actors...");
 | 
			
		||||
            self.launch_configured_actors(builder_data).await?;
 | 
			
		||||
        } else {
 | 
			
		||||
            warn!("No builder data available, no workers to start");
 | 
			
		||||
            warn!("No builder data available, no actors to start");
 | 
			
		||||
        }
 | 
			
		||||
        
 | 
			
		||||
        info!("All workers started successfully!");
 | 
			
		||||
        info!("All actors started successfully!");
 | 
			
		||||
        Ok(())
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    /// Clean up all worker services from zinit on program exit
 | 
			
		||||
    /// Clean up all actor services from zinit on program exit
 | 
			
		||||
    pub async fn cleanup_and_shutdown(&self) -> Result<(), SupervisorError> {
 | 
			
		||||
        info!("Cleaning up worker services before shutdown...");
 | 
			
		||||
        info!("Cleaning up actor services before shutdown...");
 | 
			
		||||
        
 | 
			
		||||
        let worker_names = vec![
 | 
			
		||||
            "osis_worker_1",
 | 
			
		||||
            "sal_worker_1", 
 | 
			
		||||
            "v_worker_1",
 | 
			
		||||
            "python_worker_1"
 | 
			
		||||
        let actor_names = vec![
 | 
			
		||||
            "osis_actor_1",
 | 
			
		||||
            "sal_actor_1", 
 | 
			
		||||
            "v_actor_1",
 | 
			
		||||
            "python_actor_1"
 | 
			
		||||
        ];
 | 
			
		||||
        
 | 
			
		||||
        for worker_name in worker_names {
 | 
			
		||||
            if let Err(e) = self.stop_and_delete_worker(worker_name).await {
 | 
			
		||||
                warn!("Failed to cleanup worker {}: {}", worker_name, e);
 | 
			
		||||
        for actor_name in actor_names {
 | 
			
		||||
            if let Err(e) = self.stop_and_delete_actor(actor_name).await {
 | 
			
		||||
                warn!("Failed to cleanup actor {}: {}", actor_name, e);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        
 | 
			
		||||
        info!("Worker cleanup completed");
 | 
			
		||||
        info!("Actor cleanup completed");
 | 
			
		||||
        Ok(())
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    /// Clean up any existing worker services on startup
 | 
			
		||||
    async fn cleanup_existing_workers(&self) -> Result<(), SupervisorError> {
 | 
			
		||||
        info!("Cleaning up any existing worker services...");
 | 
			
		||||
    /// Clean up any existing actor services on startup
 | 
			
		||||
    async fn cleanup_existing_actors(&self) -> Result<(), SupervisorError> {
 | 
			
		||||
        info!("Cleaning up any existing actor services...");
 | 
			
		||||
        
 | 
			
		||||
        let worker_names = vec![
 | 
			
		||||
            "osis_worker_1",
 | 
			
		||||
            "sal_worker_1", 
 | 
			
		||||
            "v_worker_1",
 | 
			
		||||
            "python_worker_1"
 | 
			
		||||
        let actor_names = vec![
 | 
			
		||||
            "osis_actor_1",
 | 
			
		||||
            "sal_actor_1", 
 | 
			
		||||
            "v_actor_1",
 | 
			
		||||
            "python_actor_1"
 | 
			
		||||
        ];
 | 
			
		||||
        
 | 
			
		||||
        for worker_name in worker_names {
 | 
			
		||||
        for actor_name in actor_names {
 | 
			
		||||
            // Try to stop and delete, but don't fail if they don't exist
 | 
			
		||||
            info!("Attempting to cleanup worker: {}", worker_name);
 | 
			
		||||
            match self.stop_and_delete_worker(worker_name).await {
 | 
			
		||||
                Ok(_) => info!("Successfully cleaned up worker: {}", worker_name),
 | 
			
		||||
                Err(e) => debug!("Failed to cleanup worker {}: {}", worker_name, e),
 | 
			
		||||
            info!("Attempting to cleanup actor: {}", actor_name);
 | 
			
		||||
            match self.stop_and_delete_actor(actor_name).await {
 | 
			
		||||
                Ok(_) => info!("Successfully cleaned up actor: {}", actor_name),
 | 
			
		||||
                Err(e) => debug!("Failed to cleanup actor {}: {}", actor_name, e),
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        
 | 
			
		||||
        info!("Existing worker cleanup completed");
 | 
			
		||||
        info!("Existing actor cleanup completed");
 | 
			
		||||
        Ok(())
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    /// Stop and delete a worker service from zinit
 | 
			
		||||
    async fn stop_and_delete_worker(&self, worker_name: &str) -> Result<(), SupervisorError> {
 | 
			
		||||
        info!("Starting cleanup for worker: {}", worker_name);
 | 
			
		||||
    /// Stop and delete a actor service from zinit
 | 
			
		||||
    async fn stop_and_delete_actor(&self, actor_name: &str) -> Result<(), SupervisorError> {
 | 
			
		||||
        info!("Starting cleanup for actor: {}", actor_name);
 | 
			
		||||
        
 | 
			
		||||
        // First try to stop the worker
 | 
			
		||||
        info!("Attempting to stop worker: {}", worker_name);
 | 
			
		||||
        if let Err(e) = self.zinit_client.stop(worker_name).await {
 | 
			
		||||
            debug!("Worker {} was not running or failed to stop: {}", worker_name, e);
 | 
			
		||||
        // First try to stop the actor
 | 
			
		||||
        info!("Attempting to stop actor: {}", actor_name);
 | 
			
		||||
        if let Err(e) = self.zinit_client.stop(actor_name).await {
 | 
			
		||||
            debug!("Actor {} was not running or failed to stop: {}", actor_name, e);
 | 
			
		||||
        } else {
 | 
			
		||||
            info!("Successfully stopped worker: {}", worker_name);
 | 
			
		||||
            info!("Successfully stopped actor: {}", actor_name);
 | 
			
		||||
        }
 | 
			
		||||
        
 | 
			
		||||
        // Then forget the service to stop monitoring it
 | 
			
		||||
        info!("Attempting to forget worker: {}", worker_name);
 | 
			
		||||
        if let Err(e) = self.zinit_client.forget(worker_name).await {
 | 
			
		||||
            info!("Worker {} was not being monitored or failed to forget: {}", worker_name, e);
 | 
			
		||||
        info!("Attempting to forget actor: {}", actor_name);
 | 
			
		||||
        if let Err(e) = self.zinit_client.forget(actor_name).await {
 | 
			
		||||
            info!("Actor {} was not being monitored or failed to forget: {}", actor_name, e);
 | 
			
		||||
        } else {
 | 
			
		||||
            info!("Successfully forgot worker service: {}", worker_name);
 | 
			
		||||
            info!("Successfully forgot actor service: {}", actor_name);
 | 
			
		||||
        }
 | 
			
		||||
        
 | 
			
		||||
        // Finally, delete the service configuration
 | 
			
		||||
        info!("Attempting to delete service for worker: {}", worker_name);
 | 
			
		||||
        if let Err(e) = self.zinit_client.delete_service(worker_name).await {
 | 
			
		||||
            debug!("Worker {} service did not exist or failed to delete: {}", worker_name, e);
 | 
			
		||||
        info!("Attempting to delete service for actor: {}", actor_name);
 | 
			
		||||
        if let Err(e) = self.zinit_client.delete_service(actor_name).await {
 | 
			
		||||
            debug!("Actor {} service did not exist or failed to delete: {}", actor_name, e);
 | 
			
		||||
        } else {
 | 
			
		||||
            info!("Successfully deleted worker service: {}", worker_name);
 | 
			
		||||
            info!("Successfully deleted actor service: {}", actor_name);
 | 
			
		||||
        }
 | 
			
		||||
        
 | 
			
		||||
        info!("Completed cleanup for worker: {}", worker_name);
 | 
			
		||||
        info!("Completed cleanup for actor: {}", actor_name);
 | 
			
		||||
        Ok(())
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Get the hardcoded worker queue key for the script type
 | 
			
		||||
    fn get_worker_queue_key(&self, script_type: &ScriptType) -> String {
 | 
			
		||||
        format!("{}worker_queue:{}", NAMESPACE_PREFIX, script_type.worker_queue_suffix())
 | 
			
		||||
    /// Get the hardcoded actor queue key for the script type
 | 
			
		||||
    fn get_actor_queue_key(&self, script_type: &ScriptType) -> String {
 | 
			
		||||
        format!("{}actor_queue:{}", NAMESPACE_PREFIX, script_type.actor_queue_suffix())
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub fn new_job(&self) -> JobBuilder {
 | 
			
		||||
@@ -432,63 +426,58 @@ impl Supervisor {
 | 
			
		||||
        })
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Extract worker configurations from the supervisor's builder data
 | 
			
		||||
    pub fn get_worker_configs(&self) -> Result<Vec<WorkerConfig>, SupervisorError> {
 | 
			
		||||
    /// Extract actor configurations from the supervisor's builder data
 | 
			
		||||
    pub fn get_actor_configs(&self) -> Result<Vec<ActorConfig>, SupervisorError> {
 | 
			
		||||
        let builder_data = self.builder_data.as_ref().ok_or_else(|| {
 | 
			
		||||
            SupervisorError::ConfigError("No builder data available for worker configs".to_string())
 | 
			
		||||
            SupervisorError::ConfigError("No builder data available for actor configs".to_string())
 | 
			
		||||
        })?;
 | 
			
		||||
        
 | 
			
		||||
        let mut configs = Vec::new();
 | 
			
		||||
        let env_vars = builder_data.worker_env_vars.clone();
 | 
			
		||||
        
 | 
			
		||||
        if let Some(osis_path) = &builder_data.osis_worker {
 | 
			
		||||
        if let Some(osis_path) = &builder_data.osis_actor {
 | 
			
		||||
            configs.push(
 | 
			
		||||
                WorkerConfig::new("osis_worker_1".to_string(), PathBuf::from(osis_path), ScriptType::OSIS)
 | 
			
		||||
                    .with_env(env_vars.clone())
 | 
			
		||||
                ActorConfig::new("osis_actor_1".to_string(), PathBuf::from(osis_path), ScriptType::OSIS)
 | 
			
		||||
            );
 | 
			
		||||
        }
 | 
			
		||||
        
 | 
			
		||||
        if let Some(sal_path) = &builder_data.sal_worker {
 | 
			
		||||
        if let Some(sal_path) = &builder_data.sal_actor {
 | 
			
		||||
            configs.push(
 | 
			
		||||
                WorkerConfig::new("sal_worker_1".to_string(), PathBuf::from(sal_path), ScriptType::SAL)
 | 
			
		||||
                    .with_env(env_vars.clone())
 | 
			
		||||
                ActorConfig::new("sal_actor_1".to_string(), PathBuf::from(sal_path), ScriptType::SAL)
 | 
			
		||||
            );
 | 
			
		||||
        }
 | 
			
		||||
        
 | 
			
		||||
        if let Some(v_path) = &builder_data.v_worker {
 | 
			
		||||
        if let Some(v_path) = &builder_data.v_actor {
 | 
			
		||||
            configs.push(
 | 
			
		||||
                WorkerConfig::new("v_worker_1".to_string(), PathBuf::from(v_path), ScriptType::V)
 | 
			
		||||
                    .with_env(env_vars.clone())
 | 
			
		||||
                ActorConfig::new("v_actor_1".to_string(), PathBuf::from(v_path), ScriptType::V)
 | 
			
		||||
            );
 | 
			
		||||
        }
 | 
			
		||||
        
 | 
			
		||||
        if let Some(python_path) = &builder_data.python_worker {
 | 
			
		||||
        if let Some(python_path) = &builder_data.python_actor {
 | 
			
		||||
            configs.push(
 | 
			
		||||
                WorkerConfig::new("python_worker_1".to_string(), PathBuf::from(python_path), ScriptType::Python)
 | 
			
		||||
                    .with_env(env_vars.clone())
 | 
			
		||||
                ActorConfig::new("python_actor_1".to_string(), PathBuf::from(python_path), ScriptType::Python)
 | 
			
		||||
            );
 | 
			
		||||
        }
 | 
			
		||||
        
 | 
			
		||||
        Ok(configs)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Spawn a background lifecycle manager that continuously monitors and maintains worker health
 | 
			
		||||
    /// Spawn a background lifecycle manager that continuously monitors and maintains actor health
 | 
			
		||||
    /// Returns a JoinHandle that can be used to stop the lifecycle manager
 | 
			
		||||
    pub fn spawn_lifecycle_manager(
 | 
			
		||||
        self: Arc<Self>,
 | 
			
		||||
        worker_configs: Vec<WorkerConfig>,
 | 
			
		||||
        actor_configs: Vec<ActorConfig>,
 | 
			
		||||
        health_check_interval: Duration,
 | 
			
		||||
    ) -> tokio::task::JoinHandle<Result<(), SupervisorError>> {
 | 
			
		||||
        let supervisor = self;
 | 
			
		||||
        
 | 
			
		||||
        tokio::spawn(async move {
 | 
			
		||||
            info!("Starting background lifecycle manager with {} workers", worker_configs.len());
 | 
			
		||||
            info!("Starting background lifecycle manager with {} actors", actor_configs.len());
 | 
			
		||||
            info!("Health check interval: {:?}", health_check_interval);
 | 
			
		||||
            
 | 
			
		||||
            // Initial worker startup
 | 
			
		||||
            info!("Performing initial worker startup...");
 | 
			
		||||
            if let Err(e) = supervisor.start_workers().await {
 | 
			
		||||
                error!("Failed to start workers during initialization: {}", e);
 | 
			
		||||
            // Initial actor startup
 | 
			
		||||
            info!("Performing initial actor startup...");
 | 
			
		||||
            if let Err(e) = supervisor.start_actors().await {
 | 
			
		||||
                error!("Failed to start actors during initialization: {}", e);
 | 
			
		||||
                return Err(e);
 | 
			
		||||
            }
 | 
			
		||||
            
 | 
			
		||||
@@ -499,12 +488,12 @@ impl Supervisor {
 | 
			
		||||
            loop {
 | 
			
		||||
                interval.tick().await;
 | 
			
		||||
                
 | 
			
		||||
                info!("Running periodic worker health check...");
 | 
			
		||||
                info!("Running periodic actor health check...");
 | 
			
		||||
                
 | 
			
		||||
                // Check each worker's health and restart if needed
 | 
			
		||||
                for worker_config in &worker_configs {
 | 
			
		||||
                    if let Err(e) = supervisor.check_and_restart_worker(worker_config).await {
 | 
			
		||||
                        error!("Failed to check/restart worker {}: {}", worker_config.name, e);
 | 
			
		||||
                // Check each actor's health and restart if needed
 | 
			
		||||
                for actor_config in &actor_configs {
 | 
			
		||||
                    if let Err(e) = supervisor.check_and_restart_actor(actor_config).await {
 | 
			
		||||
                        error!("Failed to check/restart actor {}: {}", actor_config.name, e);
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
                
 | 
			
		||||
@@ -513,59 +502,59 @@ impl Supervisor {
 | 
			
		||||
        })
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    /// Check a single worker's health and restart if needed
 | 
			
		||||
    async fn check_and_restart_worker(&self, worker_config: &WorkerConfig) -> Result<(), SupervisorError> {
 | 
			
		||||
        let worker_name = &worker_config.name;
 | 
			
		||||
    /// Check a single actor's health and restart if needed
 | 
			
		||||
    async fn check_and_restart_actor(&self, actor_config: &ActorConfig) -> Result<(), SupervisorError> {
 | 
			
		||||
        let actor_name = &actor_config.name;
 | 
			
		||||
        
 | 
			
		||||
        // Get worker status
 | 
			
		||||
        match self.zinit_client.status(worker_name).await {
 | 
			
		||||
        // Get actor status
 | 
			
		||||
        match self.zinit_client.status(actor_name).await {
 | 
			
		||||
            Ok(status) => {
 | 
			
		||||
                let is_healthy = status.state == "running" && status.pid > 0;
 | 
			
		||||
                
 | 
			
		||||
                if is_healthy {
 | 
			
		||||
                    debug!("Worker {} is healthy (state: {}, pid: {})", worker_name, status.state, status.pid);
 | 
			
		||||
                    debug!("Actor {} is healthy (state: {}, pid: {})", actor_name, status.state, status.pid);
 | 
			
		||||
                    
 | 
			
		||||
                    // Optionally send a ping job for deeper health check
 | 
			
		||||
                    if let Err(e) = self.send_ping_job(worker_config.script_type.clone()).await {
 | 
			
		||||
                        warn!("Ping job failed for worker {}: {}", worker_name, e);
 | 
			
		||||
                    if let Err(e) = self.send_ping_job(actor_config.script_type.clone()).await {
 | 
			
		||||
                        warn!("Ping job failed for actor {}: {}", actor_name, e);
 | 
			
		||||
                        // Note: We don't restart on ping failure as it might be temporary
 | 
			
		||||
                    }
 | 
			
		||||
                } else {
 | 
			
		||||
                    warn!("Worker {} is unhealthy (state: {}, pid: {}), restarting...", 
 | 
			
		||||
                          worker_name, status.state, status.pid);
 | 
			
		||||
                    warn!("Actor {} is unhealthy (state: {}, pid: {}), restarting...", 
 | 
			
		||||
                          actor_name, status.state, status.pid);
 | 
			
		||||
                    
 | 
			
		||||
                    // Attempt to restart the worker
 | 
			
		||||
                    if let Err(e) = self.restart_worker(worker_name).await {
 | 
			
		||||
                        error!("Failed to restart unhealthy worker {}: {}", worker_name, e);
 | 
			
		||||
                    // Attempt to restart the actor
 | 
			
		||||
                    if let Err(e) = self.restart_actor(actor_name).await {
 | 
			
		||||
                        error!("Failed to restart unhealthy actor {}: {}", actor_name, e);
 | 
			
		||||
                        
 | 
			
		||||
                        // If restart fails, try a full stop/start cycle
 | 
			
		||||
                        warn!("Attempting full stop/start cycle for worker: {}", worker_name);
 | 
			
		||||
                        if let Err(e) = self.stop_and_delete_worker(worker_name).await {
 | 
			
		||||
                            error!("Failed to stop worker {} during recovery: {}", worker_name, e);
 | 
			
		||||
                        warn!("Attempting full stop/start cycle for actor: {}", actor_name);
 | 
			
		||||
                        if let Err(e) = self.stop_and_delete_actor(actor_name).await {
 | 
			
		||||
                            error!("Failed to stop actor {} during recovery: {}", actor_name, e);
 | 
			
		||||
                        }
 | 
			
		||||
                        
 | 
			
		||||
                        if let Err(e) = self.start_worker(worker_config).await {
 | 
			
		||||
                            error!("Failed to start worker {} during recovery: {}", worker_name, e);
 | 
			
		||||
                        if let Err(e) = self.start_actor(actor_config).await {
 | 
			
		||||
                            error!("Failed to start actor {} during recovery: {}", actor_name, e);
 | 
			
		||||
                            return Err(e);
 | 
			
		||||
                        }
 | 
			
		||||
                        
 | 
			
		||||
                        info!("Successfully recovered worker: {}", worker_name);
 | 
			
		||||
                        info!("Successfully recovered actor: {}", actor_name);
 | 
			
		||||
                    } else {
 | 
			
		||||
                        info!("Successfully restarted worker: {}", worker_name);
 | 
			
		||||
                        info!("Successfully restarted actor: {}", actor_name);
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            Err(e) => {
 | 
			
		||||
                warn!("Could not get status for worker {} (may not exist): {}", worker_name, e);
 | 
			
		||||
                warn!("Could not get status for actor {} (may not exist): {}", actor_name, e);
 | 
			
		||||
                
 | 
			
		||||
                // Worker doesn't exist, try to start it
 | 
			
		||||
                info!("Attempting to start missing worker: {}", worker_name);
 | 
			
		||||
                if let Err(e) = self.start_worker(worker_config).await {
 | 
			
		||||
                    error!("Failed to start missing worker {}: {}", worker_name, e);
 | 
			
		||||
                // Actor doesn't exist, try to start it
 | 
			
		||||
                info!("Attempting to start missing actor: {}", actor_name);
 | 
			
		||||
                if let Err(e) = self.start_actor(actor_config).await {
 | 
			
		||||
                    error!("Failed to start missing actor {}: {}", actor_name, e);
 | 
			
		||||
                    return Err(e);
 | 
			
		||||
                }
 | 
			
		||||
                
 | 
			
		||||
                info!("Successfully started missing worker: {}", worker_name);
 | 
			
		||||
                info!("Successfully started missing actor: {}", actor_name);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        
 | 
			
		||||
@@ -597,18 +586,18 @@ impl Supervisor {
 | 
			
		||||
        job_id: String,
 | 
			
		||||
        script_type: &ScriptType
 | 
			
		||||
    ) -> Result<(), SupervisorError> {
 | 
			
		||||
        let worker_queue_key = self.get_worker_queue_key(script_type);
 | 
			
		||||
        let actor_queue_key = self.get_actor_queue_key(script_type);
 | 
			
		||||
 | 
			
		||||
        // lpush also infers its types, RV is typically i64 (length of list) or () depending on exact command variant
 | 
			
		||||
        // For `redis::AsyncCommands::lpush`, it's `RedisResult<R>` where R: FromRedisValue
 | 
			
		||||
        // Often this is the length of the list. Let's allow inference or specify if needed.
 | 
			
		||||
        let _: redis::RedisResult<i64> =
 | 
			
		||||
            conn.lpush(&worker_queue_key, job_id.clone()).await;
 | 
			
		||||
            conn.lpush(&actor_queue_key, job_id.clone()).await;
 | 
			
		||||
 | 
			
		||||
        Ok(())
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // Internal helper to await response from worker
 | 
			
		||||
    // Internal helper to await response from actor
 | 
			
		||||
    async fn await_response_from_connection(
 | 
			
		||||
        &self,
 | 
			
		||||
        conn: &mut redis::aio::MultiplexedConnection,
 | 
			
		||||
@@ -679,7 +668,7 @@ impl Supervisor {
 | 
			
		||||
        Ok(())
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // New method using dedicated reply queue with automatic worker selection
 | 
			
		||||
    // New method using dedicated reply queue with automatic actor selection
 | 
			
		||||
    pub async fn run_job_and_await_result(
 | 
			
		||||
        &self,
 | 
			
		||||
        job: &Job
 | 
			
		||||
@@ -782,7 +771,7 @@ impl Supervisor {
 | 
			
		||||
    pub async fn stop_job(&self, job_id: &str) -> Result<(), SupervisorError> {
 | 
			
		||||
        let mut conn = self.redis_client.get_multiplexed_async_connection().await?;
 | 
			
		||||
        
 | 
			
		||||
        // Get job details to determine script type and appropriate worker
 | 
			
		||||
        // Get job details to determine script type and appropriate actor
 | 
			
		||||
        let job_key = format!("{}job:{}", NAMESPACE_PREFIX, job_id);
 | 
			
		||||
        let job_data: std::collections::HashMap<String, String> = conn.hgetall(&job_key).await?;
 | 
			
		||||
        
 | 
			
		||||
@@ -798,7 +787,7 @@ impl Supervisor {
 | 
			
		||||
            .map_err(|e| SupervisorError::InvalidInput(format!("Invalid script type: {}", e)))?;
 | 
			
		||||
        
 | 
			
		||||
        // Use hardcoded stop queue key for this script type
 | 
			
		||||
        let stop_queue_key = format!("{}stop_queue:{}", NAMESPACE_PREFIX, script_type.worker_queue_suffix());
 | 
			
		||||
        let stop_queue_key = format!("{}stop_queue:{}", NAMESPACE_PREFIX, script_type.actor_queue_suffix());
 | 
			
		||||
        
 | 
			
		||||
        // Push job ID to the stop queue
 | 
			
		||||
        conn.lpush::<_, _, ()>(&stop_queue_key, job_id).await?;
 | 
			
		||||
@@ -931,7 +920,7 @@ impl Supervisor {
 | 
			
		||||
    /// Dispatch jobs that are ready (have all prerequisites completed)
 | 
			
		||||
    pub async fn dispatch_ready_jobs(&self, ready_job_ids: Vec<String>) -> Result<(), SupervisorError> {
 | 
			
		||||
        for job_id in ready_job_ids {
 | 
			
		||||
            // Get job data to determine script type and select worker
 | 
			
		||||
            // Get job data to determine script type and select actor
 | 
			
		||||
            let mut conn = self.redis_client.get_multiplexed_async_connection().await?;
 | 
			
		||||
            let job_key = format!("{}job:{}", NAMESPACE_PREFIX, job_id);
 | 
			
		||||
            let job_data: std::collections::HashMap<String, String> = conn.hgetall(&job_key).await?;
 | 
			
		||||
 
 | 
			
		||||
@@ -1,6 +1,6 @@
 | 
			
		||||
//! Worker lifecycle management functionality for the Hero Supervisor
 | 
			
		||||
//! Actor lifecycle management functionality for the Hero Supervisor
 | 
			
		||||
//!
 | 
			
		||||
//! This module provides worker process lifecycle management using Zinit as the process manager.
 | 
			
		||||
//! This module provides actor process lifecycle management using Zinit as the process manager.
 | 
			
		||||
//! All functionality is implemented as methods on the Supervisor struct for a clean API.
 | 
			
		||||
 | 
			
		||||
use log::{debug, error, info, warn};
 | 
			
		||||
@@ -12,28 +12,28 @@ use zinit_client::{Client as ZinitClient, Status};
 | 
			
		||||
use hero_job::ScriptType;
 | 
			
		||||
use crate::{Supervisor, SupervisorError};
 | 
			
		||||
 | 
			
		||||
/// Information about a worker including its configuration and current status
 | 
			
		||||
/// Information about a actor including its configuration and current status
 | 
			
		||||
#[derive(Debug, Clone)]
 | 
			
		||||
pub struct WorkerInfo {
 | 
			
		||||
    pub config: WorkerConfig,
 | 
			
		||||
pub struct ActorInfo {
 | 
			
		||||
    pub config: ActorConfig,
 | 
			
		||||
    pub status: Option<Status>,
 | 
			
		||||
    pub is_running: bool,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Configuration for a worker binary
 | 
			
		||||
/// Configuration for a actor binary
 | 
			
		||||
#[derive(Debug, Clone)]
 | 
			
		||||
pub struct WorkerConfig {
 | 
			
		||||
    /// Name of the worker service
 | 
			
		||||
pub struct ActorConfig {
 | 
			
		||||
    /// Name of the actor service
 | 
			
		||||
    pub name: String,
 | 
			
		||||
    /// Path to the worker binary
 | 
			
		||||
    /// Path to the actor binary
 | 
			
		||||
    pub binary_path: PathBuf,
 | 
			
		||||
    /// Script type this worker handles
 | 
			
		||||
    /// Script type this actor handles
 | 
			
		||||
    pub script_type: ScriptType,
 | 
			
		||||
    /// Command line arguments for the worker
 | 
			
		||||
    /// Command line arguments for the actor
 | 
			
		||||
    pub args: Vec<String>,
 | 
			
		||||
    /// Environment variables for the worker
 | 
			
		||||
    /// Environment variables for the actor
 | 
			
		||||
    pub env: HashMap<String, String>,
 | 
			
		||||
    /// Whether this worker should restart on exit
 | 
			
		||||
    /// Whether this actor should restart on exit
 | 
			
		||||
    pub restart_on_exit: bool,
 | 
			
		||||
    /// Health check command (optional)
 | 
			
		||||
    pub health_check: Option<String>,
 | 
			
		||||
@@ -41,7 +41,7 @@ pub struct WorkerConfig {
 | 
			
		||||
    pub dependencies: Vec<String>,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl WorkerConfig {
 | 
			
		||||
impl ActorConfig {
 | 
			
		||||
    pub fn new(name: String, binary_path: PathBuf, script_type: ScriptType) -> Self {
 | 
			
		||||
        Self {
 | 
			
		||||
            name,
 | 
			
		||||
@@ -81,122 +81,122 @@ impl WorkerConfig {
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Worker lifecycle management methods for Supervisor
 | 
			
		||||
/// Actor lifecycle management methods for Supervisor
 | 
			
		||||
impl Supervisor {
 | 
			
		||||
    /// Get all workers with their configuration and status - unified method
 | 
			
		||||
    pub async fn get_workers(&self, worker_configs: &[WorkerConfig]) -> Vec<WorkerInfo> {
 | 
			
		||||
        let mut workers = Vec::new();
 | 
			
		||||
    /// Get all actors with their configuration and status - unified method
 | 
			
		||||
    pub async fn get_actors(&self, actor_configs: &[ActorConfig]) -> Vec<ActorInfo> {
 | 
			
		||||
        let mut actors = Vec::new();
 | 
			
		||||
        
 | 
			
		||||
        for config in worker_configs {
 | 
			
		||||
        for config in actor_configs {
 | 
			
		||||
            let status = self.zinit_client.status(&config.name).await.ok();
 | 
			
		||||
            let is_running = status.as_ref()
 | 
			
		||||
                .map(|s| s.state == "running" && s.pid > 0)
 | 
			
		||||
                .unwrap_or(false);
 | 
			
		||||
            
 | 
			
		||||
            workers.push(WorkerInfo {
 | 
			
		||||
            actors.push(ActorInfo {
 | 
			
		||||
                config: config.clone(),
 | 
			
		||||
                status,
 | 
			
		||||
                is_running,
 | 
			
		||||
            });
 | 
			
		||||
        }
 | 
			
		||||
        
 | 
			
		||||
        workers
 | 
			
		||||
        actors
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Start a worker using Zinit
 | 
			
		||||
    pub async fn start_worker(
 | 
			
		||||
    /// Start a actor using Zinit
 | 
			
		||||
    pub async fn start_actor(
 | 
			
		||||
        &self,
 | 
			
		||||
        worker_config: &WorkerConfig,
 | 
			
		||||
        actor_config: &ActorConfig,
 | 
			
		||||
    ) -> Result<(), SupervisorError> {
 | 
			
		||||
        info!("Starting worker: {}", worker_config.name);
 | 
			
		||||
        info!("Starting actor: {}", actor_config.name);
 | 
			
		||||
        
 | 
			
		||||
        // Create service configuration for Zinit
 | 
			
		||||
        let service_config = self.create_service_config(worker_config);
 | 
			
		||||
        let service_config = self.create_service_config(actor_config);
 | 
			
		||||
        
 | 
			
		||||
        // Create the service in Zinit
 | 
			
		||||
        self.zinit_client.create_service(&worker_config.name, service_config).await
 | 
			
		||||
        self.zinit_client.create_service(&actor_config.name, service_config).await
 | 
			
		||||
            .map_err(|e| SupervisorError::ZinitError(format!("Failed to create service: {}", e)))?;
 | 
			
		||||
        
 | 
			
		||||
        // Monitor the service so Zinit starts managing it
 | 
			
		||||
        self.zinit_client.monitor(&worker_config.name).await
 | 
			
		||||
        self.zinit_client.monitor(&actor_config.name).await
 | 
			
		||||
            .map_err(|e| SupervisorError::ZinitError(format!("Failed to monitor service: {}", e)))?;
 | 
			
		||||
        
 | 
			
		||||
        // Start the service
 | 
			
		||||
        self.zinit_client.start(&worker_config.name).await
 | 
			
		||||
            .map_err(|e| SupervisorError::ZinitError(format!("Failed to start worker: {}", e)))?;
 | 
			
		||||
        self.zinit_client.start(&actor_config.name).await
 | 
			
		||||
            .map_err(|e| SupervisorError::ZinitError(format!("Failed to start actor: {}", e)))?;
 | 
			
		||||
        
 | 
			
		||||
        info!("Successfully started worker: {}", worker_config.name);
 | 
			
		||||
        info!("Successfully started actor: {}", actor_config.name);
 | 
			
		||||
        Ok(())
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Stop a worker using Zinit
 | 
			
		||||
    pub async fn stop_worker(
 | 
			
		||||
    /// Stop a actor using Zinit
 | 
			
		||||
    pub async fn stop_actor(
 | 
			
		||||
        &self,
 | 
			
		||||
        worker_name: &str,
 | 
			
		||||
        actor_name: &str,
 | 
			
		||||
    ) -> Result<(), SupervisorError> {
 | 
			
		||||
        info!("Stopping worker: {}", worker_name);
 | 
			
		||||
        info!("Stopping actor: {}", actor_name);
 | 
			
		||||
        
 | 
			
		||||
        match self.zinit_client.stop(worker_name).await {
 | 
			
		||||
        match self.zinit_client.stop(actor_name).await {
 | 
			
		||||
            Ok(_) => {
 | 
			
		||||
                info!("Successfully stopped worker: {}", worker_name);
 | 
			
		||||
                info!("Successfully stopped actor: {}", actor_name);
 | 
			
		||||
                Ok(())
 | 
			
		||||
            }
 | 
			
		||||
            Err(e) => {
 | 
			
		||||
                error!("Failed to stop worker {}: {}", worker_name, e);
 | 
			
		||||
                Err(SupervisorError::WorkerStopFailed(worker_name.to_string(), e.to_string()))
 | 
			
		||||
                error!("Failed to stop actor {}: {}", actor_name, e);
 | 
			
		||||
                Err(SupervisorError::ActorStopFailed(actor_name.to_string(), e.to_string()))
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Restart a worker using Zinit
 | 
			
		||||
    pub async fn restart_worker(
 | 
			
		||||
    /// Restart a actor using Zinit
 | 
			
		||||
    pub async fn restart_actor(
 | 
			
		||||
        &self,
 | 
			
		||||
        worker_name: &str,
 | 
			
		||||
        actor_name: &str,
 | 
			
		||||
    ) -> Result<(), SupervisorError> {
 | 
			
		||||
        info!("Restarting worker: {}", worker_name);
 | 
			
		||||
        info!("Restarting actor: {}", actor_name);
 | 
			
		||||
        
 | 
			
		||||
        match self.zinit_client.restart(worker_name).await {
 | 
			
		||||
        match self.zinit_client.restart(actor_name).await {
 | 
			
		||||
            Ok(_) => {
 | 
			
		||||
                info!("Successfully restarted worker: {}", worker_name);
 | 
			
		||||
                info!("Successfully restarted actor: {}", actor_name);
 | 
			
		||||
                Ok(())
 | 
			
		||||
            }
 | 
			
		||||
            Err(e) => {
 | 
			
		||||
                error!("Failed to restart worker {}: {}", worker_name, e);
 | 
			
		||||
                Err(SupervisorError::WorkerRestartFailed(worker_name.to_string(), e.to_string()))
 | 
			
		||||
                error!("Failed to restart actor {}: {}", actor_name, e);
 | 
			
		||||
                Err(SupervisorError::ActorRestartFailed(actor_name.to_string(), e.to_string()))
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Get status of a worker using Zinit
 | 
			
		||||
    pub async fn get_worker_status(
 | 
			
		||||
    /// Get status of a actor using Zinit
 | 
			
		||||
    pub async fn get_actor_status(
 | 
			
		||||
        &self,
 | 
			
		||||
        worker_name: &str,
 | 
			
		||||
        actor_name: &str,
 | 
			
		||||
        zinit_client: &ZinitClient,
 | 
			
		||||
    ) -> Result<Status, SupervisorError> {
 | 
			
		||||
        match zinit_client.status(worker_name).await {
 | 
			
		||||
        match zinit_client.status(actor_name).await {
 | 
			
		||||
            Ok(status) => Ok(status),
 | 
			
		||||
            Err(e) => {
 | 
			
		||||
                error!("Failed to get status for worker {}: {}", worker_name, e);
 | 
			
		||||
                Err(SupervisorError::WorkerStatusFailed(worker_name.to_string(), e.to_string()))
 | 
			
		||||
                error!("Failed to get status for actor {}: {}", actor_name, e);
 | 
			
		||||
                Err(SupervisorError::ActorStatusFailed(actor_name.to_string(), e.to_string()))
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Get status of all workers
 | 
			
		||||
    pub async fn get_all_worker_status(
 | 
			
		||||
    /// Get status of all actors
 | 
			
		||||
    pub async fn get_all_actor_status(
 | 
			
		||||
        &self,
 | 
			
		||||
        worker_configs: &[WorkerConfig],
 | 
			
		||||
        actor_configs: &[ActorConfig],
 | 
			
		||||
        zinit_client: &ZinitClient,
 | 
			
		||||
    ) -> Result<HashMap<String, Status>, SupervisorError> {
 | 
			
		||||
        let mut status_map = HashMap::new();
 | 
			
		||||
        
 | 
			
		||||
        for worker in worker_configs {
 | 
			
		||||
            match zinit_client.status(&worker.name).await {
 | 
			
		||||
        for actor in actor_configs {
 | 
			
		||||
            match zinit_client.status(&actor.name).await {
 | 
			
		||||
                Ok(status) => {
 | 
			
		||||
                    status_map.insert(worker.name.clone(), status);
 | 
			
		||||
                    status_map.insert(actor.name.clone(), status);
 | 
			
		||||
                }
 | 
			
		||||
                Err(e) => {
 | 
			
		||||
                    warn!("Failed to get status for worker {}: {}", worker.name, e);
 | 
			
		||||
                    warn!("Failed to get status for actor {}: {}", actor.name, e);
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
@@ -206,32 +206,32 @@ impl Supervisor {
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    /// Stop multiple workers
 | 
			
		||||
    pub async fn stop_workers(
 | 
			
		||||
    /// Stop multiple actors
 | 
			
		||||
    pub async fn stop_actors(
 | 
			
		||||
        &self,
 | 
			
		||||
        worker_names: &[String],
 | 
			
		||||
        actor_names: &[String],
 | 
			
		||||
    ) -> Result<(), SupervisorError> {
 | 
			
		||||
        info!("Stopping {} workers", worker_names.len());
 | 
			
		||||
        info!("Stopping {} actors", actor_names.len());
 | 
			
		||||
        
 | 
			
		||||
        for worker_name in worker_names {
 | 
			
		||||
            self.stop_worker(worker_name).await?;
 | 
			
		||||
        for actor_name in actor_names {
 | 
			
		||||
            self.stop_actor(actor_name).await?;
 | 
			
		||||
        }
 | 
			
		||||
        
 | 
			
		||||
        Ok(())
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Get count of running workers for a script type
 | 
			
		||||
    pub async fn get_running_worker_count(
 | 
			
		||||
    /// Get count of running actors for a script type
 | 
			
		||||
    pub async fn get_running_actor_count(
 | 
			
		||||
        &self,
 | 
			
		||||
        worker_configs: &[WorkerConfig],
 | 
			
		||||
        actor_configs: &[ActorConfig],
 | 
			
		||||
        script_type: &ScriptType,
 | 
			
		||||
        zinit_client: &ZinitClient,
 | 
			
		||||
    ) -> usize {
 | 
			
		||||
        let mut running_count = 0;
 | 
			
		||||
        
 | 
			
		||||
        for worker in worker_configs {
 | 
			
		||||
            if worker.script_type == *script_type {
 | 
			
		||||
                if let Ok(status) = zinit_client.status(&worker.name).await {
 | 
			
		||||
        for actor in actor_configs {
 | 
			
		||||
            if actor.script_type == *script_type {
 | 
			
		||||
                if let Ok(status) = zinit_client.status(&actor.name).await {
 | 
			
		||||
                    if status.state == "running" {
 | 
			
		||||
                        running_count += 1;
 | 
			
		||||
                    }
 | 
			
		||||
@@ -242,7 +242,7 @@ impl Supervisor {
 | 
			
		||||
        running_count
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Send a ping job to a worker for health checking
 | 
			
		||||
    /// Send a ping job to a actor for health checking
 | 
			
		||||
    pub async fn send_ping_job(
 | 
			
		||||
        &self,
 | 
			
		||||
        script_type: ScriptType,
 | 
			
		||||
@@ -268,8 +268,8 @@ impl Supervisor {
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Create Zinit service configuration from worker config
 | 
			
		||||
    fn create_service_config(&self, worker: &WorkerConfig) -> serde_json::Map<String, serde_json::Value> {
 | 
			
		||||
    /// Create Zinit service configuration from actor config
 | 
			
		||||
    fn create_service_config(&self, actor: &ActorConfig) -> serde_json::Map<String, serde_json::Value> {
 | 
			
		||||
        use serde_json::{Map, Value};
 | 
			
		||||
        
 | 
			
		||||
        let mut config = Map::new();
 | 
			
		||||
@@ -277,117 +277,117 @@ impl Supervisor {
 | 
			
		||||
        config.insert(
 | 
			
		||||
            "exec".to_string(),
 | 
			
		||||
            Value::String(format!("{} {}", 
 | 
			
		||||
                worker.binary_path.display(), 
 | 
			
		||||
                worker.args.join(" ")
 | 
			
		||||
                actor.binary_path.display(), 
 | 
			
		||||
                actor.args.join(" ")
 | 
			
		||||
            ))
 | 
			
		||||
        );
 | 
			
		||||
        
 | 
			
		||||
        config.insert(
 | 
			
		||||
            "oneshot".to_string(),
 | 
			
		||||
            Value::Bool(!worker.restart_on_exit)
 | 
			
		||||
            Value::Bool(!actor.restart_on_exit)
 | 
			
		||||
        );
 | 
			
		||||
        
 | 
			
		||||
        if let Some(health_check) = &worker.health_check {
 | 
			
		||||
        if let Some(health_check) = &actor.health_check {
 | 
			
		||||
            config.insert("test".to_string(), Value::String(health_check.clone()));
 | 
			
		||||
        }
 | 
			
		||||
        
 | 
			
		||||
        if !worker.dependencies.is_empty() {
 | 
			
		||||
            config.insert("after".to_string(), json!(worker.dependencies));
 | 
			
		||||
        if !actor.dependencies.is_empty() {
 | 
			
		||||
            config.insert("after".to_string(), json!(actor.dependencies));
 | 
			
		||||
        }
 | 
			
		||||
        
 | 
			
		||||
        // Add environment variables if any
 | 
			
		||||
        if !worker.env.is_empty() {
 | 
			
		||||
            config.insert("env".to_string(), json!(worker.env));
 | 
			
		||||
        if !actor.env.is_empty() {
 | 
			
		||||
            config.insert("env".to_string(), json!(actor.env));
 | 
			
		||||
        }
 | 
			
		||||
        
 | 
			
		||||
        config
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Launch workers based on SupervisorBuilder configuration
 | 
			
		||||
    pub(crate) async fn launch_configured_workers(&self, builder: &crate::SupervisorBuilderData) -> Result<(), SupervisorError> {
 | 
			
		||||
    /// Launch actors based on SupervisorBuilder configuration
 | 
			
		||||
    pub(crate) async fn launch_configured_actors(&self, builder: &crate::SupervisorBuilderData) -> Result<(), SupervisorError> {
 | 
			
		||||
        use hero_job::ScriptType;
 | 
			
		||||
        use std::path::PathBuf;
 | 
			
		||||
        
 | 
			
		||||
        let mut errors = Vec::new();
 | 
			
		||||
        
 | 
			
		||||
        // Launch OSIS worker if configured
 | 
			
		||||
        if let Some(binary_path) = &builder.osis_worker {
 | 
			
		||||
            let worker_id = "osis_worker_1";
 | 
			
		||||
            let mut config = WorkerConfig::new(
 | 
			
		||||
                worker_id.to_string(),
 | 
			
		||||
        // Launch OSIS actor if configured
 | 
			
		||||
        if let Some(binary_path) = &builder.osis_actor {
 | 
			
		||||
            let actor_id = "osis_actor_1";
 | 
			
		||||
            let mut config = ActorConfig::new(
 | 
			
		||||
                actor_id.to_string(),
 | 
			
		||||
                PathBuf::from(binary_path),
 | 
			
		||||
                ScriptType::OSIS
 | 
			
		||||
            );
 | 
			
		||||
            config.env.extend(builder.worker_env_vars.clone());
 | 
			
		||||
            config.env.extend(builder.actor_env_vars.clone());
 | 
			
		||||
            
 | 
			
		||||
            info!("Launching OSIS worker: {}", worker_id);
 | 
			
		||||
            if let Err(e) = self.start_worker(&config).await {
 | 
			
		||||
                let error_msg = format!("Failed to start OSIS worker: {}", e);
 | 
			
		||||
            info!("Launching OSIS actor: {}", actor_id);
 | 
			
		||||
            if let Err(e) = self.start_actor(&config).await {
 | 
			
		||||
                let error_msg = format!("Failed to start OSIS actor: {}", e);
 | 
			
		||||
                warn!("{}", error_msg);
 | 
			
		||||
                errors.push(error_msg);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        
 | 
			
		||||
        // Launch SAL worker if configured
 | 
			
		||||
        if let Some(binary_path) = &builder.sal_worker {
 | 
			
		||||
            let worker_id = "sal_worker_1";
 | 
			
		||||
            let mut config = WorkerConfig::new(
 | 
			
		||||
                worker_id.to_string(),
 | 
			
		||||
        // Launch SAL actor if configured
 | 
			
		||||
        if let Some(binary_path) = &builder.sal_actor {
 | 
			
		||||
            let actor_id = "sal_actor_1";
 | 
			
		||||
            let mut config = ActorConfig::new(
 | 
			
		||||
                actor_id.to_string(),
 | 
			
		||||
                PathBuf::from(binary_path),
 | 
			
		||||
                ScriptType::SAL
 | 
			
		||||
            );
 | 
			
		||||
            config.env.extend(builder.worker_env_vars.clone());
 | 
			
		||||
            config.env.extend(builder.actor_env_vars.clone());
 | 
			
		||||
            
 | 
			
		||||
            info!("Launching SAL worker: {}", worker_id);
 | 
			
		||||
            if let Err(e) = self.start_worker(&config).await {
 | 
			
		||||
                let error_msg = format!("Failed to start SAL worker: {}", e);
 | 
			
		||||
            info!("Launching SAL actor: {}", actor_id);
 | 
			
		||||
            if let Err(e) = self.start_actor(&config).await {
 | 
			
		||||
                let error_msg = format!("Failed to start SAL actor: {}", e);
 | 
			
		||||
                warn!("{}", error_msg);
 | 
			
		||||
                errors.push(error_msg);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        
 | 
			
		||||
        // Launch V worker if configured
 | 
			
		||||
        if let Some(binary_path) = &builder.v_worker {
 | 
			
		||||
            let worker_id = "v_worker_1";
 | 
			
		||||
            let mut config = WorkerConfig::new(
 | 
			
		||||
                worker_id.to_string(),
 | 
			
		||||
        // Launch V actor if configured
 | 
			
		||||
        if let Some(binary_path) = &builder.v_actor {
 | 
			
		||||
            let actor_id = "v_actor_1";
 | 
			
		||||
            let mut config = ActorConfig::new(
 | 
			
		||||
                actor_id.to_string(),
 | 
			
		||||
                PathBuf::from(binary_path),
 | 
			
		||||
                ScriptType::V
 | 
			
		||||
            );
 | 
			
		||||
            config.env.extend(builder.worker_env_vars.clone());
 | 
			
		||||
            config.env.extend(builder.actor_env_vars.clone());
 | 
			
		||||
            
 | 
			
		||||
            info!("Launching V worker: {}", worker_id);
 | 
			
		||||
            if let Err(e) = self.start_worker(&config).await {
 | 
			
		||||
                let error_msg = format!("Failed to start V worker: {}", e);
 | 
			
		||||
            info!("Launching V actor: {}", actor_id);
 | 
			
		||||
            if let Err(e) = self.start_actor(&config).await {
 | 
			
		||||
                let error_msg = format!("Failed to start V actor: {}", e);
 | 
			
		||||
                warn!("{}", error_msg);
 | 
			
		||||
                errors.push(error_msg);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        
 | 
			
		||||
        // Launch Python worker if configured
 | 
			
		||||
        if let Some(binary_path) = &builder.python_worker {
 | 
			
		||||
            let worker_id = "python_worker_1";
 | 
			
		||||
            let mut config = WorkerConfig::new(
 | 
			
		||||
                worker_id.to_string(),
 | 
			
		||||
        // Launch Python actor if configured
 | 
			
		||||
        if let Some(binary_path) = &builder.python_actor {
 | 
			
		||||
            let actor_id = "python_actor_1";
 | 
			
		||||
            let mut config = ActorConfig::new(
 | 
			
		||||
                actor_id.to_string(),
 | 
			
		||||
                PathBuf::from(binary_path),
 | 
			
		||||
                ScriptType::Python
 | 
			
		||||
            );
 | 
			
		||||
            config.env.extend(builder.worker_env_vars.clone());
 | 
			
		||||
            config.env.extend(builder.actor_env_vars.clone());
 | 
			
		||||
            
 | 
			
		||||
            info!("Launching Python worker: {}", worker_id);
 | 
			
		||||
            if let Err(e) = self.start_worker(&config).await {
 | 
			
		||||
                let error_msg = format!("Failed to start Python worker: {}", e);
 | 
			
		||||
            info!("Launching Python actor: {}", actor_id);
 | 
			
		||||
            if let Err(e) = self.start_actor(&config).await {
 | 
			
		||||
                let error_msg = format!("Failed to start Python actor: {}", e);
 | 
			
		||||
                warn!("{}", error_msg);
 | 
			
		||||
                errors.push(error_msg);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        
 | 
			
		||||
        // Return result based on whether any workers started successfully
 | 
			
		||||
        // Return result based on whether any actors started successfully
 | 
			
		||||
        if errors.is_empty() {
 | 
			
		||||
            info!("All configured workers started successfully");
 | 
			
		||||
            info!("All configured actors started successfully");
 | 
			
		||||
            Ok(())
 | 
			
		||||
        } else {
 | 
			
		||||
            let combined_error = format!("Some workers failed to start: {}", errors.join("; "));
 | 
			
		||||
            let combined_error = format!("Some actors failed to start: {}", errors.join("; "));
 | 
			
		||||
            warn!("{}", combined_error);
 | 
			
		||||
            Err(SupervisorError::ZinitError(combined_error))
 | 
			
		||||
        }
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user