Actor model expert covering message passing, state isolation, supervision trees, deadlock prevention, fault tolerance, Actix framework, and Erlang-style concurrency patterns.
Install with Codex or Claude Copy this prompt, paste it into Codex, Claude, or another assistant, and let it review the skill page and install it for you.
A direct command skips the review prompt. Inspect the source before running it.
Actor model expert covering message passing, state isolation, supervision trees, deadlock prevention, fault tolerance, Actix framework, and Erlang-style concurrency patterns.
Design questions:
→ What state needs isolation? Each isolated state = 1 actor
→ What operations need sequential processing? Group in same actor
→ What can fail independently? Separate actors with supervision
→ What needs to scale? Use actor pool pattern
Step 2: Choose Messaging Pattern
Message patterns:
→ Fire-and-forget: Async send, no response
→ Request-response: Oneshot channel for reply
→ Streaming: Channel for multiple responses
→ Broadcast: Multiple recipients
Mailboxes have bounded capacity (prevent memory leaks)
Message types are Send + 'static
No shared mutable state between actors
Supervision strategy appropriate for error handling
Actor lifecycle properly managed (cleanup in post_stop)
No circular message dependencies (deadlock risk)
Timeouts on request-response patterns
Monitoring tracks mailbox size and message latency
Backpressure handled when mailbox is full
Verification Commands
# Run tests with actor system
cargo test --test actor_tests
# Check for deadlocks with timeout
cargo test --test deadlock_tests -- --test-threads=1 --nocapture
# Profile actor message throughput
cargo bench --bench actor_bench
# Check memory usage under load
cargo run --release --bin load_test
# Monitor actor lifecycle events
RUST_LOG=debug cargo run
Common Pitfalls
1. Circular Message Dependencies (Deadlock)
Symptom: Actors waiting for each other's responses
// ❌ Bad: Actor A waits for Actor B, Actor B waits for Actor Aasyncfnactor_a_handler(&mutself, msg: Message) {
letresponse = self.actor_b.request(msg).await; // Blocks// Actor A is blocked, can't process Actor B's request
}
asyncfnactor_b_handler(&mutself, msg: Message) {
letresponse = self.actor_a.request(msg).await; // Blocks// Deadlock!
}
// ✅ Good: Use timeouts and avoid circular dependenciesasyncfnactor_a_handler(&mutself, msg: Message) {
match tokio::time::timeout(
Duration::from_secs(5),
self.actor_b.request(msg)
).await {
Ok(response) => { /* handle response */ }
Err(_) => { /* timeout, handle error */ }
}
}
// Better: redesign to avoid circular dependency
2. Unbounded Mailbox Growth
Symptom: Memory grows unbounded, OOM crashes
// ❌ Bad: unbounded channellet (tx, rx) = mpsc::unbounded_channel();
// Slow consumer can't keep up, mailbox grows forever// ✅ Good: bounded channel with backpressurelet (tx, rx) = mpsc::channel(100); // Max 100 messages// Sender will wait when mailbox is full (backpressure)
tx.send(msg).await?;
3. Blocking Operations in Actor
Symptom: Actor becomes unresponsive, messages pile up
// ❌ Bad: blocking I/O in actorimplActorforMyActor {
fnreceive(&mutself, ctx: &mut Context<Self>, msg: Self::Message) {
// Blocks entire actor!letdata = std::fs::read("file.txt").unwrap();
// Other messages can't be processed
}
}
// ✅ Good: use async I/O or spawn blocking taskimplActorforMyActor {
fnreceive(&mutself, ctx: &mut Context<Self>, msg: Self::Message) {
letaddr = ctx.address();
tokio::spawn(asyncmove {
// Runs in separate taskletdata = tokio::fs::read("file.txt").await.unwrap();
addr.send(ProcessData(data)).await;
});
// Actor continues processing messages
}
}