initial commit
This commit is contained in:
559
core/dispatcher/examples/dispatcher_demo.rs
Normal file
559
core/dispatcher/examples/dispatcher_demo.rs
Normal file
@@ -0,0 +1,559 @@
|
||||
use hero_dispatcher::{Dispatcher, DispatcherBuilder, ScriptType};
|
||||
use log::info;
|
||||
use redis::AsyncCommands;
|
||||
use std::collections::HashMap;
|
||||
use std::time::Duration;
|
||||
use tokio::time::sleep;
|
||||
|
||||
/// Comprehensive example demonstrating the Hero Dispatcher functionality.
|
||||
///
|
||||
/// This example shows:
|
||||
/// 1. Creating a dispatcher instance
|
||||
/// 2. Creating jobs with different configurations
|
||||
/// 3. Submitting jobs to the queue
|
||||
/// 4. Inspecting Redis entries created by the dispatcher
|
||||
/// 5. Running jobs and awaiting results
|
||||
#[tokio::main]
|
||||
async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
env_logger::init();
|
||||
|
||||
println!("🚀 Hero Dispatcher Demo");
|
||||
println!("======================\n");
|
||||
|
||||
// Create dispatcher client with worker vectors per script type
|
||||
let dispatcher = DispatcherBuilder::new()
|
||||
.caller_id("demo-caller")
|
||||
.context_id("demo-context")
|
||||
.heroscript_workers(vec!["hero-worker-1".to_string(), "hero-worker-2".to_string()])
|
||||
.rhai_sal_workers(vec!["rhai-sal-worker-1".to_string()])
|
||||
.rhai_dsl_workers(vec!["rhai-dsl-worker-1".to_string()])
|
||||
.redis_url("redis://127.0.0.1/")
|
||||
.build()?;
|
||||
|
||||
println!("✅ Dispatcher created with:");
|
||||
println!(" - Caller ID: demo-caller");
|
||||
println!(" - Worker ID: demo-worker");
|
||||
println!(" - Context ID: demo-context\n");
|
||||
|
||||
// Create Redis connection for inspection
|
||||
let redis_client = redis::Client::open("redis://127.0.0.1:6379")?;
|
||||
let mut redis_conn = redis_client.get_multiplexed_async_connection().await?;
|
||||
|
||||
// Demo 1: Create a simple job
|
||||
println!("📝 Demo 1: Creating a simple job");
|
||||
println!("--------------------------------");
|
||||
|
||||
let job1 = dispatcher
|
||||
.new_job()
|
||||
.script_type(ScriptType::HeroScript)
|
||||
.script(r#"print("Hello from job 1!");"#)
|
||||
.timeout(Duration::from_secs(10))
|
||||
.build()?;
|
||||
|
||||
println!("Job 1 created with ID: {}", job1.id);
|
||||
|
||||
// Create the job (stores in Redis)
|
||||
dispatcher.create_job(&job1).await?;
|
||||
println!("✅ Job 1 stored in Redis");
|
||||
|
||||
// Inspect Redis entries for this job
|
||||
print_job_redis_entries(&mut redis_conn, &job1.id).await?;
|
||||
println!();
|
||||
|
||||
// Demo 2: Create a job with custom settings
|
||||
println!("📝 Demo 2: Creating a job with custom settings");
|
||||
println!("----------------------------------------------");
|
||||
|
||||
let job2 = dispatcher
|
||||
.new_job()
|
||||
.script_type(ScriptType::RhaiSAL)
|
||||
.script(r#"
|
||||
let result = 42 * 2;
|
||||
print("Calculation result: " + result);
|
||||
result
|
||||
"#)
|
||||
.timeout(Duration::from_secs(30))
|
||||
.build()?;
|
||||
|
||||
println!("Job 2 created with ID: {}", job2.id);
|
||||
|
||||
// Create the job
|
||||
dispatcher.create_job(&job2).await?;
|
||||
println!("✅ Job 2 stored in Redis");
|
||||
|
||||
// Inspect Redis entries
|
||||
print_job_redis_entries(&mut redis_conn, &job2.id).await?;
|
||||
println!();
|
||||
|
||||
// Demo 3: Environment Variables
|
||||
println!("📝 Demo 3: Jobs with Environment Variables");
|
||||
println!("------------------------------------------");
|
||||
|
||||
// Create environment variables map
|
||||
let mut env_vars = HashMap::new();
|
||||
env_vars.insert("API_KEY".to_string(), "secret-api-key-123".to_string());
|
||||
env_vars.insert("DEBUG_MODE".to_string(), "true".to_string());
|
||||
env_vars.insert("MAX_RETRIES".to_string(), "5".to_string());
|
||||
env_vars.insert("SERVICE_URL".to_string(), "https://api.example.com".to_string());
|
||||
|
||||
let job_with_env = dispatcher
|
||||
.new_job()
|
||||
.script_type(ScriptType::HeroScript)
|
||||
.script(r#"
|
||||
print("Environment variables available:");
|
||||
print("API_KEY: " + env.API_KEY);
|
||||
print("DEBUG_MODE: " + env.DEBUG_MODE);
|
||||
print("MAX_RETRIES: " + env.MAX_RETRIES);
|
||||
print("SERVICE_URL: " + env.SERVICE_URL);
|
||||
"Environment variables processed successfully"
|
||||
"#)
|
||||
.env_vars(env_vars.clone())
|
||||
.timeout(Duration::from_secs(15))
|
||||
.build()?;
|
||||
|
||||
println!("Job with environment variables created: {}", job_with_env.id);
|
||||
|
||||
// Store job in Redis
|
||||
dispatcher.create_job(&job_with_env).await?;
|
||||
println!("✅ Job with env vars stored in Redis");
|
||||
|
||||
// Show Redis entries including environment variables
|
||||
print_job_redis_entries(&mut redis_conn, &job_with_env.id).await?;
|
||||
|
||||
// Demonstrate individual env var setting
|
||||
let job_individual_env = dispatcher
|
||||
.new_job()
|
||||
.script_type(ScriptType::RhaiSAL)
|
||||
.script("print('Single env var: ' + env.SINGLE_VAR); 'done'")
|
||||
.env_var("SINGLE_VAR", "individual-value")
|
||||
.env_var("ANOTHER_VAR", "another-value")
|
||||
.build()?;
|
||||
|
||||
println!("Job with individual env vars created: {}", job_individual_env.id);
|
||||
dispatcher.create_job(&job_individual_env).await?;
|
||||
println!("✅ Job with individual env vars stored in Redis");
|
||||
|
||||
print_job_redis_entries(&mut redis_conn, &job_individual_env.id).await?;
|
||||
println!();
|
||||
|
||||
// Demo 4: Create multiple jobs and show queue state
|
||||
println!("📝 Demo 4: Creating multiple jobs and inspecting queue");
|
||||
println!("----------------------------------------------------");
|
||||
|
||||
let mut job_ids = Vec::new();
|
||||
|
||||
for i in 3..=5 {
|
||||
let script_type = match i {
|
||||
3 => ScriptType::HeroScript,
|
||||
4 => ScriptType::RhaiSAL,
|
||||
5 => ScriptType::RhaiDSL,
|
||||
_ => ScriptType::HeroScript,
|
||||
};
|
||||
let job = dispatcher
|
||||
.new_job()
|
||||
.script_type(script_type)
|
||||
.script(&format!(r#"print("Job {} is running");"#, i))
|
||||
.timeout(Duration::from_secs(15))
|
||||
.build()?;
|
||||
|
||||
job_ids.push(job.id.clone());
|
||||
dispatcher.create_job(&job).await?;
|
||||
println!("✅ Job {} created with ID: {}", i, job.id);
|
||||
}
|
||||
|
||||
// Show all Redis keys related to our jobs
|
||||
print_all_dispatcher_redis_keys(&mut redis_conn).await?;
|
||||
println!();
|
||||
|
||||
// Demo 4: Show job status checking
|
||||
println!("📝 Demo 4: Checking job statuses");
|
||||
println!("--------------------------------");
|
||||
|
||||
for job_id in &job_ids {
|
||||
match dispatcher.get_job_status(job_id).await {
|
||||
Ok(status) => println!("Job {}: {:?}", job_id, status),
|
||||
Err(e) => println!("Error getting status for job {}: {}", job_id, e),
|
||||
}
|
||||
}
|
||||
println!();
|
||||
|
||||
// Demo 5: Simulate running a job and getting result (if worker is available)
|
||||
println!("📝 Demo 5: Attempting to run job and await result");
|
||||
println!("------------------------------------------------");
|
||||
|
||||
let simple_job = dispatcher
|
||||
.new_job()
|
||||
.script_type(ScriptType::HeroScript)
|
||||
.script(r#"print("This job will complete quickly"); "success""#)
|
||||
.timeout(Duration::from_secs(5))
|
||||
.build()?;
|
||||
|
||||
println!("Created job for execution: {}", simple_job.id);
|
||||
|
||||
// Try to run the job (this will timeout if no worker is available)
|
||||
match dispatcher.run_job_and_await_result(&simple_job).await {
|
||||
Ok(result) => {
|
||||
println!("✅ Job completed successfully!");
|
||||
println!("Result: {}", result);
|
||||
}
|
||||
Err(e) => {
|
||||
println!("⚠️ Job execution failed (likely no worker available): {}", e);
|
||||
println!(" This is expected if no Hero worker is running");
|
||||
}
|
||||
}
|
||||
|
||||
// Demo 6: List all jobs
|
||||
println!("📝 Demo 6: Listing all jobs");
|
||||
println!("-------------------------");
|
||||
|
||||
let all_job_ids = match dispatcher.list_jobs().await {
|
||||
Ok(job_ids) => {
|
||||
println!("Found {} jobs:", job_ids.len());
|
||||
for job_id in &job_ids {
|
||||
println!(" - {}", job_id);
|
||||
}
|
||||
job_ids
|
||||
}
|
||||
Err(e) => {
|
||||
println!("Error listing jobs: {}", e);
|
||||
Vec::new()
|
||||
}
|
||||
};
|
||||
println!();
|
||||
|
||||
// Demo 7: Create a job with log path and demonstrate logs functionality
|
||||
println!("📝 Demo 7: Job with log path and logs retrieval");
|
||||
println!("-----------------------------------------------");
|
||||
|
||||
let log_job = dispatcher
|
||||
.new_job()
|
||||
.script(r#"print("This job writes to logs"); "log_test""#)
|
||||
.log_path("/tmp/hero_job_demo.log")
|
||||
.timeout(Duration::from_secs(10))
|
||||
.build()?;
|
||||
|
||||
println!("Created job with log path: {}", log_job.id);
|
||||
dispatcher.create_job(&log_job).await?;
|
||||
|
||||
// Try to get logs (will be empty since job hasn't run)
|
||||
match dispatcher.get_job_logs(&log_job.id).await {
|
||||
Ok(Some(logs)) => println!("Job logs: {}", logs),
|
||||
Ok(None) => println!("No logs available for job (expected - job hasn't run or no log file)"),
|
||||
Err(e) => println!("Error getting logs: {}", e),
|
||||
}
|
||||
println!();
|
||||
|
||||
// Demo 8: Stop job functionality
|
||||
println!("📝 Demo 8: Stopping a job");
|
||||
println!("-------------------------");
|
||||
|
||||
if let Some(job_id) = all_job_ids.first() {
|
||||
println!("Attempting to stop job: {}", job_id);
|
||||
match dispatcher.stop_job(job_id).await {
|
||||
Ok(()) => println!("✅ Stop request sent for job {}", job_id),
|
||||
Err(e) => println!("Error stopping job: {}", e),
|
||||
}
|
||||
|
||||
// Show stop queue
|
||||
let stop_queue_key = "hero:stop_queue:demo-worker";
|
||||
let stop_queue_length: i64 = redis_conn.llen(stop_queue_key).await?;
|
||||
println!("📤 Stop queue length ({}): {}", stop_queue_key, stop_queue_length);
|
||||
|
||||
if stop_queue_length > 0 {
|
||||
let stop_items: Vec<String> = redis_conn.lrange(stop_queue_key, 0, -1).await?;
|
||||
println!("📋 Stop queue items:");
|
||||
for (i, item) in stop_items.iter().enumerate() {
|
||||
println!(" {}: {}", i, item);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
println!("No jobs available to stop");
|
||||
}
|
||||
println!();
|
||||
|
||||
// Demo 9: Final Redis state inspection
|
||||
println!("📝 Demo 9: Final Redis state");
|
||||
println!("----------------------------");
|
||||
print_all_dispatcher_redis_keys(&mut redis_conn).await?;
|
||||
|
||||
for job_id in &job_ids {
|
||||
match dispatcher.get_job_status(job_id).await {
|
||||
Ok(status) => println!("Job {}: {:?}", job_id, status),
|
||||
Err(e) => println!("Error getting status for job {}: {}", job_id, e),
|
||||
}
|
||||
}
|
||||
println!();
|
||||
|
||||
// Demo 5: Simulate running a job and getting result (if worker is available)
|
||||
println!("📝 Demo 5: Attempting to run job and await result");
|
||||
println!("------------------------------------------------");
|
||||
|
||||
let simple_job = dispatcher
|
||||
.new_job()
|
||||
.script_type(ScriptType::HeroScript)
|
||||
.script(r#"print("This job will complete quickly"); "success""#)
|
||||
.timeout(Duration::from_secs(5))
|
||||
.build()?;
|
||||
|
||||
println!("Created job for execution: {}", simple_job.id);
|
||||
|
||||
// Try to run the job (this will timeout if no worker is available)
|
||||
match dispatcher.run_job_and_await_result(&simple_job).await {
|
||||
Ok(result) => {
|
||||
println!("✅ Job completed successfully!");
|
||||
println!("Result: {}", result);
|
||||
}
|
||||
Err(e) => {
|
||||
println!("⚠️ Job execution failed (likely no worker available): {}", e);
|
||||
println!(" This is expected if no Hero worker is running");
|
||||
}
|
||||
}
|
||||
|
||||
// Demo 6: List all jobs
|
||||
println!("📝 Demo 6: Listing all jobs");
|
||||
println!("-------------------------");
|
||||
|
||||
let all_job_ids = match dispatcher.list_jobs().await {
|
||||
Ok(job_ids) => {
|
||||
println!("Found {} jobs:", job_ids.len());
|
||||
for job_id in &job_ids {
|
||||
println!(" - {}", job_id);
|
||||
}
|
||||
job_ids
|
||||
}
|
||||
Err(e) => {
|
||||
println!("Error listing jobs: {}", e);
|
||||
Vec::new()
|
||||
}
|
||||
};
|
||||
println!();
|
||||
|
||||
// Demo 7: Create a job with log path and demonstrate logs functionality
|
||||
println!("📝 Demo 7: Job with log path and logs retrieval");
|
||||
println!("-----------------------------------------------");
|
||||
|
||||
let log_job = dispatcher
|
||||
.new_job()
|
||||
.script(r#"print("This job writes to logs"); "log_test""#)
|
||||
.log_path("/tmp/hero_job_demo.log")
|
||||
.timeout(Duration::from_secs(10))
|
||||
.build()?;
|
||||
|
||||
println!("Created job with log path: {}", log_job.id);
|
||||
dispatcher.create_job(&log_job).await?;
|
||||
|
||||
// Try to get logs (will be empty since job hasn't run)
|
||||
match dispatcher.get_job_logs(&log_job.id).await {
|
||||
Ok(Some(logs)) => println!("Job logs: {}", logs),
|
||||
Ok(None) => println!("No logs available for job (expected - job hasn't run or no log file)"),
|
||||
Err(e) => println!("Error getting logs: {}", e),
|
||||
}
|
||||
println!();
|
||||
|
||||
// Demo 8: Stop job functionality
|
||||
println!("📝 Demo 8: Stopping a job");
|
||||
println!("-------------------------");
|
||||
|
||||
if let Some(job_id) = all_job_ids.first() {
|
||||
println!("Attempting to stop job: {}", job_id);
|
||||
match dispatcher.stop_job(job_id).await {
|
||||
Ok(()) => println!("✅ Stop request sent for job {}", job_id),
|
||||
Err(e) => println!("Error stopping job: {}", e),
|
||||
}
|
||||
|
||||
// Show stop queue
|
||||
let stop_queue_key = "hero:stop_queue:demo-worker";
|
||||
let stop_queue_length: i64 = redis_conn.llen(stop_queue_key).await?;
|
||||
println!("📤 Stop queue length ({}): {}", stop_queue_key, stop_queue_length);
|
||||
|
||||
if stop_queue_length > 0 {
|
||||
let stop_items: Vec<String> = redis_conn.lrange(stop_queue_key, 0, -1).await?;
|
||||
println!("📋 Stop queue items:");
|
||||
for (i, item) in stop_items.iter().enumerate() {
|
||||
println!(" {}: {}", i, item);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
println!("No jobs available to stop");
|
||||
}
|
||||
println!();
|
||||
|
||||
// Demo 9: Final Redis state inspection
|
||||
println!("📝 Demo 9: Final Redis state");
|
||||
println!("----------------------------");
|
||||
print_all_dispatcher_redis_keys(&mut redis_conn).await?;
|
||||
|
||||
println!("\n🎉 Dispatcher demo completed!");
|
||||
println!("💡 New features demonstrated:");
|
||||
println!(" - list_jobs(): List all job IDs");
|
||||
println!(" - stop_job(): Send stop request to worker");
|
||||
println!(" - get_job_logs(): Retrieve job logs from file");
|
||||
println!(" - log_path(): Configure log file for jobs");
|
||||
println!("💡 To see job execution in action, start a Hero worker that processes the 'demo-worker' queue");
|
||||
|
||||
// Demo 6: Demonstrate new job management features
|
||||
println!("📝 Demo 6: Job Management - Delete and Clear Operations");
|
||||
println!("--------------------------------------------------------");
|
||||
|
||||
// List all current jobs
|
||||
match dispatcher.list_jobs().await {
|
||||
Ok(jobs) => {
|
||||
println!("Current jobs in system: {:?}", jobs);
|
||||
|
||||
if !jobs.is_empty() {
|
||||
// Delete the first job as an example
|
||||
let job_to_delete = &jobs[0];
|
||||
println!("Deleting job: {}", job_to_delete);
|
||||
match dispatcher.delete_job(job_to_delete).await {
|
||||
Ok(()) => println!("✅ Job {} deleted successfully", job_to_delete),
|
||||
Err(e) => println!("❌ Error deleting job {}: {}", job_to_delete, e),
|
||||
}
|
||||
|
||||
// Show updated job list
|
||||
match dispatcher.list_jobs().await {
|
||||
Ok(remaining_jobs) => println!("Remaining jobs: {:?}", remaining_jobs),
|
||||
Err(e) => println!("Error listing jobs: {}", e),
|
||||
}
|
||||
}
|
||||
}
|
||||
Err(e) => println!("Error listing jobs: {}", e),
|
||||
}
|
||||
|
||||
println!();
|
||||
|
||||
// Demonstrate clear all jobs
|
||||
println!("Clearing all remaining jobs...");
|
||||
match dispatcher.clear_all_jobs().await {
|
||||
Ok(count) => println!("✅ Cleared {} jobs from Redis", count),
|
||||
Err(e) => println!("❌ Error clearing jobs: {}", e),
|
||||
}
|
||||
|
||||
// Verify all jobs are cleared
|
||||
match dispatcher.list_jobs().await {
|
||||
Ok(jobs) => {
|
||||
if jobs.is_empty() {
|
||||
println!("✅ All jobs successfully cleared from Redis");
|
||||
} else {
|
||||
println!("⚠️ Some jobs remain: {:?}", jobs);
|
||||
}
|
||||
}
|
||||
Err(e) => println!("Error verifying job clearance: {}", e),
|
||||
}
|
||||
|
||||
println!();
|
||||
println!("🎉 Demo completed! The dispatcher now supports:");
|
||||
println!(" • Script type routing (HeroScript, RhaiSAL, RhaiDSL)");
|
||||
println!(" • Multiple workers per script type for load balancing");
|
||||
println!(" • Automatic worker selection based on job script type");
|
||||
println!(" • Job management: list, delete, and clear operations");
|
||||
println!(" • Enhanced job logging and monitoring");
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Print Redis entries for a specific job
|
||||
async fn print_job_redis_entries(
|
||||
conn: &mut redis::aio::MultiplexedConnection,
|
||||
job_id: &str,
|
||||
) -> Result<(), redis::RedisError> {
|
||||
let job_key = format!("hero:job:{}", job_id);
|
||||
|
||||
println!("🔍 Redis entries for job {}:", job_id);
|
||||
|
||||
// Check if job hash exists
|
||||
let exists: bool = conn.exists(&job_key).await?;
|
||||
if exists {
|
||||
// Check if the key is actually a hash before trying to get all fields
|
||||
let key_type: String = redis::cmd("TYPE").arg(&job_key).query_async(conn).await?;
|
||||
if key_type == "hash" {
|
||||
let job_data: std::collections::HashMap<String, String> = conn.hgetall(&job_key).await?;
|
||||
println!(" 📋 Job data ({}): ", job_key);
|
||||
for (field, value) in job_data {
|
||||
println!(" {}: {}", field, value);
|
||||
}
|
||||
} else {
|
||||
println!(" ⚠️ Key {} exists but is not a hash (type: {})", job_key, key_type);
|
||||
}
|
||||
} else {
|
||||
println!(" ❌ No job data found at key: {}", job_key);
|
||||
}
|
||||
|
||||
// Check work queue
|
||||
let queue_key = "hero:work_queue:demo-worker";
|
||||
let queue_length: i64 = conn.llen(queue_key).await?;
|
||||
println!(" 📤 Work queue length ({}): {}", queue_key, queue_length);
|
||||
|
||||
if queue_length > 0 {
|
||||
let queue_items: Vec<String> = conn.lrange(queue_key, 0, -1).await?;
|
||||
println!(" 📋 Queue items:");
|
||||
for (i, item) in queue_items.iter().enumerate() {
|
||||
println!(" {}: {}", i, item);
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Print all dispatcher-related Redis keys
|
||||
async fn print_all_dispatcher_redis_keys(
|
||||
conn: &mut redis::aio::MultiplexedConnection,
|
||||
) -> Result<(), redis::RedisError> {
|
||||
println!("🔍 All Hero Dispatcher Redis keys:");
|
||||
|
||||
// Get all keys with hero: prefix
|
||||
let keys: Vec<String> = conn.keys("hero:*").await?;
|
||||
|
||||
if keys.is_empty() {
|
||||
println!(" ❌ No Hero keys found in Redis");
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
// Group keys by type
|
||||
let mut job_keys = Vec::new();
|
||||
let mut queue_keys = Vec::new();
|
||||
let mut other_keys = Vec::new();
|
||||
|
||||
for key in keys {
|
||||
if key.starts_with("hero:job:") {
|
||||
job_keys.push(key);
|
||||
} else if key.contains("queue") {
|
||||
queue_keys.push(key);
|
||||
} else {
|
||||
other_keys.push(key);
|
||||
}
|
||||
}
|
||||
|
||||
// Print job keys
|
||||
if !job_keys.is_empty() {
|
||||
println!(" 📋 Job entries:");
|
||||
for key in job_keys {
|
||||
// Check if the key is actually a hash before trying to get all fields
|
||||
let key_type: String = redis::cmd("TYPE").arg(&key).query_async(conn).await?;
|
||||
if key_type == "hash" {
|
||||
let job_data: std::collections::HashMap<String, String> = conn.hgetall(&key).await?;
|
||||
println!(" {}: {} fields", key, job_data.len());
|
||||
} else {
|
||||
println!(" {}: {} (not a hash, skipping)", key, key_type);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Print queue keys
|
||||
if !queue_keys.is_empty() {
|
||||
println!(" 📤 Queue entries:");
|
||||
for key in queue_keys {
|
||||
let length: i64 = conn.llen(&key).await?;
|
||||
println!(" {}: {} items", key, length);
|
||||
}
|
||||
}
|
||||
|
||||
// Print other keys
|
||||
if !other_keys.is_empty() {
|
||||
println!(" 🔧 Other entries:");
|
||||
for key in other_keys {
|
||||
println!(" {}", key);
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
90
core/dispatcher/examples/timeout_example.rs
Normal file
90
core/dispatcher/examples/timeout_example.rs
Normal file
@@ -0,0 +1,90 @@
|
||||
use log::info;
|
||||
use hero_dispatcher::{DispatcherBuilder, DispatcherError, 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 = DispatcherBuilder::new()
|
||||
.caller_id("timeout-example-runner")
|
||||
.redis_url("redis://127.0.0.1/")
|
||||
.build()?;
|
||||
info!("Dispatcher 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 {
|
||||
DispatcherError::Timeout(task_id) => {
|
||||
info!("Timeout Example PASSED: Correctly received DispatcherError::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 DispatcherError::Timeout, but got other error: {:?}",
|
||||
other_error
|
||||
);
|
||||
Err(format!(
|
||||
"Expected DispatcherError::Timeout, got other error: {:?}",
|
||||
other_error
|
||||
)
|
||||
.into())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user