dev-stress 0.9.0

High-load stress testing for Rust. Concurrency, volume, saturation. Part of the dev-* verification suite.
Documentation
<h1 align="center">
    <strong>dev-stress</strong>
    <br>
    <sup><sub>HIGH-LOAD STRESS TESTING FOR RUST</sub></sup>
</h1>

<p align="center">
    <a href="https://crates.io/crates/dev-stress"><img alt="crates.io" src="https://img.shields.io/crates/v/dev-stress.svg"></a>
    <a href="https://crates.io/crates/dev-stress"><img alt="downloads" src="https://img.shields.io/crates/d/dev-stress.svg"></a>
    <a href="https://github.com/jamesgober/dev-stress/actions/workflows/ci.yml"><img alt="CI" src="https://github.com/jamesgober/dev-stress/actions/workflows/ci.yml/badge.svg"></a>
    <a href="https://docs.rs/dev-stress"><img alt="docs.rs" src="https://docs.rs/dev-stress/badge.svg"></a>
</p>

<p align="center">
    Concurrency, volume, saturation under pressure.<br>
    Part of the <code>dev-*</code> verification suite.
</p>

---

## What it does

`dev-stress` measures how a workload behaves when scaled up: thousands
of concurrent operations, millions of iterations, sustained pressure.

It detects:

- Throughput collapse under concurrency
- Lock contention (via thread-time variance)
- Latency cliff-falls
- Sustained-load instability

It does NOT do:

- Single-threaded micro-benchmarking (use [`dev-bench`]https://github.com/jamesgober/dev-bench)
- Async-specific issue detection (use [`dev-async`]https://github.com/jamesgober/dev-async)
- Failure injection (use [`dev-chaos`]https://github.com/jamesgober/dev-chaos)

## Quick start

```toml
[dependencies]
dev-stress = "0.9"
```

```rust
use dev_stress::{Workload, StressRun};

#[derive(Clone)]
struct MyWorkload;
impl Workload for MyWorkload {
    fn run_once(&self) {
        std::hint::black_box(40 + 2);
    }
}

let run = StressRun::new("hot_path")
    .iterations(100_000)
    .threads(8);

let result = run.execute(&MyWorkload);
println!("ops/sec: {}", result.ops_per_sec());
println!("thread CV: {}", result.thread_time_cv());

let _check = result.into_check_result(None);
```

The returned `CheckResult` carries the `stress` tag and numeric
`Evidence` for `iterations`, `threads`, `ops_per_sec`,
`thread_time_cv`, and `total_elapsed_ms` — no detail-string parsing.

## Per-op latency percentiles

Track p50/p95/p99 per operation by enabling latency tracking:

```rust
use dev_stress::{StressRun, Workload};

#[derive(Clone)]
struct MyWorkload;
impl Workload for MyWorkload {
    fn run_once(&self) { std::hint::black_box(40 + 2); }
}

let run = StressRun::new("hot")
    .iterations(100_000)
    .threads(4)
    .track_latency(10);  // 10% sampling

let r = run.execute(&MyWorkload);
if let Some(lat) = &r.latency {
    println!("p99 = {}ns", lat.p99.as_nanos());
}
```

## Configurable thresholds

```rust
use dev_stress::{CompareOptions, StressRun, Workload};
use std::time::Duration;

#[derive(Clone)]
struct MyWorkload;
impl Workload for MyWorkload {
    fn run_once(&self) { std::hint::black_box(40 + 2); }
}

let r = StressRun::new("hot").iterations(10_000).threads(4)
    .track_latency(1)
    .execute(&MyWorkload);

let opts = CompareOptions {
    baseline_ops_per_sec: Some(900_000.0),
    ops_drop_pct_threshold: 10.0,
    baseline_p99: Some(Duration::from_micros(50)),
    p99_regression_pct_threshold: 25.0,
};
let _check = r.compare_with_options(&opts);
```

## Soak tests

Run a workload for sustained duration and detect degradation across
checkpoints:

```rust
use dev_stress::{SoakRun, Workload};
use std::time::Duration;

#[derive(Clone)]
struct MyWorkload;
impl Workload for MyWorkload {
    fn run_once(&self) { std::hint::black_box(40 + 2); }
}

let r = SoakRun::new("steady")
    .duration(Duration::from_secs(30))
    .checkpoint(Duration::from_secs(5))
    .threads(4)
    .track_latency(100)
    .execute(&MyWorkload);

// Fail if second-half mean ops/sec drops more than 15% below first half.
let _check = r.into_check_result(15.0);
```

## Producer trait

```rust
use dev_stress::{CompareOptions, StressProducer, StressRun, Workload};
use dev_report::Producer;

#[derive(Clone)]
struct MyWorkload;
impl Workload for MyWorkload {
    fn run_once(&self) { std::hint::black_box(40 + 2); }
}

let producer = StressProducer::new(
    || StressRun::new("hot").iterations(10_000).threads(4).execute(&MyWorkload),
    "0.1.0",
    CompareOptions::default(),
);
let report = producer.produce(); // dev_report::Report
```

## System stats (opt-in)

```toml
[dependencies]
dev-stress = { version = "0.9", features = ["system-stats"] }
```

```rust,ignore
use dev_stress::system::{SystemSampler, SystemStats};

let mut sampler = SystemSampler::new();
let before = sampler.sample().unwrap();
// ... run workload ...
let after = sampler.sample().unwrap();
let _check = SystemStats::compare("hot", before, after, Some(500_000_000));
```

## Thread-time CV

The coefficient of variation across thread elapsed times is the
clearest signal that a workload is contention-bound:

- CV near 0: threads finished at nearly the same time. Healthy.
- CV > 0.2: significant variance. Some threads were waiting on locks
  or contended resources.
- CV > 0.5: severe contention. Investigate.

## Status

`v0.9.x` is the pre-1.0 stabilization line. APIs are expected to be
near-final; minor adjustments may still happen ahead of `1.0`. The
statistic definitions (`ops_per_sec`, `thread_time_cv`) are pinned
and will not change.

## Minimum supported Rust version

`1.85` — pinned in `Cargo.toml` via `rust-version` and verified by
the MSRV job in CI. (Bumped from 1.75 to align with the suite's
shared MSRV after sibling crates picked up dependencies that require
`edition2024`.)

## License

Apache-2.0. See [LICENSE](LICENSE).