rlt Load Testing Library

repository·main·Indexed 42 days ago

https://github.com/wfxr/rlt

A high-performance universal load testing framework for Rust (version 0.5.0) supporting HTTP, gRPC, and database testing. It features real-time TUI monitoring, stateless and stateful benchmark implementations via the StatelessBenchSuite and BenchSuite traits, and built-in baseline comparison for regression tracking in CI/CD environments.

Tokens
12K
Snippets
43
Records
51
Agent score
52%

What's inside rlt

  1. Reference: Comparison verdict logic

    main

    The verdict of a comparison is determined by the metrics selected for calculation (either the defaults or those specified via --regression-metrics).

    ConditionVerdict
    At least one improved, none regressedimproved
    At least one regressed, none improvedregressed
    All unchangedunchanged
    Some improved, some regressedmixed

    Note: When using --fail-on-regression, a mixed verdict is treated as a regression.

  2. Manage and Compare Performance Baselines

    main

    rlt allows you to track performance changes over time by saving and comparing results.

    Saving a Baseline

    Save current results with a name:

    mybench --url http://localhost:8080 -c 10 -d 30s --save-baseline v1.0

    Baselines are stored in target/rlt/baselines/ by default. You can change this using --baseline-dir or the RLT_BASELINE_DIR environment variable.

    Comparing Against a Baseline

    Compare a new run against a saved baseline. The output shows color-coded deltas:

    • Green: Performance improved
    • Red: Performance regressed
    • Yellow: Within noise threshold (unchanged)
    mybench --url http://localhost:8080 -c 10 -d 30s --baseline v1.0

    CI/CD Integration

    To automatically fail a pipeline if performance regresses, use --fail-on-regression. You can also specify which metrics to monitor using --regression-metrics (e.g., latency-p99, success-ratio).

    # Fail on regression
    mybench --baseline main --fail-on-regression
    
    # Fail on specific metrics
    mybench --baseline main --fail-on-regression --regression-metrics latency-p99,success-ratio

    Compare and Save

    You can compare against an old version and save the new result as a new baseline in one command:

    mybench --baseline v1 --save-baseline v2
  3. Implement a Stateless Benchmark

    main

    For simple benchmarks that do not require per-worker state (e.g., simple math or CPU tasks), implement the StatelessBenchSuite trait. Use rlt::cli::run to execute the benchmark with CLI arguments.

    use async_trait::async_trait;
    use clap::Parser;
    use rlt::{cli::BenchCli, BenchResult, IterInfo, IterReport, StatelessBenchSuite, Status};
    use tokio::time::Instant;
    
    #[derive(Clone)]
    struct SimpleBench;
    
    #[async_trait]
    impl StatelessBenchSuite for SimpleBench {
        async fn bench(&mut self, _: &IterInfo) -> BenchResult<IterReport> {
            let t = Instant::now();
            // Your benchmark logic here
            Ok(IterReport { duration: t.elapsed(), status: Status::success(0), bytes: 0, items: 1 })
        }
    }
    
    #[tokio::main]
    async fn main() -> rlt::Result<()> {
        rlt::cli::run(BenchCli::parse(), SimpleBench).await
    }
  4. Configure regression detection and CI/CD behavior

    main

    You can use rlt in CI/CD pipelines to automatically detect and fail on performance regressions.

    Fail on Regression

    Use the --fail-on-regression flag. When this flag is set, the CLI will exit with code 1 if a regression is detected. If the verdict is improved, unchanged, or mixed (where mixed is treated as a regression), the exit code will be 1.

    Customizing Regression Metrics

    By default, rlt uses a specific set of metrics to calculate the performance verdict. You can override this using --regression-metrics <METRICS>, where <METRICS> is a comma-separated list of metric names.

    Supported metrics include:

    • Throughput: iters-rate, items-rate, bytes-rate
    • Latency: latency-mean, latency-median, latency-p90, latency-p99, latency-max
    • Reliability: success-ratio

    Noise Threshold

    Use --noise-threshold <PERCENT> to define the threshold below which changes are considered "unchanged". The default is 1.0 (1.0%).

    # CI mode: fail on regression
    mybench --url http://localhost:8080 --baseline main --fail-on-regression
    
    # Custom regression metrics
    mybench --url http://localhost:8080 --baseline main \
        --regression-metrics iters-rate,latency-mean,latency-p99
  5. Manage and compare benchmark baselines via CLI

    main

    rlt allows you to save benchmark results as named baselines and compare future runs against them to detect performance regressions or improvements.

    Baseline Management Commands

    • Save a baseline: Use --save-baseline <NAME> to store the current run. Note that this overwrites existing baselines of the same name by default.
    • Compare against a baseline: Use --baseline <NAME> to compare the current run against a previously saved named baseline.
    • Compare against a file: Use --baseline-file <PATH> to compare against a specific JSON baseline file (this is mutually exclusive with --baseline).

    Baseline Directory Resolution

    If you do not specify a directory using --baseline-dir <PATH>, rlt searches for baselines in this order:

    1. The directory provided via --baseline-dir.
    2. The RLT_BASELINE_DIR environment variable.
    3. ${CARGO_TARGET_DIR}/rlt/baselines.
    4. target/rlt/baselines (default).

    Baseline Name Rules

    Names must match the pattern [a-zA-Z0-9_.-]+.

    # Save current run as baseline "v1.0"
    mybench --url http://localhost:8080 -c 10 -d 30s --save-baseline v1.0
    
    # Compare against baseline "v1.0"
    mybench --url http://localhost:8080 -c 10 -d 30s --baseline v1.0
    
    # Compare against main and save as feature-branch
    mybench --url http://localhost:8080 -c 10 -d 30s --baseline main --save-baseline feature-branch
  6. Implement a Stateful Benchmark

    main

    For benchmarks requiring per-worker state (such as HTTP clients or database connections), implement the BenchSuite trait.

    1. Use the bench_cli! macro to define your CLI options (which uses clap).
    2. Implement BenchSuite, defining your WorkerState type.
    3. Implement setup to initialize the WorkerState for each worker.
    4. Implement bench to perform the actual workload using the provided WorkerState.
    5. Use the bench_cli_run! macro in your main function to run the test.
    bench_cli!(HttpBench, {
        /// Target URL.
        #[clap(long)]
        pub url: Url,
    });
    
    #[async_trait]
    impl BenchSuite for HttpBench {
        type WorkerState = Client;
    
        async fn setup(&mut self, _worker_id: u32) -> BenchResult<Self::WorkerState> {
            Ok(Client::new())
        }
    
        async fn bench(&mut self, client: &mut Self::WorkerState, _: &IterInfo) -> BenchResult<IterReport> {
            let t = Instant::now();
            let resp = client.get(self.url.clone()).send().await?;
            let status = resp.status().into();
            let bytes = resp.bytes().await?.len() as u64;
            let duration = t.elapsed();
            Ok(IterReport { duration, status, bytes, items: 1 })
        }
    }
    
    #[tokio::main]
    async fn main() -> rlt::Result<()> {
        bench_cli_run!(HttpBench).await
    }
  7. Manage and compare benchmark baselines

    main

    The baseline module allows you to save benchmark results as named baselines and compare future benchmark runs against them to detect performance regressions.

    Key operations include:

    • Saving: Use save to persist current results as a new baseline.
    • Loading: Use load or load_file to retrieve existing baselines for comparison.
    • Comparing: Use compare to generate a Comparison object containing Delta and Verdict information, which indicates if performance has regressed based on RegressionMetric settings.
    use rlt::baseline::{save, load, compare};
    
    // Example conceptual flow:
    // 1. Save a baseline
    // save(baseline_name, report, dir)?
    
    // 2. Load and compare
    // let baseline = load(baseline_name, dir)?;
    // let comparison = compare(&baseline, &current_report, metrics)?;
  8. Configure rlt Cargo Features

    main

    By default, rlt enables tracing, rate_limit, and http. To use a minimal footprint, disable default features:

    [dependencies]
    rlt = { version = "0.3", default-features = false }
  9. Understand delta status and noise thresholds

    main

    Each individual metric comparison results in a Delta containing a DeltaStatus. The status is determined by comparing the current value to the baseline, adjusted by the noise_threshold:

    • improved: The performance moved in the desired direction (higher for throughput/success, lower for latency) beyond the noise threshold.
    • regressed: The performance moved in the undesired direction beyond the noise threshold.
    • unchanged: The change was within the noise_threshold percentage.

    Note: For throughput and success ratio, higher is better. For latency, lower is better.

  10. Understand comparison verdicts

    main

    The Verdict represents the overall result of a comparison based on the selected regression_metrics:

    • improved: All selected metrics improved or were unchanged, and at least one improved.
    • regressed: All selected metrics regressed or were unchanged, and at least one regressed.
    • unchanged: All selected metrics were within the noise threshold (unchanged).
    • mixed: Some selected metrics improved while others regressed. This is often treated as a regression in CI pipelines (e.g., when using --fail-on-regression).
  11. Embed BenchCli into a custom CLI

    main

    If you are building your own CLI tool and want to include the standard benchmarking options provided by rlt, you can embed the BenchCli struct into your own clap::Parser implementation using the #[command(flatten)] attribute.

    use clap::Parser;
    use rlt::cli::BenchCli;
    
    #[derive(Parser, Clone)]
    pub struct Opts {
        /// Target URL.
        pub url: String,
    
        /// Embed BenchOpts into this Opts.
        #[command(flatten)]
        pub bench_opts: BenchCli,
    }