InMemory — Stress
Use this to establish your framework performance baseline before adding a real broker. Everything runs in-process, so results here isolate shove's scheduling and dispatch overhead from network and broker costs — the ceiling against which all durable backends are measured.
Prerequisites
No external services required. The in-memory backend runs entirely in-process.
- Cargo feature:
inmemory
Run
cargo run --example inmemory_stress --features inmemoryYou can narrow the run with CLI flags:
# Single tier, single handler profile
cargo run --example inmemory_stress --features inmemory -- --tier moderate --handler fast
# JSON output
cargo run --example inmemory_stress --features inmemory -- --output jsonRun in release mode for representative numbers:
cargo run -q --release --example inmemory_stress --features inmemoryExpected output
Output is non-deterministic and machine-dependent. Look for the scenario progress lines on stderr and the final table on stdout:
shove stress benchmarks — inmemory
scenarios: 60
[1/60] moderate | 50000msg | 1c | zero (no-op) ...
-> 285000.4 msg/s | dispatch p50=0.0ms p99=0.1ms | e2e p50=0.0ms p99=0.1ms | cpu=98% rss=12.3MB | 0.2s
[2/60] moderate | 50000msg | 4c | zero (no-op) ...
-> 512000.1 msg/s | ...
...
Backend: inmemory
TIER MSGS C HANDLER MSG/SEC disp p50 disp p95 disp p99 e2e p50 e2e p95 e2e p99 SCALE RSS(MB) CPU%
...Characteristic markers to look for:
zero (no-op)handler throughput in the hundreds of thousands of msg/s — this is the raw scheduling cost.scaling_efficiencycolumn approaching1.0xas consumer count grows for CPU-bound profiles.peak_rss_mbstaying well under 100 MB even at extreme tiers.
Source
//! Stress benchmarks for the in-memory backend.
//!
//! cargo run -q --release --example inmemory_stress --features inmemory
//! cargo run -q --release --example inmemory_stress --features inmemory -- --tier moderate
//! cargo run -q --release --example inmemory_stress --features inmemory -- --handler fast
//!
//! No containers, no external deps — useful as a ceiling for framework
//! overhead under different handler profiles.
#[path = "../common/stress_test.rs"]
mod harness;
use shove::inmemory::{InMemoryConfig, InMemoryConsumerGroupConfig};
use shove::{Broker, InMemory};
use harness::{HarnessConfig, run_all_scenarios};
#[tokio::main]
async fn main() {
let hcfg = HarnessConfig::<InMemory>::new("inmemory");
run_all_scenarios(
hcfg,
|| async {
Broker::<InMemory>::new(InMemoryConfig::default())
.await
.expect("connect InMemory")
},
|consumers, prefetch, _concurrent| {
InMemoryConsumerGroupConfig::new(consumers..=consumers).with_prefetch_count(prefetch)
},
)
.await;
}Walkthrough
Thin backend wrapper
inmemory/stress.rs is intentionally minimal — it constructs a HarnessConfig::<InMemory> with the backend name "inmemory", then calls run_all_scenarios from the shared harness in examples/common/stress_test.rs. The make_cfg closure maps (consumers, prefetch, _concurrent) directly to:
InMemoryConsumerGroupConfig::new(consumers..=consumers)
.with_prefetch_count(prefetch)Because in-memory has no concurrent-mode distinction, the _concurrent parameter is intentionally ignored.
Harness configuration
HarnessConfig::<InMemory>::new("inmemory") leaves all defaults: prefetch_cap of 100, publish_chunk_size of 1000, and a no-op purge function. The in-memory backend does not need purging between scenarios because a fresh Broker\<InMemory\> is created for each scenario — previous messages cannot bleed across.
Scenario matrix
The harness defines three tiers (moderate, high, extreme) and four handler profiles (zero, fast, slow, heavy). The moderate tier uses consumer counts [1, 4, 8, 16, 32]; the extreme tier goes up to 256 consumers. Handler latencies range from 0 ms (zero) to 1–5 s (heavy). Each combination is one scenario, run sequentially — a full --tier all --handler all sweep is 60 scenarios on the moderate tier alone.
Metrics collected
For each scenario the harness records per-message dispatch latency (published_at_ns to handler entry) and end-to-end latency (published_at_ns to handler completion), then reports p50/p95/p99 percentiles. It also samples peak RSS and CPU% via platform-specific APIs (mach_task_basic_info on macOS, /proc/self/statm on Linux). The scaling_efficiency column divides each throughput by the baseline (lowest consumer count for the same tier/handler combination) — a value of 4.0x with 4 consumers indicates linear scaling.
What to try next
- Run with
--tier extreme --handler zeroto stress-test the scheduler at 1 M messages and 256 consumers. - Add
--concurrentto enable pipeline processing within each consumer and compare throughput against the default sequential mode. - Pipe output through
--output jsonand import into a spreadsheet to plot scaling curves. - Compare numbers against the RabbitMQ or Kafka stress examples to quantify the network + broker overhead relative to this in-process baseline.