175 lines
4.9 KiB
Rust
175 lines
4.9 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,
|
|
..Default::default()
|
|
};
|
|
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,
|
|
..Default::default()
|
|
};
|
|
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,
|
|
..Default::default()
|
|
};
|
|
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");
|
|
}
|
|
|
|
/// 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,
|
|
..Default::default()
|
|
};
|
|
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");
|
|
}
|