swactor/tests/stress/saturation.rs
Zachery Aaron Shores-Chmielewski 7c7947c823 feat: actor addresses, Ctx abstraction, pools, and more
Fleshing out the architecture before abstracting into components amenable to api-based test harnesses for fuzz and optimization working loops.
2026-02-05 22:54:03 +07:00

172 lines
4.8 KiB
Rust

//! Saturation stress tests - find where the runtime breaks.
//!
//! These tests intentionally push past limits to document failure modes.
use super::{BlackHole, Counter, Msg, Stress, StressResult};
use std::time::Duration;
use swactor::runtime::{Runtime, RuntimeConfig};
/// Blast the transfer queue (replaces router_inbox_overflow)
#[test]
#[cfg(feature = "stress")]
fn transfer_queue_overflow() {
println!("\n>>> STRESS: Transfer Queue Overflow");
let config = RuntimeConfig {
max_actors: 10,
actor_max_messages: 100, // Tiny buffer
num_threads: 1,
};
let runtime = Runtime::new(config);
let sink = runtime.spawn(BlackHole).unwrap();
// Process spawn
runtime.tick();
// Blast messages without processing
let mut result = StressResult::new("transfer_queue_overflow");
let start = std::time::Instant::now();
for _ in 0..10_000 {
result.operations += 1;
if runtime.send_to::<Msg>(sink, Msg).is_ok() {
result.successes += 1;
} else {
result.failures += 1;
}
}
result.duration = start.elapsed();
result.note(format!("Transfer buffer: 100, Messages sent: 10,000"));
// With hybrid channel, no failures expected
assert_eq!(result.failures, 0, "Hybrid channel should not reject");
result.print();
println!(">>> PASS: Hybrid channel prevented transfer queue overflow\n");
}
/// Blast a single actor's mailbox via transfer queue
#[test]
#[cfg(feature = "stress")]
fn actor_inbox_overflow() {
println!("\n>>> STRESS: Actor Inbox Overflow");
let config = RuntimeConfig {
max_actors: 10,
actor_max_messages: 100_000, // Large transfer buffer
num_threads: 1,
};
let runtime = Runtime::new(config);
let sink = runtime.spawn(Counter::new()).unwrap();
// Process spawn
runtime.tick();
// Now blast messages
let mut sent = 0u64;
let mut failed = 0u64;
for _ in 0..10_000 {
if runtime.send_to::<Msg>(sink, Msg).is_ok() {
sent += 1;
} else {
failed += 1;
}
// Tick occasionally to let worker deliver
if sent % 100 == 0 {
runtime.tick();
}
}
// Process all remaining messages
for _ in 0..5000 {
runtime.tick();
}
println!(" Sent: {}", sent);
println!(" Failed: {}", failed);
assert_eq!(failed, 0, "Transfer queue rejected message under load");
println!(">>> PASS: No message loss with hybrid channel\n");
}
/// Blast the runtime with actor spawns
#[test]
#[cfg(feature = "stress")]
fn actor_queue_overflow() {
println!("\n>>> STRESS: Actor Queue Overflow");
let config = RuntimeConfig {
max_actors: 100, // Small actor queue
actor_max_messages: 100,
num_threads: 1,
};
let runtime = Runtime::new(config);
let mut result = StressResult::new("actor_queue_overflow");
let start = std::time::Instant::now();
// Try to spawn 500 actors into 100-slot queue
for _ in 0..500 {
result.operations += 1;
match runtime.spawn(BlackHole) {
Ok(_) => result.successes += 1,
Err(_) => result.failures += 1,
}
}
result.duration = start.elapsed();
result.note(format!("Queue capacity: 100, Spawn attempts: 500"));
result.print();
// Note: With hybrid channel (overflow to SegQueue), we expect no failures
assert_eq!(
result.failures, 0,
"Spawned more actors than queue capacity"
);
println!(">>> PASS: Actor queue correctly handles overflow\n");
}
/// FIXME: IS this actually testing what it should be?
/// Sustained overload - run at 2x capacity for extended period.
/// Documents: Does the system degrade gracefully or crash?
#[test]
#[cfg(feature = "stress")]
fn sustained_overload() {
println!("\n>>> STRESS: Sustained Overload");
let config = RuntimeConfig {
max_actors: 100,
actor_max_messages: 1000,
num_threads: 1,
};
let runtime = Runtime::new(config);
// Spawn some actors
let mut actors = Vec::new();
for _ in 0..50 {
if let Ok(addr) = runtime.spawn(Counter::new()) {
actors.push(addr);
}
}
// Process spawn registrations
for _ in 0..200 {
runtime.tick();
}
let result = Stress::new("sustained_overload")
.for_duration(Duration::from_secs(2))
.run(|| {
// Send to random actor
let idx = (std::time::Instant::now().elapsed().as_nanos() as usize) % actors.len();
let success = runtime.send_to::<Msg>(actors[idx], Msg).is_ok();
// Process some (but not all) - simulating overload
runtime.tick();
success
});
result.print();
println!(">>> System survived sustained overload without panic\n");
}