rename worker to actor
This commit is contained in:
		
							
								
								
									
										113
									
								
								_archive/core/actor/cmd/README.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										113
									
								
								_archive/core/actor/cmd/README.md
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,113 @@
 | 
			
		||||
# Rhai Actor Binary
 | 
			
		||||
 | 
			
		||||
A command-line actor for executing Rhai scripts from Redis task queues.
 | 
			
		||||
 | 
			
		||||
## Binary: `actor`
 | 
			
		||||
 | 
			
		||||
### Installation
 | 
			
		||||
 | 
			
		||||
Build the binary:
 | 
			
		||||
```bash
 | 
			
		||||
cargo build --bin actor --release
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
### Usage
 | 
			
		||||
 | 
			
		||||
```bash
 | 
			
		||||
# Basic usage - requires circle public key
 | 
			
		||||
actor --circle-public-key <CIRCLE_PUBLIC_KEY>
 | 
			
		||||
 | 
			
		||||
# Custom Redis URL
 | 
			
		||||
actor -c <CIRCLE_PUBLIC_KEY> --redis-url redis://localhost:6379/1
 | 
			
		||||
 | 
			
		||||
# Custom actor ID and database path
 | 
			
		||||
actor -c <CIRCLE_PUBLIC_KEY> --actor-id my_actor --db-path /tmp/actor_db
 | 
			
		||||
 | 
			
		||||
# Preserve tasks for debugging/benchmarking
 | 
			
		||||
actor -c <CIRCLE_PUBLIC_KEY> --preserve-tasks
 | 
			
		||||
 | 
			
		||||
# Remove timestamps from logs
 | 
			
		||||
actor -c <CIRCLE_PUBLIC_KEY> --no-timestamp
 | 
			
		||||
 | 
			
		||||
# Increase verbosity
 | 
			
		||||
actor -c <CIRCLE_PUBLIC_KEY> -v    # Debug logging
 | 
			
		||||
actor -c <CIRCLE_PUBLIC_KEY> -vv   # Full debug
 | 
			
		||||
actor -c <CIRCLE_PUBLIC_KEY> -vvv  # Trace logging
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
### Command-Line Options
 | 
			
		||||
 | 
			
		||||
| Option | Short | Default | Description |
 | 
			
		||||
|--------|-------|---------|-------------|
 | 
			
		||||
| `--circle-public-key` | `-c` | **Required** | Circle public key to listen for tasks |
 | 
			
		||||
| `--redis-url` | `-r` | `redis://localhost:6379` | Redis connection URL |
 | 
			
		||||
| `--actor-id` | `-w` | `actor_1` | Unique actor identifier |
 | 
			
		||||
| `--preserve-tasks` | | `false` | Preserve task details after completion |
 | 
			
		||||
| `--db-path` | | `actor_rhai_temp_db` | Database path for Rhai engine |
 | 
			
		||||
| `--no-timestamp` | | `false` | Remove timestamps from log output |
 | 
			
		||||
| `--verbose` | `-v` | | Increase verbosity (stackable) |
 | 
			
		||||
 | 
			
		||||
### Features
 | 
			
		||||
 | 
			
		||||
- **Task Queue Processing**: Listens to Redis queues for Rhai script execution tasks
 | 
			
		||||
- **Performance Optimized**: Configured for maximum Rhai engine performance
 | 
			
		||||
- **Graceful Shutdown**: Supports shutdown signals for clean termination
 | 
			
		||||
- **Flexible Logging**: Configurable verbosity and timestamp control
 | 
			
		||||
- **Database Integration**: Uses heromodels for data persistence
 | 
			
		||||
- **Task Cleanup**: Optional task preservation for debugging/benchmarking
 | 
			
		||||
 | 
			
		||||
### How It Works
 | 
			
		||||
 | 
			
		||||
1. **Queue Listening**: Actor listens on Redis queue `baobab:{circle_public_key}`
 | 
			
		||||
2. **Task Processing**: Receives task IDs, fetches task details from Redis
 | 
			
		||||
3. **Script Execution**: Executes Rhai scripts with configured engine
 | 
			
		||||
4. **Result Handling**: Updates task status and sends results to reply queues
 | 
			
		||||
5. **Cleanup**: Optionally cleans up task details after completion
 | 
			
		||||
 | 
			
		||||
### Configuration Examples
 | 
			
		||||
 | 
			
		||||
#### Development Actor
 | 
			
		||||
```bash
 | 
			
		||||
# Simple development actor
 | 
			
		||||
actor -c dev_circle_123
 | 
			
		||||
 | 
			
		||||
# Development with verbose logging (no timestamps)
 | 
			
		||||
actor -c dev_circle_123 -v --no-timestamp
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
#### Production Actor
 | 
			
		||||
```bash
 | 
			
		||||
# Production actor with custom configuration
 | 
			
		||||
actor \
 | 
			
		||||
  --circle-public-key prod_circle_456 \
 | 
			
		||||
  --redis-url redis://redis-server:6379/0 \
 | 
			
		||||
  --actor-id prod_actor_1 \
 | 
			
		||||
  --db-path /var/lib/actor/db \
 | 
			
		||||
  --preserve-tasks
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
#### Benchmarking Actor
 | 
			
		||||
```bash
 | 
			
		||||
# Actor optimized for benchmarking
 | 
			
		||||
actor \
 | 
			
		||||
  --circle-public-key bench_circle_789 \
 | 
			
		||||
  --preserve-tasks \
 | 
			
		||||
  --no-timestamp \
 | 
			
		||||
  -vv
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
### Error Handling
 | 
			
		||||
 | 
			
		||||
The actor provides clear error messages for:
 | 
			
		||||
- Missing or invalid circle public key
 | 
			
		||||
- Redis connection failures
 | 
			
		||||
- Script execution errors
 | 
			
		||||
- Database access issues
 | 
			
		||||
 | 
			
		||||
### Dependencies
 | 
			
		||||
 | 
			
		||||
- `baobab_engine`: Rhai engine with heromodels integration
 | 
			
		||||
- `redis`: Redis client for task queue management
 | 
			
		||||
- `rhai`: Script execution engine
 | 
			
		||||
- `clap`: Command-line argument parsing
 | 
			
		||||
- `env_logger`: Logging infrastructure
 | 
			
		||||
							
								
								
									
										233
									
								
								_archive/core/actor/cmd/osis.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										233
									
								
								_archive/core/actor/cmd/osis.rs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,233 @@
 | 
			
		||||
//! OSIS Actor Binary - Synchronous actor for system-level operations
 | 
			
		||||
 | 
			
		||||
use clap::Parser;
 | 
			
		||||
use log::{error, info};
 | 
			
		||||
use baobab_actor::config::{ConfigError, ActorConfig};
 | 
			
		||||
use baobab_actor::engine::create_heromodels_engine;
 | 
			
		||||
use baobab_actor::sync_actor::SyncActor;
 | 
			
		||||
use baobab_actor::actor_trait::{spawn_actor, ActorConfig as TraitActorConfig};
 | 
			
		||||
use std::path::PathBuf;
 | 
			
		||||
use std::sync::Arc;
 | 
			
		||||
use tokio::signal;
 | 
			
		||||
use tokio::sync::mpsc;
 | 
			
		||||
 | 
			
		||||
#[derive(Parser, Debug)]
 | 
			
		||||
#[command(
 | 
			
		||||
    name = "osis",
 | 
			
		||||
    version = "0.1.0",
 | 
			
		||||
    about = "OSIS (Operating System Integration Service) - Synchronous Actor",
 | 
			
		||||
    long_about = "A synchronous actor for Hero framework that processes jobs sequentially. \
 | 
			
		||||
                  Ideal for system-level operations that require careful resource management."
 | 
			
		||||
)]
 | 
			
		||||
struct Args {
 | 
			
		||||
    /// Path to TOML configuration file
 | 
			
		||||
    #[arg(short, long, help = "Path to TOML configuration file")]
 | 
			
		||||
    config: PathBuf,
 | 
			
		||||
 | 
			
		||||
    /// Override actor ID from config
 | 
			
		||||
    #[arg(long, help = "Override actor ID from configuration file")]
 | 
			
		||||
    actor_id: Option<String>,
 | 
			
		||||
 | 
			
		||||
    /// Override Redis URL from config
 | 
			
		||||
    #[arg(long, help = "Override Redis URL from configuration file")]
 | 
			
		||||
    redis_url: Option<String>,
 | 
			
		||||
 | 
			
		||||
    /// Override database path from config
 | 
			
		||||
    #[arg(long, help = "Override database path from configuration file")]
 | 
			
		||||
    db_path: Option<String>,
 | 
			
		||||
 | 
			
		||||
    /// Enable verbose logging (debug level)
 | 
			
		||||
    #[arg(short, long, help = "Enable verbose logging")]
 | 
			
		||||
    verbose: bool,
 | 
			
		||||
 | 
			
		||||
    /// Disable timestamps in log output
 | 
			
		||||
    #[arg(long, help = "Remove timestamps from log output")]
 | 
			
		||||
    no_timestamp: bool,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[tokio::main]
 | 
			
		||||
async fn main() -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
 | 
			
		||||
    let args = Args::parse();
 | 
			
		||||
 | 
			
		||||
    // Load configuration from TOML file
 | 
			
		||||
    let mut config = match ActorConfig::from_file(&args.config) {
 | 
			
		||||
        Ok(config) => config,
 | 
			
		||||
        Err(e) => {
 | 
			
		||||
            eprintln!("Failed to load configuration from {:?}: {}", args.config, e);
 | 
			
		||||
            std::process::exit(1);
 | 
			
		||||
        }
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    // Validate that this is a sync actor configuration
 | 
			
		||||
    if !config.is_sync() {
 | 
			
		||||
        eprintln!("Error: OSIS actor requires a sync actor configuration");
 | 
			
		||||
        eprintln!("Expected: [actor_type] type = \"sync\"");
 | 
			
		||||
        eprintln!("Found: {:?}", config.actor_type);
 | 
			
		||||
        std::process::exit(1);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // Apply command line overrides
 | 
			
		||||
    if let Some(actor_id) = args.actor_id {
 | 
			
		||||
        config.actor_id = actor_id;
 | 
			
		||||
    }
 | 
			
		||||
    if let Some(redis_url) = args.redis_url {
 | 
			
		||||
        config.redis_url = redis_url;
 | 
			
		||||
    }
 | 
			
		||||
    if let Some(db_path) = args.db_path {
 | 
			
		||||
        config.db_path = db_path;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // Configure logging
 | 
			
		||||
    setup_logging(&config, args.verbose, args.no_timestamp)?;
 | 
			
		||||
 | 
			
		||||
    info!("🚀 OSIS Actor starting...");
 | 
			
		||||
    info!("Actor ID: {}", config.actor_id);
 | 
			
		||||
    info!("Redis URL: {}", config.redis_url);
 | 
			
		||||
    info!("Database Path: {}", config.db_path);
 | 
			
		||||
    info!("Preserve Tasks: {}", config.preserve_tasks);
 | 
			
		||||
 | 
			
		||||
    // Create Rhai engine
 | 
			
		||||
    let engine = create_heromodels_engine();
 | 
			
		||||
    info!("✅ Rhai engine initialized");
 | 
			
		||||
 | 
			
		||||
    // Create actor configuration for the trait-based interface
 | 
			
		||||
    let actor_config = TraitActorConfig::new(
 | 
			
		||||
        config.actor_id.clone(),
 | 
			
		||||
        config.db_path.clone(),
 | 
			
		||||
        config.redis_url.clone(),
 | 
			
		||||
        config.preserve_tasks,
 | 
			
		||||
    );
 | 
			
		||||
 | 
			
		||||
    // Create sync actor instance
 | 
			
		||||
    let actor = Arc::new(SyncActor::default());
 | 
			
		||||
    info!("✅ Sync actor instance created");
 | 
			
		||||
 | 
			
		||||
    // Setup shutdown signal handling
 | 
			
		||||
    let (shutdown_tx, shutdown_rx) = mpsc::channel(1);
 | 
			
		||||
    
 | 
			
		||||
    // Spawn shutdown signal handler
 | 
			
		||||
    let shutdown_tx_clone = shutdown_tx.clone();
 | 
			
		||||
    tokio::spawn(async move {
 | 
			
		||||
        if let Err(e) = signal::ctrl_c().await {
 | 
			
		||||
            error!("Failed to listen for shutdown signal: {}", e);
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
        info!("🛑 Shutdown signal received");
 | 
			
		||||
        if let Err(e) = shutdown_tx_clone.send(()).await {
 | 
			
		||||
            error!("Failed to send shutdown signal: {}", e);
 | 
			
		||||
        }
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    // Spawn the actor
 | 
			
		||||
    info!("🔄 Starting actor loop...");
 | 
			
		||||
    let actor_handle = spawn_actor(actor, engine, shutdown_rx);
 | 
			
		||||
 | 
			
		||||
    // Wait for the actor to complete
 | 
			
		||||
    match actor_handle.await {
 | 
			
		||||
        Ok(Ok(())) => {
 | 
			
		||||
            info!("✅ OSIS Actor shut down gracefully");
 | 
			
		||||
        }
 | 
			
		||||
        Ok(Err(e)) => {
 | 
			
		||||
            error!("❌ OSIS Actor encountered an error: {}", e);
 | 
			
		||||
            std::process::exit(1);
 | 
			
		||||
        }
 | 
			
		||||
        Err(e) => {
 | 
			
		||||
            error!("❌ Failed to join actor task: {}", e);
 | 
			
		||||
            std::process::exit(1);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    Ok(())
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Setup logging based on configuration and command line arguments
 | 
			
		||||
fn setup_logging(
 | 
			
		||||
    config: &ActorConfig,
 | 
			
		||||
    verbose: bool,
 | 
			
		||||
    no_timestamp: bool,
 | 
			
		||||
) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
 | 
			
		||||
    let mut builder = env_logger::Builder::new();
 | 
			
		||||
 | 
			
		||||
    // Determine log level
 | 
			
		||||
    let log_level = if verbose {
 | 
			
		||||
        "debug"
 | 
			
		||||
    } else {
 | 
			
		||||
        &config.logging.level
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    // Set log level
 | 
			
		||||
    builder.filter_level(match log_level.to_lowercase().as_str() {
 | 
			
		||||
        "trace" => log::LevelFilter::Trace,
 | 
			
		||||
        "debug" => log::LevelFilter::Debug,
 | 
			
		||||
        "info" => log::LevelFilter::Info,
 | 
			
		||||
        "warn" => log::LevelFilter::Warn,
 | 
			
		||||
        "error" => log::LevelFilter::Error,
 | 
			
		||||
        _ => {
 | 
			
		||||
            eprintln!("Invalid log level: {}. Using 'info'", log_level);
 | 
			
		||||
            log::LevelFilter::Info
 | 
			
		||||
        }
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    // Configure timestamps
 | 
			
		||||
    let show_timestamps = !no_timestamp && config.logging.timestamps;
 | 
			
		||||
    if !show_timestamps {
 | 
			
		||||
        builder.format_timestamp(None);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    builder.init();
 | 
			
		||||
    Ok(())
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[cfg(test)]
 | 
			
		||||
mod tests {
 | 
			
		||||
    use super::*;
 | 
			
		||||
    use std::io::Write;
 | 
			
		||||
    use tempfile::NamedTempFile;
 | 
			
		||||
 | 
			
		||||
    #[test]
 | 
			
		||||
    fn test_config_validation() {
 | 
			
		||||
        let config_toml = r#"
 | 
			
		||||
actor_id = "test_osis"
 | 
			
		||||
redis_url = "redis://localhost:6379"
 | 
			
		||||
db_path = "/tmp/test_db"
 | 
			
		||||
 | 
			
		||||
[actor_type]
 | 
			
		||||
type = "sync"
 | 
			
		||||
 | 
			
		||||
[logging]
 | 
			
		||||
level = "info"
 | 
			
		||||
"#;
 | 
			
		||||
 | 
			
		||||
        let mut temp_file = NamedTempFile::new().unwrap();
 | 
			
		||||
        temp_file.write_all(config_toml.as_bytes()).unwrap();
 | 
			
		||||
 | 
			
		||||
        let config = ActorConfig::from_file(temp_file.path()).unwrap();
 | 
			
		||||
        assert!(config.is_sync());
 | 
			
		||||
        assert!(!config.is_async());
 | 
			
		||||
        assert_eq!(config.actor_id, "test_osis");
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    #[test]
 | 
			
		||||
    fn test_async_config_rejection() {
 | 
			
		||||
        let config_toml = r#"
 | 
			
		||||
actor_id = "test_osis"
 | 
			
		||||
redis_url = "redis://localhost:6379"
 | 
			
		||||
db_path = "/tmp/test_db"
 | 
			
		||||
 | 
			
		||||
[actor_type]
 | 
			
		||||
type = "async"
 | 
			
		||||
default_timeout_seconds = 300
 | 
			
		||||
 | 
			
		||||
[logging]
 | 
			
		||||
level = "info"
 | 
			
		||||
"#;
 | 
			
		||||
 | 
			
		||||
        let mut temp_file = NamedTempFile::new().unwrap();
 | 
			
		||||
        temp_file.write_all(config_toml.as_bytes()).unwrap();
 | 
			
		||||
 | 
			
		||||
        let config = ActorConfig::from_file(temp_file.path()).unwrap();
 | 
			
		||||
        assert!(!config.is_sync());
 | 
			
		||||
        assert!(config.is_async());
 | 
			
		||||
        // This would be rejected in main() function
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										302
									
								
								_archive/core/actor/cmd/system.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										302
									
								
								_archive/core/actor/cmd/system.rs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,302 @@
 | 
			
		||||
//! System Actor Binary - Asynchronous actor for high-throughput concurrent processing
 | 
			
		||||
 | 
			
		||||
use clap::Parser;
 | 
			
		||||
use log::{error, info, warn};
 | 
			
		||||
use baobab_actor::async_actor_impl::AsyncActor;
 | 
			
		||||
use baobab_actor::config::{ConfigError, ActorConfig};
 | 
			
		||||
use baobab_actor::engine::create_heromodels_engine;
 | 
			
		||||
use baobab_actor::actor_trait::{spawn_actor, ActorConfig as TraitActorConfig};
 | 
			
		||||
use std::path::PathBuf;
 | 
			
		||||
use std::sync::Arc;
 | 
			
		||||
use std::time::Duration;
 | 
			
		||||
use tokio::signal;
 | 
			
		||||
use tokio::sync::mpsc;
 | 
			
		||||
 | 
			
		||||
#[derive(Parser, Debug)]
 | 
			
		||||
#[command(
 | 
			
		||||
    name = "system",
 | 
			
		||||
    version = "0.1.0",
 | 
			
		||||
    about = "System Actor - Asynchronous Actor with Concurrent Job Processing",
 | 
			
		||||
    long_about = "An asynchronous actor for Hero framework that processes multiple jobs \
 | 
			
		||||
                  concurrently with timeout support. Ideal for high-throughput scenarios \
 | 
			
		||||
                  where jobs can be executed in parallel."
 | 
			
		||||
)]
 | 
			
		||||
struct Args {
 | 
			
		||||
    /// Path to TOML configuration file
 | 
			
		||||
    #[arg(short, long, help = "Path to TOML configuration file")]
 | 
			
		||||
    config: PathBuf,
 | 
			
		||||
 | 
			
		||||
    /// Override actor ID from config
 | 
			
		||||
    #[arg(long, help = "Override actor ID from configuration file")]
 | 
			
		||||
    actor_id: Option<String>,
 | 
			
		||||
 | 
			
		||||
    /// Override Redis URL from config
 | 
			
		||||
    #[arg(long, help = "Override Redis URL from configuration file")]
 | 
			
		||||
    redis_url: Option<String>,
 | 
			
		||||
 | 
			
		||||
    /// Override database path from config
 | 
			
		||||
    #[arg(long, help = "Override database path from configuration file")]
 | 
			
		||||
    db_path: Option<String>,
 | 
			
		||||
 | 
			
		||||
    /// Override default timeout in seconds
 | 
			
		||||
    #[arg(long, help = "Override default job timeout in seconds")]
 | 
			
		||||
    timeout: Option<u64>,
 | 
			
		||||
 | 
			
		||||
    /// Enable verbose logging (debug level)
 | 
			
		||||
    #[arg(short, long, help = "Enable verbose logging")]
 | 
			
		||||
    verbose: bool,
 | 
			
		||||
 | 
			
		||||
    /// Disable timestamps in log output
 | 
			
		||||
    #[arg(long, help = "Remove timestamps from log output")]
 | 
			
		||||
    no_timestamp: bool,
 | 
			
		||||
 | 
			
		||||
    /// Show actor statistics periodically
 | 
			
		||||
    #[arg(long, help = "Show periodic actor statistics")]
 | 
			
		||||
    show_stats: bool,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[tokio::main]
 | 
			
		||||
async fn main() -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
 | 
			
		||||
    let args = Args::parse();
 | 
			
		||||
 | 
			
		||||
    // Load configuration from TOML file
 | 
			
		||||
    let mut config = match ActorConfig::from_file(&args.config) {
 | 
			
		||||
        Ok(config) => config,
 | 
			
		||||
        Err(e) => {
 | 
			
		||||
            eprintln!("Failed to load configuration from {:?}: {}", args.config, e);
 | 
			
		||||
            std::process::exit(1);
 | 
			
		||||
        }
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    // Validate that this is an async actor configuration
 | 
			
		||||
    if !config.is_async() {
 | 
			
		||||
        eprintln!("Error: System actor requires an async actor configuration");
 | 
			
		||||
        eprintln!("Expected: [actor_type] type = \"async\"");
 | 
			
		||||
        eprintln!("Found: {:?}", config.actor_type);
 | 
			
		||||
        std::process::exit(1);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // Apply command line overrides
 | 
			
		||||
    if let Some(actor_id) = args.actor_id {
 | 
			
		||||
        config.actor_id = actor_id;
 | 
			
		||||
    }
 | 
			
		||||
    if let Some(redis_url) = args.redis_url {
 | 
			
		||||
        config.redis_url = redis_url;
 | 
			
		||||
    }
 | 
			
		||||
    if let Some(db_path) = args.db_path {
 | 
			
		||||
        config.db_path = db_path;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // Override timeout if specified
 | 
			
		||||
    if let Some(timeout_secs) = args.timeout {
 | 
			
		||||
        if let baobab_actor::config::ActorType::Async { ref mut default_timeout_seconds } = config.actor_type {
 | 
			
		||||
            *default_timeout_seconds = timeout_secs;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // Configure logging
 | 
			
		||||
    setup_logging(&config, args.verbose, args.no_timestamp)?;
 | 
			
		||||
 | 
			
		||||
    info!("🚀 System Actor starting...");
 | 
			
		||||
    info!("Actor ID: {}", config.actor_id);
 | 
			
		||||
    info!("Redis URL: {}", config.redis_url);
 | 
			
		||||
    info!("Database Path: {}", config.db_path);
 | 
			
		||||
    info!("Preserve Tasks: {}", config.preserve_tasks);
 | 
			
		||||
    
 | 
			
		||||
    if let Some(timeout) = config.get_default_timeout() {
 | 
			
		||||
        info!("Default Timeout: {:?}", timeout);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // Create Rhai engine
 | 
			
		||||
    let engine = create_heromodels_engine();
 | 
			
		||||
    info!("✅ Rhai engine initialized");
 | 
			
		||||
 | 
			
		||||
    // Create actor configuration for the trait-based interface
 | 
			
		||||
    let mut actor_config = TraitActorConfig::new(
 | 
			
		||||
        config.actor_id.clone(),
 | 
			
		||||
        config.db_path.clone(),
 | 
			
		||||
        config.redis_url.clone(),
 | 
			
		||||
        config.preserve_tasks,
 | 
			
		||||
    );
 | 
			
		||||
 | 
			
		||||
    // Add timeout configuration for async actor
 | 
			
		||||
    if let Some(timeout) = config.get_default_timeout() {
 | 
			
		||||
        actor_config = actor_config.with_default_timeout(timeout);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // Create async actor instance
 | 
			
		||||
    let actor = Arc::new(AsyncActor::default());
 | 
			
		||||
    info!("✅ Async actor instance created");
 | 
			
		||||
 | 
			
		||||
    // Setup shutdown signal handling
 | 
			
		||||
    let (shutdown_tx, shutdown_rx) = mpsc::channel(1);
 | 
			
		||||
    
 | 
			
		||||
    // Spawn shutdown signal handler
 | 
			
		||||
    let shutdown_tx_clone = shutdown_tx.clone();
 | 
			
		||||
    tokio::spawn(async move {
 | 
			
		||||
        if let Err(e) = signal::ctrl_c().await {
 | 
			
		||||
            error!("Failed to listen for shutdown signal: {}", e);
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
        info!("🛑 Shutdown signal received");
 | 
			
		||||
        if let Err(e) = shutdown_tx_clone.send(()).await {
 | 
			
		||||
            error!("Failed to send shutdown signal: {}", e);
 | 
			
		||||
        }
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    // Spawn statistics reporter if requested
 | 
			
		||||
    if args.show_stats {
 | 
			
		||||
        let actor_stats = Arc::clone(&actor);
 | 
			
		||||
        tokio::spawn(async move {
 | 
			
		||||
            let mut interval = tokio::time::interval(Duration::from_secs(30));
 | 
			
		||||
            loop {
 | 
			
		||||
                interval.tick().await;
 | 
			
		||||
                let running_count = actor_stats.running_job_count().await;
 | 
			
		||||
                if running_count > 0 {
 | 
			
		||||
                    info!("📊 Actor Stats: {} jobs currently running", running_count);
 | 
			
		||||
                } else {
 | 
			
		||||
                    info!("📊 Actor Stats: No jobs currently running");
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        });
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // Spawn the actor
 | 
			
		||||
    info!("🔄 Starting actor loop...");
 | 
			
		||||
    let actor_handle = spawn_actor(actor, engine, shutdown_rx);
 | 
			
		||||
 | 
			
		||||
    // Wait for the actor to complete
 | 
			
		||||
    match actor_handle.await {
 | 
			
		||||
        Ok(Ok(())) => {
 | 
			
		||||
            info!("✅ System Actor shut down gracefully");
 | 
			
		||||
        }
 | 
			
		||||
        Ok(Err(e)) => {
 | 
			
		||||
            error!("❌ System Actor encountered an error: {}", e);
 | 
			
		||||
            std::process::exit(1);
 | 
			
		||||
        }
 | 
			
		||||
        Err(e) => {
 | 
			
		||||
            error!("❌ Failed to join actor task: {}", e);
 | 
			
		||||
            std::process::exit(1);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    Ok(())
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Setup logging based on configuration and command line arguments
 | 
			
		||||
fn setup_logging(
 | 
			
		||||
    config: &ActorConfig,
 | 
			
		||||
    verbose: bool,
 | 
			
		||||
    no_timestamp: bool,
 | 
			
		||||
) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
 | 
			
		||||
    let mut builder = env_logger::Builder::new();
 | 
			
		||||
 | 
			
		||||
    // Determine log level
 | 
			
		||||
    let log_level = if verbose {
 | 
			
		||||
        "debug"
 | 
			
		||||
    } else {
 | 
			
		||||
        &config.logging.level
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    // Set log level
 | 
			
		||||
    builder.filter_level(match log_level.to_lowercase().as_str() {
 | 
			
		||||
        "trace" => log::LevelFilter::Trace,
 | 
			
		||||
        "debug" => log::LevelFilter::Debug,
 | 
			
		||||
        "info" => log::LevelFilter::Info,
 | 
			
		||||
        "warn" => log::LevelFilter::Warn,
 | 
			
		||||
        "error" => log::LevelFilter::Error,
 | 
			
		||||
        _ => {
 | 
			
		||||
            warn!("Invalid log level: {}. Using 'info'", log_level);
 | 
			
		||||
            log::LevelFilter::Info
 | 
			
		||||
        }
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    // Configure timestamps
 | 
			
		||||
    let show_timestamps = !no_timestamp && config.logging.timestamps;
 | 
			
		||||
    if !show_timestamps {
 | 
			
		||||
        builder.format_timestamp(None);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    builder.init();
 | 
			
		||||
    Ok(())
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[cfg(test)]
 | 
			
		||||
mod tests {
 | 
			
		||||
    use super::*;
 | 
			
		||||
    use std::io::Write;
 | 
			
		||||
    use tempfile::NamedTempFile;
 | 
			
		||||
 | 
			
		||||
    #[test]
 | 
			
		||||
    fn test_config_validation() {
 | 
			
		||||
        let config_toml = r#"
 | 
			
		||||
actor_id = "test_system"
 | 
			
		||||
redis_url = "redis://localhost:6379"
 | 
			
		||||
db_path = "/tmp/test_db"
 | 
			
		||||
 | 
			
		||||
[actor_type]
 | 
			
		||||
type = "async"
 | 
			
		||||
default_timeout_seconds = 600
 | 
			
		||||
 | 
			
		||||
[logging]
 | 
			
		||||
level = "info"
 | 
			
		||||
"#;
 | 
			
		||||
 | 
			
		||||
        let mut temp_file = NamedTempFile::new().unwrap();
 | 
			
		||||
        temp_file.write_all(config_toml.as_bytes()).unwrap();
 | 
			
		||||
 | 
			
		||||
        let config = ActorConfig::from_file(temp_file.path()).unwrap();
 | 
			
		||||
        assert!(!config.is_sync());
 | 
			
		||||
        assert!(config.is_async());
 | 
			
		||||
        assert_eq!(config.actor_id, "test_system");
 | 
			
		||||
        assert_eq!(config.get_default_timeout(), Some(Duration::from_secs(600)));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    #[test]
 | 
			
		||||
    fn test_sync_config_rejection() {
 | 
			
		||||
        let config_toml = r#"
 | 
			
		||||
actor_id = "test_system"
 | 
			
		||||
redis_url = "redis://localhost:6379"
 | 
			
		||||
db_path = "/tmp/test_db"
 | 
			
		||||
 | 
			
		||||
[actor_type]
 | 
			
		||||
type = "sync"
 | 
			
		||||
 | 
			
		||||
[logging]
 | 
			
		||||
level = "info"
 | 
			
		||||
"#;
 | 
			
		||||
 | 
			
		||||
        let mut temp_file = NamedTempFile::new().unwrap();
 | 
			
		||||
        temp_file.write_all(config_toml.as_bytes()).unwrap();
 | 
			
		||||
 | 
			
		||||
        let config = ActorConfig::from_file(temp_file.path()).unwrap();
 | 
			
		||||
        assert!(config.is_sync());
 | 
			
		||||
        assert!(!config.is_async());
 | 
			
		||||
        // This would be rejected in main() function
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    #[test]
 | 
			
		||||
    fn test_timeout_override() {
 | 
			
		||||
        let config_toml = r#"
 | 
			
		||||
actor_id = "test_system"
 | 
			
		||||
redis_url = "redis://localhost:6379"
 | 
			
		||||
db_path = "/tmp/test_db"
 | 
			
		||||
 | 
			
		||||
[actor_type]
 | 
			
		||||
type = "async"
 | 
			
		||||
default_timeout_seconds = 300
 | 
			
		||||
"#;
 | 
			
		||||
 | 
			
		||||
        let mut temp_file = NamedTempFile::new().unwrap();
 | 
			
		||||
        temp_file.write_all(config_toml.as_bytes()).unwrap();
 | 
			
		||||
 | 
			
		||||
        let mut config = ActorConfig::from_file(temp_file.path()).unwrap();
 | 
			
		||||
        assert_eq!(config.get_default_timeout(), Some(Duration::from_secs(300)));
 | 
			
		||||
 | 
			
		||||
        // Test timeout override
 | 
			
		||||
        if let baobab_actor::config::ActorType::Async { ref mut default_timeout_seconds } = config.actor_type {
 | 
			
		||||
            *default_timeout_seconds = 600;
 | 
			
		||||
        }
 | 
			
		||||
        assert_eq!(config.get_default_timeout(), Some(Duration::from_secs(600)));
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										95
									
								
								_archive/core/actor/cmd/worker.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										95
									
								
								_archive/core/actor/cmd/worker.rs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,95 @@
 | 
			
		||||
use clap::Parser;
 | 
			
		||||
use baobab_actor::engine::create_heromodels_engine;
 | 
			
		||||
use baobab_actor::spawn_rhai_actor;
 | 
			
		||||
use tokio::sync::mpsc;
 | 
			
		||||
 | 
			
		||||
#[derive(Parser, Debug)]
 | 
			
		||||
#[command(author, version, about, long_about = None)]
 | 
			
		||||
struct Args {
 | 
			
		||||
    /// Actor ID for identification
 | 
			
		||||
    #[arg(short, long)]
 | 
			
		||||
    actor_id: String,
 | 
			
		||||
 | 
			
		||||
    /// Redis URL
 | 
			
		||||
    #[arg(short, long, default_value = "redis://localhost:6379")]
 | 
			
		||||
    redis_url: String,
 | 
			
		||||
 | 
			
		||||
    /// Preserve task details after completion (for benchmarking)
 | 
			
		||||
    #[arg(long, default_value = "false")]
 | 
			
		||||
    preserve_tasks: bool,
 | 
			
		||||
 | 
			
		||||
    /// Root directory for engine database
 | 
			
		||||
    #[arg(long, default_value = "actor_rhai_temp_db")]
 | 
			
		||||
    db_path: String,
 | 
			
		||||
 | 
			
		||||
    /// Disable timestamps in log output
 | 
			
		||||
    #[arg(long, help = "Remove timestamps from log output")]
 | 
			
		||||
    no_timestamp: bool,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[tokio::main]
 | 
			
		||||
async fn main() -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
 | 
			
		||||
    let args = Args::parse();
 | 
			
		||||
 | 
			
		||||
    // Configure env_logger with or without timestamps
 | 
			
		||||
    if args.no_timestamp {
 | 
			
		||||
        env_logger::Builder::from_default_env()
 | 
			
		||||
            .format_timestamp(None)
 | 
			
		||||
            .init();
 | 
			
		||||
    } else {
 | 
			
		||||
        env_logger::init();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    log::info!("Rhai Actor (binary) starting with performance-optimized engine.");
 | 
			
		||||
    log::info!(
 | 
			
		||||
        "Actor ID: {}, Redis: {}",
 | 
			
		||||
        args.actor_id,
 | 
			
		||||
        args.redis_url
 | 
			
		||||
    );
 | 
			
		||||
 | 
			
		||||
    let mut engine = create_heromodels_engine();
 | 
			
		||||
 | 
			
		||||
    // Performance optimizations for benchmarking
 | 
			
		||||
    engine.set_max_operations(0); // Unlimited operations for performance testing
 | 
			
		||||
    engine.set_max_expr_depths(0, 0); // Unlimited expression depth
 | 
			
		||||
    engine.set_max_string_size(0); // Unlimited string size
 | 
			
		||||
    engine.set_max_array_size(0); // Unlimited array size
 | 
			
		||||
    engine.set_max_map_size(0); // Unlimited map size
 | 
			
		||||
 | 
			
		||||
    // Enable full optimization for maximum performance
 | 
			
		||||
    engine.set_optimization_level(rhai::OptimizationLevel::Full);
 | 
			
		||||
 | 
			
		||||
    log::info!("Engine configured for maximum performance");
 | 
			
		||||
 | 
			
		||||
    // Create shutdown channel (for graceful shutdown, though not used in benchmarks)
 | 
			
		||||
    let (_shutdown_tx, shutdown_rx) = mpsc::channel::<()>(1);
 | 
			
		||||
 | 
			
		||||
    // Spawn the actor
 | 
			
		||||
    let actor_handle = spawn_rhai_actor(
 | 
			
		||||
        args.actor_id,
 | 
			
		||||
        args.db_path,
 | 
			
		||||
        engine,
 | 
			
		||||
        args.redis_url,
 | 
			
		||||
        shutdown_rx,
 | 
			
		||||
        args.preserve_tasks,
 | 
			
		||||
    );
 | 
			
		||||
 | 
			
		||||
    // Wait for the actor to complete
 | 
			
		||||
    match actor_handle.await {
 | 
			
		||||
        Ok(result) => match result {
 | 
			
		||||
            Ok(_) => {
 | 
			
		||||
                log::info!("Actor completed successfully");
 | 
			
		||||
                Ok(())
 | 
			
		||||
            }
 | 
			
		||||
            Err(e) => {
 | 
			
		||||
                log::error!("Actor failed: {}", e);
 | 
			
		||||
                Err(e)
 | 
			
		||||
            }
 | 
			
		||||
        },
 | 
			
		||||
        Err(e) => {
 | 
			
		||||
            log::error!("Actor task panicked: {}", e);
 | 
			
		||||
            Err(Box::new(e) as Box<dyn std::error::Error + Send + Sync>)
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
		Reference in New Issue
	
	Block a user