### Basic Actor Setup with Tracing Source: https://github.com/hiking90/rsactor/blob/main/docs/tracing.md This example demonstrates the basic setup of an actor with tracing enabled. It initializes the tracing subscriber and spawns an actor that processes messages. Tracing events are generated if the 'tracing' feature is enabled. ```rust use rsactor::{Actor, ActorRef, message_handlers, spawn}; use std::time::Duration; #[derive(Actor)] struct MyActor { counter: u64, } struct Increment; struct GetCounter; #[message_handlers] impl MyActor { #[handler] async fn handle_increment(&mut self, _msg: Increment, _: &ActorRef) -> u64 { self.counter += 1; self.counter } #[handler] async fn handle_get_counter(&mut self, _msg: GetCounter, _: &ActorRef) -> u64 { self.counter } } #[tokio::main] async fn main() -> Result<(), Box> { // Initialize tracing subscriber #[cfg(feature = "tracing")] { tracing_subscriber::fmt() .with_max_level(tracing::Level::DEBUG) .with_target(false) .init(); println!("🚀 Tracing is ENABLED"); } #[cfg(not(feature = "tracing"))] { println!("📝 Tracing is DISABLED"); } let actor = MyActor { counter: 0 }; let (actor_ref, _handle) = spawn(actor); // This will generate tracing events when tracing feature is enabled let count = actor_ref.ask(Increment).await?; println!("Count: {}", count); actor_ref.stop().await?; Ok(()) } ``` -------------------------------- ### Run Example Source: https://github.com/hiking90/rsactor/blob/main/book/src/examples/handler_demo.md Command to compile and run the handler_demo example. ```bash cargo run --example handler_demo ``` -------------------------------- ### Running Unified Macro Example Source: https://github.com/hiking90/rsactor/blob/main/book/src/macros/unified_macro.md Command to run the example demonstrating the unified macro. ```bash cargo run --example unified_macro_demo ``` -------------------------------- ### Run Example with Tracing Source: https://github.com/hiking90/rsactor/blob/main/README.md Execute a basic example with tracing enabled. Ensure RUST_LOG is set to debug. ```bash RUST_LOG=debug cargo run --example basic --features tracing ``` -------------------------------- ### Run rsactor Example Source: https://github.com/hiking90/rsactor/blob/main/README.md Demonstrates how to run any of the provided rsactor examples using Cargo. Replace `` with the desired example. ```bash cargo run --example ``` -------------------------------- ### Run Core Examples with Cargo Source: https://github.com/hiking90/rsactor/blob/main/book/src/examples.md Demonstrates how to compile and run the core rsActor examples using Cargo. ```bash cargo run --example basic cargo run --example actor_async_worker cargo run --example actor_blocking_task ``` -------------------------------- ### Run Metrics Example Source: https://github.com/hiking90/rsactor/blob/main/book/src/advanced/metrics.md Execute the metrics demonstration example using Cargo, ensuring the 'metrics' feature is enabled. ```bash cargo run --example metrics_demo --features metrics ``` -------------------------------- ### Run Advanced Examples with Cargo Source: https://github.com/hiking90/rsactor/blob/main/book/src/examples.md Demonstrates how to compile and run the advanced rsActor examples using Cargo. ```bash cargo run --example kill_demo cargo run --example ask_join_demo cargo run --example handler_demo cargo run --example weak_reference_demo ``` -------------------------------- ### Quick Start: Managing Actors with TellHandler Source: https://github.com/hiking90/rsactor/blob/main/docs/handler_traits_design.md Demonstrates how to manage different Actor types that handle the same message in a single collection using `TellHandler`. This setup is useful for broadcasting messages to multiple actors. ```rust use rsactor::{TellHandler, AskHandler, WeakTellHandler, WeakAskHandler}; // Manage different Actor types in a single collection let handlers: Vec>> = vec![ (&actor_a).into(), // From<&ActorRef> actor_b.into(), // From> ]; // Send messages to all handlers for handler in &handlers { handler.tell(PingMsg { timestamp: 12345 }).await?; } ``` -------------------------------- ### Run Dining Philosophers Example Source: https://github.com/hiking90/rsactor/blob/main/book/src/examples/dining_philosophers.md Execute the dining philosophers example using Cargo. This command compiles and runs the example, demonstrating actor-based concurrency. ```bash cargo run --example dining_philosophers ``` -------------------------------- ### Run metrics demo example Source: https://github.com/hiking90/rsactor/blob/main/docs/metrics.md Execute the metrics demo example with the 'metrics' feature enabled. For enhanced debugging, enable tracing. ```bash # Basic execution cargo run --example metrics_demo --features metrics # With tracing enabled RUST_LOG=debug cargo run --example metrics_demo --features "metrics tracing" ``` -------------------------------- ### Run Weak Reference Demo Example Source: https://github.com/hiking90/rsactor/blob/main/book/src/advanced/weak_references.md Execute the example demonstrating weak references using the provided cargo command. ```bash cargo run --example weak_reference_demo ``` -------------------------------- ### Client Code to Get User Profile Source: https://github.com/hiking90/rsactor/blob/main/docs/FAQ.md Example of client code using `ask` to send a `GetUserDetails` message to a `UserManagerActor` and process the response. ```rust async fn get_user_profile( user_manager: &ActorRef, user_id: UserId, ) -> Result { let user_details = user_manager.ask(GetUserDetails { user_id }).await??; Ok(user_details) } ``` -------------------------------- ### Run Feature-Gated Examples with Cargo Source: https://github.com/hiking90/rsactor/blob/main/book/src/examples.md Demonstrates how to compile and run rsActor examples that require specific feature flags using Cargo. ```bash cargo run --example metrics_demo --features metrics cargo run --example tracing_demo --features tracing ``` -------------------------------- ### Run Tracing Demonstration Source: https://github.com/hiking90/rsactor/blob/main/docs/tracing.md Execute the tracing demonstration example with tracing enabled. Ensure the 'tracing' feature is enabled for this command. ```bash # Comprehensive tracing demonstration RUST_LOG=debug cargo run --example tracing_demo --features tracing ``` ```bash # Actor lifecycle and weak references RUST_LOG=debug cargo run --example weak_reference_demo --features tracing ``` ```bash # Explicit kill scenario tracing RUST_LOG=debug cargo run --example kill_demo --features tracing ``` ```bash # Run without tracing to see the difference cargo run --example tracing_demo ``` -------------------------------- ### Run Basic rsactor Example Source: https://github.com/hiking90/rsactor/blob/main/README.md Command to run the 'basic' example, which demonstrates core concepts of rsactor using the `#[message_handlers]` macro. ```bash cargo run --example basic ``` -------------------------------- ### Running Derive Macro Demo Source: https://github.com/hiking90/rsactor/blob/main/book/src/macros/unified_macro.md Command to run the example demonstrating the derive macro. ```bash cargo run --example derive_macro_demo ``` -------------------------------- ### Run rsactor Example with Tracing Source: https://github.com/hiking90/rsactor/blob/main/README.md Shows how to enable and run rsactor examples with tracing support enabled. This requires the `tracing` feature and setting the `RUST_LOG` environment variable. ```bash RUST_LOG=debug cargo run --example --features tracing ``` -------------------------------- ### Run Async Worker Example Source: https://github.com/hiking90/rsactor/blob/main/book/src/examples/async_worker.md Command to compile and run the asynchronous worker actor example using Cargo. ```bash cargo run --example actor_async_worker ``` -------------------------------- ### Basic tracing setup Source: https://github.com/hiking90/rsactor/blob/main/book/src/advanced/tracing.md Initialize the tracing subscriber with a maximum log level and disable target information for cleaner output. This setup is essential before your actor code runs. ```rust use tracing_subscriber; #[tokio::main] async fn main() { // Simple setup tracing_subscriber::fmt() .with_max_level(tracing::Level::DEBUG) .with_target(false) .init(); // Your actor code here... } ``` -------------------------------- ### Run Blocking Task Example Source: https://github.com/hiking90/rsactor/blob/main/book/src/examples/blocking_task.md Command to compile and run the blocking task actor example. Ensure you have the necessary dependencies and project structure set up. ```bash cargo run --example actor_blocking_task ``` -------------------------------- ### Install rsActor Claude Code Skills Globally Source: https://github.com/hiking90/rsactor/blob/main/README.md Install the rsActor Claude Code skills globally for use across projects. This is the recommended installation method. ```bash curl -sSL https://raw.githubusercontent.com/hiking90/rsactor/main/install-skills.sh | bash ``` -------------------------------- ### Install rsActor Claude Code Skills Locally Source: https://github.com/hiking90/rsactor/blob/main/README.md Install the rsActor Claude Code skills for a specific project. Use this if you prefer project-local installations. ```bash curl -sSL https://raw.githubusercontent.com/hiking90/rsactor/main/install-skills.sh | bash -s -- --local ``` -------------------------------- ### Run Actor Timeout Example Source: https://github.com/hiking90/rsactor/blob/main/book/src/examples/actor_with_timeout.md Execute the actor timeout example using Cargo to observe its behavior, including successful queries, timeouts, and error handling. ```bash cargo run --example actor_with_timeout ``` -------------------------------- ### Complete rsactor Debugging Example Source: https://github.com/hiking90/rsactor/blob/main/docs/debugging_guide.md A full example demonstrating actor creation, message handling, `ask_with_timeout`, error handling with debugging tips, actor stopping, and dead letter generation. ```rust use rsactor::{spawn, Actor, ActorRef, Error, message_handlers}; use std::time::Duration; #[derive(Actor)] struct WorkerActor; struct Work { id: u32 } struct Query; #[message_handlers] impl WorkerActor { #[handler] async fn handle_work(&mut self, msg: Work, _ctx: &ActorRef) { println!("Processing work {}", msg.id); } #[handler] async fn handle_query(&mut self, _msg: Query, _ctx: &ActorRef) -> String { "status: ok".to_string() } } #[tokio::main] async fn main() -> Result<(), Box> { // Initialize tracing to observe dead letters tracing_subscriber::fmt() .with_env_filter("rsactor=warn") .init(); let (actor_ref, handle) = spawn::(()); // Normal operation actor_ref.tell(Work { id: 1 }).await?; // With timeout match actor_ref.ask_with_timeout(Query, Duration::from_secs(5)).await { Ok(response) => println!("Response: {}", response), Err(e) => { eprintln!("Error: {}", e); if e.is_retryable() { eprintln!("This error is retryable"); } for tip in e.debugging_tips() { eprintln!("Tip: {}", tip); } } } // Stop the actor actor_ref.stop().await?; handle.await?; // This will generate a dead letter (logged automatically) let result = actor_ref.tell(Work { id: 2 }).await; assert!(result.is_err()); Ok(()) } ``` -------------------------------- ### Unified Actor Lifecycle Management Example Source: https://github.com/hiking90/rsactor/blob/main/docs/handler_traits_design.md Demonstrates storing different actor types in a unified collection for lifecycle management, checking their status, and stopping them. ```rust use rsactor::ActorControl; // Store different actor types for unified lifecycle management let controls: Vec> = vec![ (&worker_actor).into(), (&logger_actor).into(), ]; // Check status and stop all for control in &controls { println!("Actor {} alive: {}", control.identity(), control.is_alive()); control.stop().await?; } ``` -------------------------------- ### Example Trace Output for Message Processing Source: https://github.com/hiking90/rsactor/blob/main/docs/tracing.md Shows example structured log output for actor message processing, including handler execution and potential unhandled message warnings. ```log DEBUG rsactor::actor: Actor processing message message_type=Ping actor_id=DemoActor DEBUG rsactor::actor: Actor processing message message_type=Increment actor_id=DemoActor WARN rsactor::actor: Unhandled message type expected_types=["Ping", "Increment"] actual_type_id=TypeId { .. } ``` -------------------------------- ### Demonstrate Actor Timeout Behavior Source: https://github.com/hiking90/rsactor/blob/main/book/src/timeouts.md This example showcases an actor that simulates slow responses and demonstrates how `ask_with_timeout` behaves with insufficient and sufficient timeout durations. ```rust use rsactor::{message_handlers, spawn, Actor, ActorRef}; use std::time::Duration; use anyhow::Result; use tracing::info; // Define an actor that can process requests with varying response times struct TimeoutDemoActor { name: String, } impl Actor for TimeoutDemoActor { type Args = String; type Error = anyhow::Error; async fn on_start(name: Self::Args, _ar: &ActorRef) -> Result { Ok(Self { name }) } } struct FastQuery(String); struct SlowQuery(String); #[message_handlers] impl TimeoutDemoActor { #[handler] async fn handle_fast_query(&mut self, msg: FastQuery, _: &ActorRef) -> String { // Fast response - completes immediately format!("{}: Fast response to: {{}}", self.name, msg.0) } #[handler] async fn handle_slow_query(&mut self, msg: SlowQuery, _: &ActorRef) -> String { // Slow response - takes 500ms to complete tokio::time::sleep(Duration::from_millis(500)).await; format!("{}: Slow response to: {{}}", self.name, msg.0) } } #[tokio::main] async fn main() -> Result<()> { tracing_subscriber::fmt::init(); let (actor_ref, _join_handle) = spawn::("Demo".to_string()); // Fast query with sufficient timeout - should succeed info!("=== Fast query with long timeout ==="); match actor_ref.ask_with_timeout( FastQuery("What is your name?".to_string()), Duration::from_millis(100) ).await { Ok(response) => info!("Success: {{}}", response), Err(e) => info!("Failed: {{}}", e), } // Slow query with insufficient timeout - should fail info!("=== Slow query with short timeout ==="); match actor_ref.ask_with_timeout( SlowQuery("Complex calculation".to_string()), Duration::from_millis(100) // Less than 500ms needed ).await { Ok(response) => info!("Success: {{}}", response), Err(e) => info!("Failed: {{}}", e), } // Slow query with sufficient timeout - should succeed info!("=== Slow query with sufficient timeout ==="); match actor_ref.ask_with_timeout( SlowQuery("Another calculation".to_string()), Duration::from_millis(1000) // More than 500ms needed ).await { Ok(response) => info!("Success: {{}}", response), Err(e) => info!("Failed: {{}}", e), } Ok(()) } ``` -------------------------------- ### Actor Ask Request-Response Example Source: https://github.com/hiking90/rsactor/blob/main/book/src/communicating_with_actors/ask.md Demonstrates sending a message using the `ask` pattern and awaiting a typed reply. This is suitable for request-response interactions where a direct result is needed. ```rust use rsactor::{Actor, ActorRef, message_handlers, spawn}; use anyhow::Result; use tracing::info; #[derive(Actor)] struct CalculatorActor { last_result: i32, } struct AddMsg(i32, i32); struct GetLastResult; #[message_handlers] impl CalculatorActor { #[handler] async fn handle_add(&mut self, msg: AddMsg, _: &ActorRef) -> i32 { let sum = msg.0 + msg.1; self.last_result = sum; sum } #[handler] async fn handle_get_last(&mut self, _: GetLastResult, _: &ActorRef) -> i32 { self.last_result } } #[tokio::main] async fn main() -> Result<()> { tracing_subscriber::fmt::init(); let (calc_ref, jh) = spawn::(CalculatorActor { last_result: 0 }); // Send an AddMsg using ask and await the reply let sum: i32 = calc_ref.ask(AddMsg(10, 25)).await?; info!("Sum: {}", sum); // Sum: 35 let last: i32 = calc_ref.ask(GetLastResult).await?; assert_eq!(sum, last); calc_ref.stop().await?; jh.await?; Ok(()) } ``` -------------------------------- ### Initialize Tracing Subscriber Source: https://github.com/hiking90/rsactor/blob/main/docs/debugging_guide.md Sets up the `tracing` subscriber with an environment filter to control log output, typically called at the start of `main`. ```rust fn main() { tracing_subscriber::fmt() .with_env_filter(tracing_subscriber::EnvFilter::from_default_env()) .init(); // Your actor code } ``` -------------------------------- ### Manual Actor Implementation with `Actor` Trait Source: https://github.com/hiking90/rsactor/blob/main/book/src/creating_actors.md Implement the `Actor` trait for full control over actor initialization and lifecycle. The `on_start` method handles setup using provided arguments. ```rust struct DatabaseActor { connection: DbConnection, } impl Actor for DatabaseActor { type Args = String; // connection string type Error = anyhow::Error; async fn on_start(conn_str: Self::Args, _: &ActorRef) -> Result { let connection = DbConnection::connect(&conn_str).await?; Ok(Self { connection }) } } ``` -------------------------------- ### Example Trace Output for Message Operations Source: https://github.com/hiking90/rsactor/blob/main/docs/tracing.md Demonstrates typical structured log output for various message sending operations like 'actor_ask', 'actor_tell', and 'actor_ask_with_timeout'. ```log DEBUG actor_ask{actor_id=DemoActor message_type=Ping reply_type=String}: Sending ask message and waiting for reply DEBUG actor_ask{actor_id=DemoActor message_type=Ping reply_type=String}: Ask reply received successfully DEBUG actor_tell{actor_id=DemoActor message_type=Increment}: Sending tell message (fire-and-forget) DEBUG actor_tell{actor_id=DemoActor message_type=Increment}: Tell message sent successfully DEBUG actor_ask_with_timeout{actor_id=DemoActor message_type=SlowOperation reply_type=String timeout_ms=150}: Sending ask message with timeout WARN actor_ask_with_timeout{actor_id=DemoActor message_type=SlowOperation reply_type=String timeout_ms=150}: Ask with timeout failed error=Timeout { identity: Identity { name: "DemoActor", id: ... }, timeout: 150ms, operation: "ask" } ``` -------------------------------- ### AskHandler Usage Example Source: https://github.com/hiking90/rsactor/blob/main/docs/handler_traits_design.md Shows how to use `AskHandler` to collect responses from multiple actors that return the same reply type. This is useful for aggregating status or data from various sources. ```rust // Actors that return the same Reply type let handlers: Vec>> = vec![ (&counter_actor).into(), (&logger_actor).into(), ]; // Collect status from all actors for handler in &handlers { let status = handler.ask(GetStatus).await?; println!("{}: {} messages", status.name, status.message_count); } ``` -------------------------------- ### Log Errors with Debugging Tips Source: https://github.com/hiking90/rsactor/blob/main/docs/debugging_guide.md Utilize `debugging_tips()` to get actionable suggestions for resolving specific error types. This helps in diagnosing and fixing issues more efficiently. ```rust use rsactor::Error; fn log_error_with_tips(err: &Error) { eprintln!("Error: {}", err); eprintln!("Debugging tips:"); for tip in err.debugging_tips() { eprintln!(" - {}", tip); } } ``` -------------------------------- ### Test Actor Increment and Get Count Functionality Source: https://github.com/hiking90/rsactor/blob/main/docs/FAQ.md Integration-style tests for actors involve spawning the actor and interacting with it using `ask`. This example demonstrates testing a `CounterActor`'s increment and get count operations. ```rust @tokio::test async fn test_counter_actor() { let (actor_ref, _handle) = spawn::(0); // Test increment let result = actor_ref.ask(Increment(5)).await.unwrap(); assert_eq!(result, 5); // Test get count let count = actor_ref.ask(GetCount).await.unwrap(); assert_eq!(count, 5); } ``` -------------------------------- ### Create a Simple Counter Actor with Derive Macro Source: https://github.com/hiking90/rsactor/blob/main/docs/getting_started.md Use the Actor derive macro for simple actors. Define message types, derive Actor, and use the message_handlers macro for automatic message handling. This example demonstrates incrementing and getting a count. ```rust use rsactor::{Actor, ActorRef, message_handlers, spawn}; // 1. Define message types struct Increment; struct GetCount; // 2. Define your actor struct and derive Actor #[derive(Actor)] struct CounterActor { count: u32, } // 3. Use the message_handlers macro for automatic message handling #[message_handlers] impl CounterActor { #[handler] async fn handle_increment(&mut self, _msg: Increment, _: &ActorRef) -> () { self.count += 1; } #[handler] async fn handle_get_count(&mut self, _msg: GetCount, _: &ActorRef) -> u32 { self.count } } // 4. Usage #[tokio::main] async fn main() -> Result<(), Box> { let actor = CounterActor { count: 0 }; let (actor_ref, _join_handle) = spawn::(actor); // Send messages to the actor actor_ref.tell(Increment).await?; let count = actor_ref.ask(GetCount).await?; println!("Count: {}", count); // Prints: Count: 1 actor_ref.stop().await?; Ok(()) } ``` -------------------------------- ### Proper Resource Initialization in `on_start` (Rust) Source: https://github.com/hiking90/rsactor/blob/main/docs/best_practices.md Use `on_start` for all resource initialization that might fail, such as database connections or network setups. This method should return a `Result` to handle potential initialization errors. ```rust impl Actor for DatabaseActor { type Args = DatabaseConfig; type Error = anyhow::Error; async fn on_start(config: Self::Args, _: &ActorRef) -> Result { // Initialize resources that might fail let pool = sqlx::PgPool::connect(&config.database_url).await?; // Run migrations or health checks sqlx::migrate!("./migrations").run(&pool).await?; Ok(DatabaseActor { pool }) } } ``` -------------------------------- ### Implement Actor Initialization with on_start Source: https://github.com/hiking90/rsactor/blob/main/book/src/core_concepts/actor_lifecycle.md Implement the `on_start` method to initialize an actor's state and acquire resources. This method is required and receives spawn arguments and a reference to the actor itself. It must return `Ok(Self)` on success or `Err` on failure. ```rust async fn on_start(args: Self::Args, actor_ref: &ActorRef) -> Result { // Initialize state, acquire resources Ok(Self { /* ... */ }) } ``` -------------------------------- ### Compile-Time Type Safety Example Source: https://github.com/hiking90/rsactor/blob/main/docs/getting_started.md Demonstrates rsActor's compile-time type safety. The first example shows a valid message send, while the commented-out second example illustrates a compile error when an incorrect message type is sent. ```rust let count: u32 = actor_ref.ask(IncrementMsg(5)).await?; // This will NOT compile - CounterActor doesn't handle this message type // let result = actor_ref.ask("invalid message").await?; // Compile error! ``` -------------------------------- ### Define and Use a Simple Actor with Derive Macros Source: https://github.com/hiking90/rsactor/blob/main/docs/FAQ.md Example demonstrating how to define a simple actor using derive macros and handle messages. This approach is recommended for most use cases. Ensure Tokio is set up for async execution. ```rust use rsactor::{Actor, ActorRef, message_handlers, spawn}; // Define actor struct with derive macro #[derive(Actor)] struct SimpleActor { counter: u32, } // Define message types struct Increment(u32); struct GetCount; // Use message_handlers macro with handler attributes #[message_handlers] impl SimpleActor { #[handler] async fn handle_increment(&mut self, msg: Increment, _: &ActorRef) -> u32 { self.counter += msg.0; self.counter } #[handler] async fn handle_get_count(&mut self, _msg: GetCount, _: &ActorRef) -> u32 { self.counter } } #[tokio::main] async fn main() -> Result<(), Box> { // Create and spawn actor let actor = SimpleActor { counter: 0 }; let (actor_ref, _handle) = spawn(actor); // Send messages let new_count = actor_ref.ask(Increment(5)).await?; println!("New count: {}", new_count); let current_count = actor_ref.ask(GetCount).await?; println!("Current count: {}", current_count); // Gracefully stop the actor actor_ref.stop().await?; Ok(()) } ``` -------------------------------- ### Implement Request-Response Pattern with Ask Source: https://github.com/hiking90/rsactor/blob/main/docs/FAQ.md Shows how to implement the request-response pattern using the `ask` method and defining a `Reply` type for messages. ```rust // Request message struct GetUserDetails { ``` -------------------------------- ### Run Tracing Demo Source: https://github.com/hiking90/rsactor/blob/main/book/src/examples/tracing.md Provides commands to run the tracing demo. The first command enables instrumentation spans by including the `tracing` feature, while the second command runs only with core logging. ```bash # With instrumentation spans RUST_LOG=debug cargo run --example tracing_demo --features tracing ``` ```bash # Without instrumentation (core logging only) RUST_LOG=debug cargo run --example tracing_demo ``` -------------------------------- ### Basic Counter Actor Example Source: https://github.com/hiking90/rsactor/blob/main/book/src/getting_started.md Implement a simple counter actor that maintains a count and increments it upon receiving an IncrementMsg. This example showcases actor state management, message handling, and lifecycle control. ```rust use rsactor::{Actor, ActorRef, message_handlers, spawn}; use anyhow::Result; use tracing::info; // Define actor struct #[derive(Debug)] // Added Debug for printing the actor in ActorResult struct CounterActor { count: u32, } // Implement Actor trait impl Actor for CounterActor { type Args = u32; // Define an args type for actor creation type Error = anyhow::Error; // on_start is required and must be implemented. // on_run and on_stop are optional and have default implementations. async fn on_start(initial_count: Self::Args, actor_ref: &ActorRef) -> Result { info!("CounterActor (id: {})", actor_ref.identity()); Ok(CounterActor { count: initial_count, }) } } // Define message types struct Increment(u32); // Use message_handlers macro with handler attributes #[message_handlers] impl CounterActor { #[handler] async fn handle_increment(&mut self, msg: Increment, _: &ActorRef) -> u32 { self.count += msg.0; self.count } } #[tokio::main] async fn main() -> Result<()> { tracing_subscriber::fmt::init(); // Initialize tracing info!("Creating CounterActor"); let (actor_ref, join_handle) = spawn::(0u32); // Pass initial count info!("CounterActor spawned with ID: {}", actor_ref.identity()); let new_count: u32 = actor_ref.ask(Increment(5)).await?; info!("Incremented count: {}", new_count); actor_ref.stop().await?; info!("Stop signal sent to CounterActor (ID: {})", actor_ref.identity()); let actor_result = join_handle.await?; info!( "CounterActor (ID: {})", actor_ref.identity(), actor_result ); Ok(()) } ``` -------------------------------- ### Monitor Actor Lifecycle with on_start and on_stop Source: https://github.com/hiking90/rsactor/blob/main/docs/best_practices.md Implement `on_start` for actor initialization and `on_stop` for cleanup logic. `on_start` receives arguments and an actor reference, while `on_stop` receives a weak actor reference and a boolean indicating if the actor was killed. ```rust impl Actor for MonitoredActor { async fn on_start(args: Self::Args, actor_ref: &ActorRef) -> Result { tracing::info!("Actor {} starting", actor_ref.identity()); // ... initialization ... } async fn on_stop(&mut self, actor_weak: &ActorWeak, killed: bool) -> Result<(), Self::Error> { tracing::warn!("Actor {} stopping (killed: {})", actor_weak.identity(), killed); Ok(()) } } ``` -------------------------------- ### Rsactor Actor Example with Handler Traits Source: https://github.com/hiking90/rsactor/blob/main/docs/handler_traits_design.md Demonstrates defining an actor, its message types, and implementing message handlers using the `#[message_handlers]` and `#[handler]` attributes. This example shows how to manage multiple actors and send messages to them collectively. ```rust use rsactor::{ message_handlers, spawn, Actor, ActorRef, AskHandler, TellHandler, WeakTellHandler, ActorControl, }; struct Ping { timestamp: u64 } struct GetStatus; #[derive(Debug, Clone)] struct Status { name: String, message_count: u32, } #[derive(Actor)] struct CounterActor { name: String, count: u32, } #[message_handlers] impl CounterActor { #[handler] async fn handle_ping(&mut self, msg: Ping, _: &ActorRef) -> () { self.count += 1; println!("[{{}}] Ping: {{}}", self.name, msg.timestamp); } #[handler] async fn handle_get_status(&mut self, _: GetStatus, _: &ActorRef) -> Status { Status { name: self.name.clone(), message_count: self.count, } } } #[tokio::main] async fn main() -> anyhow::Result<()> { let (actor_a, _) = spawn::(CounterActor { name: "A".into(), count: 0, }); let (actor_b, _) = spawn::(CounterActor { name: "B".into(), count: 0, }); // Unified management with TellHandler let handlers: Vec>> = vec![ (&actor_a).into(), (&actor_b).into(), ]; for handler in &handlers { handler.tell(Ping { timestamp: 1000 }).await?; } // Collect responses with AskHandler let ask_handlers: Vec>> = vec![ (&actor_a).into(), (&actor_b).into(), ]; for handler in &ask_handlers { let status = handler.ask(GetStatus).await?; println!("{{}}: {{}} messages", status.name, status.message_count); } Ok(()) } ``` -------------------------------- ### rsactor Deadlock Detection Example Source: https://github.com/hiking90/rsactor/blob/main/README.md A conceptual example illustrating how rsactor's deadlock detection works for `ask` cycles. It shows a scenario where Actor A waits for Actor B, and Actor B waits for Actor A, leading to a detected deadlock. ```text Actor A handler: actor_ref_b.ask(msg).await ← waiting for B's reply Actor B handler: actor_ref_a.ask(msg).await ← waiting for A's reply → Deadlock detected! Panic with cycle path: A(#1) -> B(#2) -> A(#1) ``` -------------------------------- ### Periodic Monitoring Source: https://github.com/hiking90/rsactor/blob/main/docs/metrics.md An example of a periodic monitoring task that fetches and prints actor metrics at regular intervals. ```APIDOC ## Periodic Monitoring ```rust use std::time::Duration; async fn monitor_actor(actor_ref: ActorRef) { while actor_ref.is_alive() { tokio::time::sleep(Duration::from_secs(10)).await; let m = actor_ref.metrics(); println!( "[Monitor] messages={} avg={:?} max={:?} errors={} uptime={:?}", m.message_count, m.avg_processing_time, m.max_processing_time, m.error_count, m.uptime ); } } ``` ``` -------------------------------- ### Run with basic logging Source: https://github.com/hiking90/rsactor/blob/main/book/src/advanced/tracing.md Execute your application with the RUST_LOG environment variable set to 'debug' to enable basic logging. This works regardless of the tracing feature. ```bash RUST_LOG=debug cargo run --example basic ``` -------------------------------- ### Actor Lifecycle Methods Source: https://github.com/hiking90/rsactor/blob/main/skills/rsactor-guide/SKILL.md Implementations for `on_start`, `on_run`, and `on_stop` methods to manage actor initialization, background tasks, and cleanup. ```rust impl Actor for MyActor { type Args = MyActorArgs; // Arguments passed to spawn() type Error = anyhow::Error; // Required: Initialize actor state async fn on_start(args: Self::Args, actor_ref: &ActorRef) -> Result; // Optional: Background task (called repeatedly while returning Ok(true)) // Return Ok(true) to keep calling, Ok(false) to stop idle processing async fn on_run(&mut self, actor_ref: &ActorWeak) -> Result { Ok(false) // Default: no idle processing } // Optional: Cleanup on shutdown // killed: true if via kill(), false if via stop() async fn on_stop(&mut self, actor_ref: &ActorWeak, killed: bool) -> Result<(), Self::Error> { Ok(()) // Default: no-op } } ``` -------------------------------- ### Get Actor Metrics Snapshot Source: https://github.com/hiking90/rsactor/blob/main/book/src/examples/metrics.md Retrieves all available metrics for an actor at once. Ensure the 'metrics' feature is enabled. ```rust let (actor_ref, handle) = spawn::(actor); // Send some messages for _ in 0..10 { actor_ref.tell(FastTask).await?; } // Get all metrics at once let metrics = actor_ref.metrics(); println!("Message count: {}", metrics.message_count); println!("Avg processing time: {:?}", metrics.avg_processing_time); println!("Max processing time: {:?}", metrics.max_processing_time); println!("Error count: {}", metrics.error_count); println!("Uptime: {:?}", metrics.uptime); println!("Last activity: {:?}", metrics.last_activity); ``` -------------------------------- ### Rsactor Cargo.toml with Feature Flags Source: https://github.com/hiking90/rsactor/blob/main/skills/rsactor-guide/SKILL.md Example of how to include the rsactor crate in your Cargo.toml, enabling specific features like tracing and metrics. ```toml [dependencies] rsactor = { version = "0.x", features = ["tracing", "metrics"] } ``` -------------------------------- ### Main Execution with Test Cases Source: https://github.com/hiking90/rsactor/blob/main/book/src/error_handling/actor_result.md Sets up tracing and runs `check_actor_termination` with different failure scenarios: normal completion, failure during `on_start`, and failure during `on_run`. This demonstrates the practical application of the `ActorResult` handling. ```rust @tokio::main async fn main() { tracing_subscriber::fmt() .with_max_level(tracing::Level::DEBUG) .init(); tracing::info!("--- Checking normal completion ---"); check_actor_termination(false, false).await; tracing::info!("--- Checking failure in on_start ---"); check_actor_termination(true, false).await; tracing::info!("--- Checking failure in on_run ---"); check_actor_termination(false, true).await; } ``` -------------------------------- ### Actor Communication Patterns Source: https://github.com/hiking90/rsactor/blob/main/skills/rsactor-guide/SKILL.md Examples of fire-and-forget ('tell') and request-response ('ask') communication patterns, including how to set timeouts for 'ask' operations. ```rust // Fire-and-forget (tell) - handler returns () actor_ref.tell(MyMessage).await?; // Request-response (ask) - handler returns a value let result: Response = actor_ref.ask(MyQuery).await?; // With timeout use std::time::Duration; let result = actor_ref.ask_with_timeout(MyQuery, Duration::from_secs(5)).await?; ``` -------------------------------- ### Querying Actor Metrics Source: https://github.com/hiking90/rsactor/blob/main/docs/metrics.md Demonstrates how to spawn an actor, send it messages, and then query its metrics using the `metrics()` method on `ActorRef`. ```APIDOC ## Querying Actor Metrics Access actor metrics through `ActorRef`. ```rust use rsactor::{message_handlers, spawn, Actor, ActorRef, MetricsSnapshot}; use std::time::Duration; #[derive(Actor)] struct MyActor; struct Work; #[message_handlers] impl MyActor { #[handler] async fn handle_work(&mut self, _msg: Work, _ctx: &ActorRef) { // Message processing logic } } #[tokio::main] async fn main() { let (actor_ref, _handle) = spawn::(()); // Send messages for _ in 0..100 { actor_ref.tell(Work).await.unwrap(); } // Wait for processing tokio::time::sleep(Duration::from_millis(100)).await; // Query metrics let metrics = actor_ref.metrics(); println!("Messages processed: {}", metrics.message_count); println!("Avg processing time: {:?}", metrics.avg_processing_time); println!("Max processing time: {:?}", metrics.max_processing_time); println!("Error count: {}", metrics.error_count); println!("Uptime: {:?}", metrics.uptime); println!("Last activity: {:?}", metrics.last_activity); } ``` ``` -------------------------------- ### Define and Run a Basic Counter Actor in Rust Source: https://github.com/hiking90/rsactor/blob/main/book/src/examples/basic.md This example shows how to define a stateful actor, implement the Actor trait, handle messages using macros, spawn the actor, send messages, and manage its lifecycle. It includes periodic tasks using `on_run` and `tokio::select!` for concurrent timers. ```rust use anyhow::Result; use rsactor::{message_handlers, Actor, ActorRef, ActorWeak}; use tokio::time::{interval, Duration}; use tracing::info; // Message types struct Increment; struct Decrement; // Define the actor struct struct MyActor { count: u32, start_up: std::time::Instant, tick_300ms: tokio::time::Interval, tick_1s: tokio::time::Interval, } // Implement the Actor trait for MyActor impl Actor for MyActor { type Args = Self; type Error = anyhow::Error; // Called when the actor is started async fn on_start(args: Self::Args, _actor_ref: &ActorRef) -> Result { info!("MyActor started. Initial count: {}.", args.count); Ok(args) } // Called repeatedly when the message queue is empty (idle handler). // Returns Ok(true) to continue calling on_run, Ok(false) to stop idle processing. // Note: receives &ActorWeak, not &ActorRef. async fn on_run(&mut self, _actor_weak: &ActorWeak) -> Result { // Use tokio::select! to handle multiple async operations tokio::select! { _ = self.tick_300ms.tick() => { println!("300ms tick. Elapsed: {:?}", self.start_up.elapsed()); } _ = self.tick_1s.tick() => { println!("1s tick. Elapsed: {:?}", self.start_up.elapsed()); } } Ok(true) // Continue calling on_run } } // Message handling using the #[message_handlers] macro with #[handler] attributes #[message_handlers] impl MyActor { #[handler] async fn handle_increment(&mut self, _msg: Increment, _: &ActorRef) -> u32 { self.count += 1; println!("MyActor handled Increment. Count is now {}.", self.count); self.count } #[handler] async fn handle_decrement(&mut self, _msg: Decrement, _: &ActorRef) -> u32 { self.count -= 1; println!("MyActor handled Decrement. Count is now {}.", self.count); self.count } } #[tokio::main] async fn main() -> Result<()> { tracing_subscriber::fmt() .with_max_level(tracing::Level::DEBUG) .with_target(false) .init(); // Create actor instance with initial state let my_actor = MyActor { count: 100, start_up: std::time::Instant::now(), tick_300ms: interval(Duration::from_millis(300)), tick_1s: interval(Duration::from_secs(1)), }; // Spawn the actor let (actor_ref, join_handle) = rsactor::spawn::(my_actor); println!("MyActor spawned with ID: {}", actor_ref.identity()); // Wait a bit to see some ticks tokio::time::sleep(Duration::from_millis(700)).await; // Send messages and await replies println!("Sending Increment message..."); let count_after_inc: u32 = actor_ref.ask(Increment).await?; println!("Reply after Increment: {}", count_after_inc); println!("Sending Decrement message..."); let count_after_dec: u32 = actor_ref.ask(Decrement).await?; println!("Reply after Decrement: {}", count_after_dec); // Wait for more ticks tokio::time::sleep(Duration::from_millis(700)).await; // Stop the actor gracefully println!("Stopping actor..."); actor_ref.stop().await?; // Wait for completion and check result let result = join_handle.await?; match result { rsactor::ActorResult::Completed { actor, killed } => { println!("Actor completed. Final count: {}. Killed: {}", actor.count, killed); } rsactor::ActorResult::Failed { actor, error, phase, killed } => { println!("Actor failed: {}. Phase: {:?}, Killed: {}", error, phase, killed); if let Some(actor) = actor { println!("Final count: {}", actor.count); } } } Ok(()) } ``` -------------------------------- ### Calculating Error Rate Source: https://github.com/hiking90/rsactor/blob/main/docs/metrics.md Example demonstrating how to calculate the error rate of an actor based on its total messages processed and error count. ```APIDOC ## Calculating Error Rate ```rust let metrics = actor_ref.metrics(); let error_rate = if metrics.message_count > 0 { (metrics.error_count as f64 / metrics.message_count as f64) * 100.0 } else { 0.0 }; println!("Error rate: {:.2}%", error_rate); ``` ``` -------------------------------- ### Comparing ask() and ask_join() Source: https://github.com/hiking90/rsactor/blob/main/book/src/examples/ask_join.md Illustrates the difference between manually awaiting a JoinHandle returned by `ask()` and the automatic awaiting provided by `ask_join()`. ```rust // ask() returns JoinHandle — you await it manually let join_handle: JoinHandle = worker_ref.ask(task).await?; let result = join_handle.await?; // ask_join() does both steps automatically let result: u64 = worker_ref.ask_join(task).await?; ``` -------------------------------- ### Controlling Log Output with `RUST_LOG` Source: https://github.com/hiking90/rsactor/blob/main/docs/debugging_guide.md Provides examples of how to use the `RUST_LOG` environment variable to filter and control the verbosity of rsactor logs. ```bash # Show all rsactor warnings RUST_LOG=rsactor=warn cargo run # Show dead letter events only RUST_LOG=rsactor::dead_letter=warn cargo run # Full debugging output RUST_LOG=rsactor=debug cargo run ``` -------------------------------- ### Initialize Tracing Subscriber in Rust Source: https://github.com/hiking90/rsactor/blob/main/README.md Provides the recommended pattern for initializing the tracing subscriber in a Rust application using `tokio`. This setup is necessary for enabling structured logging and observability for rsactor. ```rust #[tokio::main] async fn main() -> Result<(), Box> { // Initialize tracing subscriber tracing_subscriber::fmt() .with_max_level(tracing::Level::DEBUG) .with_target(false) .init(); // Your actor code here... Ok(()) } ``` -------------------------------- ### Implement a Simple Actor with Message Handling Source: https://github.com/hiking90/rsactor/blob/main/docs/FAQ.md Demonstrates how to define an actor, implement the Actor trait, define message types, and handle messages using the Message trait and the impl_message_handler macro. ```rust use rsactor::{Actor, ActorRef, Message, impl_message_handler, spawn, ActorResult}; use anyhow::Result; // Define actor struct struct SimpleActor { counter: u32, } // Implement Actor trait impl Actor for SimpleActor { type Args = u32; // Starting counter value type Error = anyhow::Error; async fn on_start(initial_counter: Self::Args, _actor_ref: &ActorRef) -> Result { Ok(SimpleActor { counter: initial_counter }) } } // Define message type struct Increment(u32); // Implement message handler impl Message for SimpleActor { type Reply = u32; // Return new counter value async fn handle(&mut self, msg: Increment, _actor_ref: &ActorRef) -> Self::Reply { self.counter += msg.0; self.counter } } // Use macro to implement MessageHandler trait (deprecated approach) impl_message_handler!(SimpleActor, [Increment]); #[tokio::main] async fn main() -> Result<()> { // Spawn actor with initial counter value of 0 let (actor_ref, _join_handle) = spawn::(0); // Send Increment message and await reply let new_value = actor_ref.ask(Increment(5)).await?; println!("New counter value: {}", new_value); Ok(()) } ``` -------------------------------- ### Convert metrics to Prometheus format Source: https://github.com/hiking90/rsactor/blob/main/docs/metrics.md Example function to convert rsactor's MetricsSnapshot into Prometheus exposition format. This is useful for integrating with Prometheus monitoring. ```rust // Example: Converting to Prometheus format fn to_prometheus_format(actor_name: &str, m: &MetricsSnapshot) -> String { format!( r"# HELP actor_message_count Total messages processed # TYPE actor_message_count counter actor_message_count{{actor="{}"}} {} # HELP actor_processing_time_avg Average processing time in seconds # TYPE actor_processing_time_avg gauge actor_processing_time_avg{{actor="{}"}} {} # HELP actor_processing_time_max Max processing time in seconds # TYPE actor_processing_time_max gauge actor_processing_time_max{{actor="{}"}} {} # HELP actor_error_count Total errors # TYPE actor_error_count counter actor_error_count{{actor="{}"}} {} ", actor_name, m.message_count, actor_name, m.avg_processing_time.as_secs_f64(), actor_name, m.max_processing_time.as_secs_f64(), actor_name, m.error_count ) } ``` -------------------------------- ### Simple Actor Initialization with `#[derive(Actor)]` Source: https://github.com/hiking90/rsactor/blob/main/book/src/macros/unified_macro.md Demonstrates the `#[derive(Actor)]` macro for actors without custom initialization. The struct instance itself is used as `Args` for `spawn`. ```rust #[derive(Actor)] struct SimpleActor { name: String, count: u32, } // spawn takes the struct instance directly as Args let actor = SimpleActor { name: "test".into(), count: 0 }; let (actor_ref, handle) = spawn::(actor); ```