wip
This commit is contained in:
239
core/supervisor/examples/lifecycle_demo.rs
Normal file
239
core/supervisor/examples/lifecycle_demo.rs
Normal file
@@ -0,0 +1,239 @@
|
||||
use hero_supervisor::{
|
||||
Supervisor, SupervisorBuilder, WorkerConfig, WorkerLifecycleManager,
|
||||
WorkerLifecycleManagerBuilder, ScriptType
|
||||
};
|
||||
use log::{info, warn, error};
|
||||
use std::collections::HashMap;
|
||||
use std::path::PathBuf;
|
||||
use std::time::Duration;
|
||||
use tokio::time::sleep;
|
||||
|
||||
#[tokio::main]
|
||||
async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
// Initialize logging
|
||||
env_logger::init();
|
||||
|
||||
info!("Starting Worker Lifecycle Management Demo");
|
||||
|
||||
// Configuration
|
||||
let redis_url = "redis://localhost:6379";
|
||||
let zinit_socket = "/var/run/zinit.sock";
|
||||
|
||||
// Create supervisor
|
||||
let supervisor = SupervisorBuilder::new()
|
||||
.redis_url(redis_url)
|
||||
.caller_id("lifecycle_demo")
|
||||
.context_id("demo_context")
|
||||
.build()?;
|
||||
|
||||
// Configure workers for different script types
|
||||
let mut worker_configs = Vec::new();
|
||||
|
||||
// OSIS workers (Rhai/HeroScript)
|
||||
for i in 0..2 {
|
||||
let config = WorkerConfig::new(
|
||||
format!("osis_worker_{}", i),
|
||||
PathBuf::from("/usr/local/bin/osis_worker"),
|
||||
ScriptType::OSIS,
|
||||
)
|
||||
.with_args(vec![
|
||||
"--redis-url".to_string(),
|
||||
redis_url.to_string(),
|
||||
"--worker-id".to_string(),
|
||||
format!("osis_worker_{}", i),
|
||||
])
|
||||
.with_env({
|
||||
let mut env = HashMap::new();
|
||||
env.insert("RUST_LOG".to_string(), "info".to_string());
|
||||
env.insert("WORKER_TYPE".to_string(), "osis".to_string());
|
||||
env
|
||||
})
|
||||
.with_health_check("/usr/local/bin/osis_worker --health-check".to_string())
|
||||
.with_dependencies(vec!["redis".to_string()]);
|
||||
|
||||
worker_configs.push(config);
|
||||
}
|
||||
|
||||
// SAL workers (System Abstraction Layer)
|
||||
for i in 0..3 {
|
||||
let config = WorkerConfig::new(
|
||||
format!("sal_worker_{}", i),
|
||||
PathBuf::from("/usr/local/bin/sal_worker"),
|
||||
ScriptType::SAL,
|
||||
)
|
||||
.with_args(vec![
|
||||
"--redis-url".to_string(),
|
||||
redis_url.to_string(),
|
||||
"--worker-id".to_string(),
|
||||
format!("sal_worker_{}", i),
|
||||
])
|
||||
.with_env({
|
||||
let mut env = HashMap::new();
|
||||
env.insert("RUST_LOG".to_string(), "info".to_string());
|
||||
env.insert("WORKER_TYPE".to_string(), "sal".to_string());
|
||||
env
|
||||
})
|
||||
.with_health_check("/usr/local/bin/sal_worker --health-check".to_string())
|
||||
.with_dependencies(vec!["redis".to_string()]);
|
||||
|
||||
worker_configs.push(config);
|
||||
}
|
||||
|
||||
// V workers (HeroScript in V language)
|
||||
for i in 0..2 {
|
||||
let config = WorkerConfig::new(
|
||||
format!("v_worker_{}", i),
|
||||
PathBuf::from("/usr/local/bin/v_worker"),
|
||||
ScriptType::V,
|
||||
)
|
||||
.with_args(vec![
|
||||
"--redis-url".to_string(),
|
||||
redis_url.to_string(),
|
||||
"--worker-id".to_string(),
|
||||
format!("v_worker_{}", i),
|
||||
])
|
||||
.with_env({
|
||||
let mut env = HashMap::new();
|
||||
env.insert("RUST_LOG".to_string(), "info".to_string());
|
||||
env.insert("WORKER_TYPE".to_string(), "v".to_string());
|
||||
env
|
||||
})
|
||||
.with_health_check("/usr/local/bin/v_worker --health-check".to_string())
|
||||
.with_dependencies(vec!["redis".to_string()]);
|
||||
|
||||
worker_configs.push(config);
|
||||
}
|
||||
|
||||
// Create lifecycle manager
|
||||
let mut lifecycle_manager = WorkerLifecycleManagerBuilder::new(zinit_socket.to_string())
|
||||
.with_supervisor(supervisor.clone());
|
||||
|
||||
// Add all worker configurations
|
||||
for config in worker_configs {
|
||||
lifecycle_manager = lifecycle_manager.add_worker(config);
|
||||
}
|
||||
|
||||
let mut lifecycle_manager = lifecycle_manager.build();
|
||||
|
||||
// Demonstrate lifecycle operations
|
||||
info!("=== Starting Worker Lifecycle Demo ===");
|
||||
|
||||
// 1. Start all workers
|
||||
info!("1. Starting all workers...");
|
||||
match lifecycle_manager.start_all_workers().await {
|
||||
Ok(_) => info!("✅ All workers started successfully"),
|
||||
Err(e) => {
|
||||
error!("❌ Failed to start workers: {}", e);
|
||||
return Err(e.into());
|
||||
}
|
||||
}
|
||||
|
||||
// Wait for workers to initialize
|
||||
sleep(Duration::from_secs(5)).await;
|
||||
|
||||
// 2. Check worker status
|
||||
info!("2. Checking worker status...");
|
||||
match lifecycle_manager.get_all_worker_status().await {
|
||||
Ok(status_map) => {
|
||||
for (worker_name, status) in status_map {
|
||||
info!(" Worker '{}': State={:?}, PID={}", worker_name, status.state, status.pid);
|
||||
}
|
||||
}
|
||||
Err(e) => warn!("Failed to get worker status: {}", e),
|
||||
}
|
||||
|
||||
// 3. Demonstrate scaling
|
||||
info!("3. Demonstrating worker scaling...");
|
||||
|
||||
// Scale up OSIS workers
|
||||
info!(" Scaling up OSIS workers to 3...");
|
||||
if let Err(e) = lifecycle_manager.scale_workers(&ScriptType::OSIS, 3).await {
|
||||
warn!("Failed to scale OSIS workers: {}", e);
|
||||
}
|
||||
|
||||
sleep(Duration::from_secs(3)).await;
|
||||
|
||||
// Scale down SAL workers
|
||||
info!(" Scaling down SAL workers to 1...");
|
||||
if let Err(e) = lifecycle_manager.scale_workers(&ScriptType::SAL, 1).await {
|
||||
warn!("Failed to scale SAL workers: {}", e);
|
||||
}
|
||||
|
||||
sleep(Duration::from_secs(3)).await;
|
||||
|
||||
// 4. Check running worker counts
|
||||
info!("4. Checking running worker counts after scaling...");
|
||||
for script_type in [ScriptType::OSIS, ScriptType::SAL, ScriptType::V] {
|
||||
let count = lifecycle_manager.get_running_worker_count(&script_type).await;
|
||||
info!(" {:?}: {} workers running", script_type, count);
|
||||
}
|
||||
|
||||
// 5. Demonstrate restart functionality
|
||||
info!("5. Demonstrating worker restart...");
|
||||
if let Err(e) = lifecycle_manager.restart_worker("osis_worker_0").await {
|
||||
warn!("Failed to restart worker: {}", e);
|
||||
} else {
|
||||
info!(" ✅ Successfully restarted osis_worker_0");
|
||||
}
|
||||
|
||||
sleep(Duration::from_secs(3)).await;
|
||||
|
||||
// 6. Simulate job dispatch and health monitoring
|
||||
info!("6. Simulating job dispatch and health monitoring...");
|
||||
|
||||
// Update job time for a worker (simulating job dispatch)
|
||||
lifecycle_manager.update_worker_job_time("sal_worker_0");
|
||||
info!(" Updated job time for sal_worker_0");
|
||||
|
||||
// Perform health monitoring check
|
||||
if let Err(e) = lifecycle_manager.monitor_worker_health().await {
|
||||
warn!("Health monitoring failed: {}", e);
|
||||
} else {
|
||||
info!(" ✅ Health monitoring completed");
|
||||
}
|
||||
|
||||
// 7. Create and execute a test job
|
||||
info!("7. Creating and executing a test job...");
|
||||
let test_job = supervisor
|
||||
.new_job()
|
||||
.script_type(ScriptType::OSIS)
|
||||
.script_content("println!(\"Hello from worker!\");".to_string())
|
||||
.timeout(Duration::from_secs(30))
|
||||
.build()?;
|
||||
|
||||
match supervisor.run_job_and_await_result(&test_job).await {
|
||||
Ok(result) => info!(" ✅ Job executed successfully: {}", result),
|
||||
Err(e) => warn!(" ❌ Job execution failed: {}", e),
|
||||
}
|
||||
|
||||
// 8. Demonstrate graceful shutdown
|
||||
info!("8. Demonstrating graceful shutdown...");
|
||||
|
||||
// Stop specific workers
|
||||
info!(" Stopping specific workers...");
|
||||
for worker_name in ["osis_worker_1", "v_worker_0"] {
|
||||
if let Err(e) = lifecycle_manager.stop_worker(worker_name).await {
|
||||
warn!("Failed to stop worker {}: {}", worker_name, e);
|
||||
} else {
|
||||
info!(" ✅ Stopped worker: {}", worker_name);
|
||||
}
|
||||
}
|
||||
|
||||
sleep(Duration::from_secs(2)).await;
|
||||
|
||||
// Stop all remaining workers
|
||||
info!(" Stopping all remaining workers...");
|
||||
if let Err(e) = lifecycle_manager.stop_all_workers().await {
|
||||
error!("Failed to stop all workers: {}", e);
|
||||
} else {
|
||||
info!(" ✅ All workers stopped successfully");
|
||||
}
|
||||
|
||||
info!("=== Worker Lifecycle Demo Completed ===");
|
||||
|
||||
// Optional: Start health monitoring loop (commented out for demo)
|
||||
// info!("Starting health monitoring loop (Ctrl+C to stop)...");
|
||||
// lifecycle_manager.start_health_monitoring().await;
|
||||
|
||||
Ok(())
|
||||
}
|
74
core/supervisor/examples/simple_lifecycle_demo.rs
Normal file
74
core/supervisor/examples/simple_lifecycle_demo.rs
Normal file
@@ -0,0 +1,74 @@
|
||||
use hero_supervisor::SupervisorBuilder;
|
||||
use tokio::time::{sleep, Duration};
|
||||
use log::{info, error};
|
||||
|
||||
#[tokio::main]
|
||||
async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
env_logger::init();
|
||||
|
||||
info!("Starting Hero Supervisor Lifecycle Demo");
|
||||
|
||||
// Build supervisor with simplified worker configuration
|
||||
// Workers are automatically launched during build
|
||||
let supervisor = SupervisorBuilder::new()
|
||||
.redis_url("redis://localhost:6379")
|
||||
.zinit_socket_path("/var/run/zinit.sock")
|
||||
.osis_worker("/usr/local/bin/osis_worker")
|
||||
.sal_worker("/usr/local/bin/sal_worker")
|
||||
.v_worker("/usr/local/bin/v_worker")
|
||||
.worker_env_var("REDIS_URL", "redis://localhost:6379")
|
||||
.worker_env_var("LOG_LEVEL", "info")
|
||||
.build().await?;
|
||||
|
||||
info!("Supervisor created and workers launched successfully");
|
||||
|
||||
// Wait a moment for workers to start
|
||||
sleep(Duration::from_secs(2)).await;
|
||||
|
||||
// Check worker status using the simplified API
|
||||
info!("Checking worker status...");
|
||||
let workers = supervisor.get_workers(&[]).await;
|
||||
|
||||
for worker in &workers {
|
||||
let status_info = if worker.is_running {
|
||||
format!("Running (PID: {})", worker.status.as_ref().map(|s| s.pid).unwrap_or(0))
|
||||
} else {
|
||||
"Stopped".to_string()
|
||||
};
|
||||
info!(" Worker '{}' ({:?}): {}", worker.config.name, worker.config.script_type, status_info);
|
||||
}
|
||||
|
||||
// Demonstrate lifecycle operations with simplified API
|
||||
info!("=== Worker Lifecycle Operations ===");
|
||||
|
||||
// 1. Demonstrate restart functionality
|
||||
info!("1. Demonstrating worker restart...");
|
||||
if let Err(e) = supervisor.restart_worker("osis_worker_1").await {
|
||||
error!("Failed to restart worker: {}", e);
|
||||
} else {
|
||||
info!(" ✅ Successfully restarted osis_worker_1");
|
||||
}
|
||||
|
||||
sleep(Duration::from_secs(2)).await;
|
||||
|
||||
// 2. Send a ping job for health checking
|
||||
info!("2. Sending ping job for health checking...");
|
||||
if let Err(e) = supervisor.send_ping_job(hero_job::ScriptType::OSIS).await {
|
||||
error!("Ping job failed: {}", e);
|
||||
} else {
|
||||
info!(" ✅ Ping job completed successfully");
|
||||
}
|
||||
|
||||
// 3. Demonstrate graceful shutdown
|
||||
info!("3. Demonstrating graceful shutdown...");
|
||||
|
||||
// Stop specific workers
|
||||
if let Err(e) = supervisor.stop_worker("osis_worker_1").await {
|
||||
error!("Failed to stop worker: {}", e);
|
||||
} else {
|
||||
info!(" ✅ Worker stopped successfully");
|
||||
}
|
||||
|
||||
info!("Demo completed successfully!");
|
||||
Ok(())
|
||||
}
|
90
core/supervisor/examples/timeout_example.rs
Normal file
90
core/supervisor/examples/timeout_example.rs
Normal file
@@ -0,0 +1,90 @@
|
||||
use log::info;
|
||||
use hero_supervisor::{SupervisorBuilder, SupervisorError, ScriptType};
|
||||
use std::time::{Duration, Instant};
|
||||
|
||||
#[tokio::main]
|
||||
async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
env_logger::builder()
|
||||
.filter_level(log::LevelFilter::Info)
|
||||
.init();
|
||||
|
||||
// Build the client using the new builder pattern
|
||||
let client = SupervisorBuilder::new()
|
||||
.caller_id("timeout-example-runner")
|
||||
.redis_url("redis://127.0.0.1/")
|
||||
.build()?;
|
||||
info!("Supervisor created.");
|
||||
|
||||
let script_content = r#"
|
||||
// This script will never be executed by a worker because the recipient does not exist.
|
||||
let x = 10;
|
||||
let y = x + 32;
|
||||
y
|
||||
"#;
|
||||
|
||||
// The worker_id points to a worker queue that doesn't have a worker.
|
||||
let non_existent_recipient = "non_existent_worker_for_timeout_test";
|
||||
let very_short_timeout = Duration::from_secs(2);
|
||||
|
||||
info!(
|
||||
"Submitting script to non-existent recipient '{}' with a timeout of {:?}...",
|
||||
non_existent_recipient, very_short_timeout
|
||||
);
|
||||
|
||||
let start_time = Instant::now();
|
||||
|
||||
// Use the new JobBuilder
|
||||
let result = client
|
||||
.new_job()
|
||||
.script_type(ScriptType::HeroScript)
|
||||
.script(script_content)
|
||||
.timeout(very_short_timeout)
|
||||
.await_response()
|
||||
.await;
|
||||
|
||||
match result {
|
||||
Ok(details) => {
|
||||
log::error!(
|
||||
"Timeout Example FAILED: Expected a timeout, but got Ok: {:?}",
|
||||
details
|
||||
);
|
||||
Err("Expected timeout, but task completed successfully.".into())
|
||||
}
|
||||
Err(e) => {
|
||||
let elapsed = start_time.elapsed();
|
||||
info!("Timeout Example: Received error as expected: {}", e);
|
||||
info!("Elapsed time: {:?}", elapsed);
|
||||
|
||||
match e {
|
||||
SupervisorError::Timeout(task_id) => {
|
||||
info!("Timeout Example PASSED: Correctly received SupervisorError::Timeout for task_id: {}", task_id);
|
||||
// Ensure the elapsed time is close to the timeout duration
|
||||
// Allow for some buffer for processing
|
||||
assert!(
|
||||
elapsed >= very_short_timeout
|
||||
&& elapsed < very_short_timeout + Duration::from_secs(1),
|
||||
"Elapsed time {:?} should be close to timeout {:?}",
|
||||
elapsed,
|
||||
very_short_timeout
|
||||
);
|
||||
info!(
|
||||
"Elapsed time {:?} is consistent with timeout duration {:?}.",
|
||||
elapsed, very_short_timeout
|
||||
);
|
||||
Ok(())
|
||||
}
|
||||
other_error => {
|
||||
log::error!(
|
||||
"Timeout Example FAILED: Expected SupervisorError::Timeout, but got other error: {:?}",
|
||||
other_error
|
||||
);
|
||||
Err(format!(
|
||||
"Expected SupervisorError::Timeout, got other error: {:?}",
|
||||
other_error
|
||||
)
|
||||
.into())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user