merge runners into single project

This commit is contained in:
Timur Gordon
2025-09-09 15:42:20 +02:00
parent 89a3abee63
commit 629d59f7db
20 changed files with 2033 additions and 894 deletions

71
cmd/async_actor.rs Normal file
View File

@@ -0,0 +1,71 @@
use clap::Parser;
use log::info;
use std::time::Duration;
use tokio::sync::mpsc;
use actor_system::{spawn_async_actor};
#[derive(Parser, Debug)]
#[command(name = "async_actor")]
#[command(about = "Async Actor - processes jobs concurrently with SAL modules")]
struct Args {
/// Actor ID for this instance
#[arg(short, long)]
actor_id: String,
/// Database path
#[arg(short, long, default_value = "/tmp/actor_db")]
db_path: String,
/// Redis URL
#[arg(short, long, default_value = "redis://localhost:6379")]
redis_url: String,
/// Default timeout in seconds for job execution
#[arg(short, long, default_value = "300")]
timeout: u64,
}
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
env_logger::init();
let args = Args::parse();
info!("Starting Async Actor with ID: {}", args.actor_id);
info!("Database path: {}", args.db_path);
info!("Redis URL: {}", args.redis_url);
info!("Default timeout: {}s", args.timeout);
// Create shutdown channel
let (_shutdown_tx, shutdown_rx) = mpsc::channel::<()>(1);
// Spawn the async actor
let handle = spawn_async_actor(
args.actor_id,
args.db_path,
args.redis_url,
shutdown_rx,
Duration::from_secs(args.timeout),
);
// Wait for the actor to complete
match handle.await {
Ok(result) => {
match result {
Ok(()) => {
info!("Async Actor completed successfully");
Ok(())
}
Err(e) => {
eprintln!("Async Actor error: {}", e);
Err(e)
}
}
}
Err(e) => {
eprintln!("Failed to join async actor task: {}", e);
Err(Box::new(e))
}
}
}

149
cmd/async_actor_tui.rs Normal file
View File

@@ -0,0 +1,149 @@
//! Terminal UI for Async Actor - Monitor and dispatch jobs to async actor with SAL modules
//!
//! This binary provides a TUI interface for monitoring and dispatching jobs to the async actor.
use anyhow::{Result, Context};
use clap::Parser;
use log::{info, warn, error};
use std::path::PathBuf;
use std::process::{Child, Command};
use tokio::signal;
#[derive(Parser)]
#[command(name = "async-actor-tui")]
#[command(about = "Terminal UI for Async Actor - Monitor and dispatch jobs with SAL modules")]
struct Args {
/// Actor ID for this instance
#[arg(short, long, default_value = "async_sal")]
actor_id: String,
/// Redis URL for job queue
#[arg(short, long, default_value = "redis://localhost:6379")]
redis_url: String,
/// Database path
#[arg(short, long, default_value = "/tmp/actor_db")]
db_path: String,
/// Default timeout in seconds for job execution
#[arg(short, long, default_value = "300")]
timeout: u64,
/// Enable verbose logging
#[arg(short, long)]
verbose: bool,
}
/// Initialize logging based on verbosity level
fn init_logging(verbose: bool) {
if verbose {
env_logger::Builder::from_default_env()
.filter_level(log::LevelFilter::Debug)
.init();
} else {
env_logger::Builder::from_default_env()
.filter_level(log::LevelFilter::Info)
.init();
}
}
/// Spawn the async actor binary as a background process
fn spawn_actor_process(args: &Args) -> Result<Child> {
// Get the crate root directory
let crate_root = std::env::var("CARGO_MANIFEST_DIR")
.unwrap_or_else(|_| ".".to_string());
let actor_path = PathBuf::from(crate_root).join("target/debug/async_actor");
info!("🎬 Spawning async actor process: {}", actor_path.display());
let mut cmd = Command::new(&actor_path);
// Add command line arguments
cmd.args(&[
"--actor-id", &args.actor_id,
"--db-path", &args.db_path,
"--redis-url", &args.redis_url,
"--timeout", &args.timeout.to_string(),
]);
// Redirect stdout and stderr to null to prevent logs from interfering with TUI
cmd.stdout(std::process::Stdio::null())
.stderr(std::process::Stdio::null());
// Spawn the process
let child = cmd
.spawn()
.with_context(|| format!("Failed to spawn async actor process: {}", actor_path.display()))?;
info!("✅ Async actor process spawned with PID: {}", child.id());
Ok(child)
}
/// Cleanup function to terminate actor process
fn cleanup_actor_process(mut actor_process: Child) {
info!("🧹 Cleaning up async actor process...");
match actor_process.try_wait() {
Ok(Some(status)) => {
info!("Async actor process already exited with status: {}", status);
}
Ok(None) => {
info!("Terminating async actor process...");
if let Err(e) = actor_process.kill() {
error!("Failed to kill async actor process: {}", e);
} else {
match actor_process.wait() {
Ok(status) => info!("Async actor process terminated with status: {}", status),
Err(e) => error!("Failed to wait for async actor process: {}", e),
}
}
}
Err(e) => {
error!("Failed to check async actor process status: {}", e);
}
}
}
#[tokio::main]
async fn main() -> Result<()> {
let args = Args::parse();
// Initialize logging
init_logging(args.verbose);
let crate_root = std::env::var("CARGO_MANIFEST_DIR")
.unwrap_or_else(|_| ".".to_string());
info!("🚀 Starting Async Actor TUI...");
info!("Actor ID: {}", args.actor_id);
info!("Actor Path: {}/target/debug/async_actor", crate_root);
info!("Redis URL: {}", args.redis_url);
info!("Database Path: {}", args.db_path);
info!("Default Timeout: {}s", args.timeout);
info!("Script Type: SAL (System Abstraction Layer)");
// Spawn the actor process first
let actor_process = spawn_actor_process(&args)?;
// Give the actor a moment to start up
tokio::time::sleep(tokio::time::Duration::from_millis(500)).await;
info!("📋 Async Actor TUI is running. The actor processes jobs concurrently.");
info!("💡 Use Redis CLI or job submission tools to send jobs to queue: actor_queue:{}", args.actor_id);
info!("🔄 Jobs will be processed with SAL modules (system operations)");
info!("⏱️ Each job has a timeout of {}s", args.timeout);
info!("📊 Multiple jobs can run simultaneously");
info!("Press Ctrl+C to exit...");
// Wait for Ctrl+C
let result = tokio::select! {
_ = signal::ctrl_c() => {
info!("Received Ctrl+C, shutting down...");
Ok(())
}
};
// Clean up the actor process
cleanup_actor_process(actor_process);
result
}

70
cmd/sync_actor.rs Normal file
View File

@@ -0,0 +1,70 @@
use clap::Parser;
use log::info;
use tokio::sync::mpsc;
use actor_system::{spawn_sync_actor};
#[derive(Parser, Debug)]
#[command(name = "sync_actor")]
#[command(about = "Sync Actor - processes jobs sequentially with DSL modules")]
struct Args {
/// Actor ID for this instance
#[arg(short, long)]
actor_id: String,
/// Database path
#[arg(short, long, default_value = "/tmp/actor_db")]
db_path: String,
/// Redis URL
#[arg(short, long, default_value = "redis://localhost:6379")]
redis_url: String,
/// Preserve completed tasks in Redis (don't delete them)
#[arg(short, long, default_value = "false")]
preserve_tasks: bool,
}
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
env_logger::init();
let args = Args::parse();
info!("Starting Sync Actor with ID: {}", args.actor_id);
info!("Database path: {}", args.db_path);
info!("Redis URL: {}", args.redis_url);
info!("Preserve tasks: {}", args.preserve_tasks);
// Create shutdown channel
let (_shutdown_tx, shutdown_rx) = mpsc::channel::<()>(1);
// Spawn the sync actor
let handle = spawn_sync_actor(
args.actor_id,
args.db_path,
args.redis_url,
shutdown_rx,
args.preserve_tasks,
);
// Wait for the actor to complete
match handle.await {
Ok(result) => {
match result {
Ok(()) => {
info!("Sync Actor completed successfully");
Ok(())
}
Err(e) => {
eprintln!("Sync Actor error: {}", e);
Err(e)
}
}
}
Err(e) => {
eprintln!("Failed to join sync actor task: {}", e);
Err(Box::new(e))
}
}
}

153
cmd/sync_actor_tui.rs Normal file
View File

@@ -0,0 +1,153 @@
//! Terminal UI for Sync Actor - Monitor and dispatch jobs to sync actor with DSL modules
//!
//! This binary provides a TUI interface for monitoring and dispatching jobs to the sync actor.
use anyhow::{Result, Context};
use clap::Parser;
use log::{info, warn, error};
use std::path::PathBuf;
use std::process::{Child, Command};
use tokio::signal;
#[derive(Parser)]
#[command(name = "sync-actor-tui")]
#[command(about = "Terminal UI for Sync Actor - Monitor and dispatch jobs with DSL modules")]
struct Args {
/// Actor ID for this instance
#[arg(short, long, default_value = "sync_osis")]
actor_id: String,
/// Redis URL for job queue
#[arg(short, long, default_value = "redis://localhost:6379")]
redis_url: String,
/// Database path
#[arg(short, long, default_value = "/tmp/actor_db")]
db_path: String,
/// Preserve completed tasks in Redis (don't delete them)
#[arg(short, long, default_value = "false")]
preserve_tasks: bool,
/// Enable verbose logging
#[arg(short, long)]
verbose: bool,
}
/// Initialize logging based on verbosity level
fn init_logging(verbose: bool) {
if verbose {
env_logger::Builder::from_default_env()
.filter_level(log::LevelFilter::Debug)
.init();
} else {
env_logger::Builder::from_default_env()
.filter_level(log::LevelFilter::Info)
.init();
}
}
/// Spawn the sync actor binary as a background process
fn spawn_actor_process(args: &Args) -> Result<Child> {
// Get the crate root directory
let crate_root = std::env::var("CARGO_MANIFEST_DIR")
.unwrap_or_else(|_| ".".to_string());
let actor_path = PathBuf::from(crate_root).join("target/debug/sync_actor");
info!("🎬 Spawning sync actor process: {}", actor_path.display());
let mut cmd = Command::new(&actor_path);
// Add command line arguments
cmd.args(&[
"--actor-id", &args.actor_id,
"--db-path", &args.db_path,
"--redis-url", &args.redis_url,
"--preserve-tasks", &args.preserve_tasks.to_string(),
]);
// Redirect stdout and stderr to null to prevent logs from interfering with TUI
cmd.stdout(std::process::Stdio::null())
.stderr(std::process::Stdio::null());
// Spawn the process
let child = cmd
.spawn()
.with_context(|| format!("Failed to spawn sync actor process: {}", actor_path.display()))?;
info!("✅ Sync actor process spawned with PID: {}", child.id());
Ok(child)
}
/// Cleanup function to terminate actor process
fn cleanup_actor_process(mut actor_process: Child) {
info!("🧹 Cleaning up sync actor process...");
match actor_process.try_wait() {
Ok(Some(status)) => {
info!("Sync actor process already exited with status: {}", status);
}
Ok(None) => {
info!("Terminating sync actor process...");
if let Err(e) = actor_process.kill() {
error!("Failed to kill sync actor process: {}", e);
} else {
match actor_process.wait() {
Ok(status) => info!("Sync actor process terminated with status: {}", status),
Err(e) => error!("Failed to wait for sync actor process: {}", e),
}
}
}
Err(e) => {
error!("Failed to check sync actor process status: {}", e);
}
}
}
#[tokio::main]
async fn main() -> Result<()> {
let args = Args::parse();
// Initialize logging
init_logging(args.verbose);
let crate_root = std::env::var("CARGO_MANIFEST_DIR")
.unwrap_or_else(|_| ".".to_string());
info!("🚀 Starting Sync Actor TUI...");
info!("Actor ID: {}", args.actor_id);
info!("Actor Path: {}/target/debug/sync_actor", crate_root);
info!("Redis URL: {}", args.redis_url);
info!("Database Path: {}", args.db_path);
info!("Preserve Tasks: {}", args.preserve_tasks);
info!("Script Type: DSL (Domain Specific Language)");
// Spawn the actor process first
let actor_process = spawn_actor_process(&args)?;
// Give the actor a moment to start up
tokio::time::sleep(tokio::time::Duration::from_millis(500)).await;
info!("📋 Sync Actor TUI is running. The actor processes jobs sequentially.");
info!("💡 Use Redis CLI or job submission tools to send jobs to queue: actor_queue:{}", args.actor_id);
info!("🔄 Jobs will be processed with DSL modules (business operations)");
info!("📊 Jobs are processed one at a time in order");
if args.preserve_tasks {
info!("💾 Completed tasks will be preserved in Redis");
} else {
info!("🗑️ Completed tasks will be cleaned up from Redis");
}
info!("Press Ctrl+C to exit...");
// Wait for Ctrl+C
let result = tokio::select! {
_ = signal::ctrl_c() => {
info!("Received Ctrl+C, shutting down...");
Ok(())
}
};
// Clean up the actor process
cleanup_actor_process(actor_process);
result
}