From f6f7dbc60a45e91851873cf993eaf02eb4a55f02 Mon Sep 17 00:00:00 2001 From: lcian <17258265+lcian@users.noreply.github.com> Date: Thu, 12 Mar 2026 12:18:44 +0100 Subject: [PATCH] fix(stresstest): Report actual elapsed time instead of configured duration When a backlog of requests builds up, tasks may finish after the configured duration expires. Using the hardcoded duration for metrics reporting produced incorrect ops/s and throughput numbers. This tracks the real wall-clock time from start until all tasks complete and uses that for reporting. Also moves the semaphore acquire before reading metrics to ensure all in-flight tasks are reflected. --- stresstest/src/stresstest.rs | 21 +++++++++++++-------- 1 file changed, 13 insertions(+), 8 deletions(-) diff --git a/stresstest/src/stresstest.rs b/stresstest/src/stresstest.rs index 3a0e7ce2..97c8b6b4 100644 --- a/stresstest/src/stresstest.rs +++ b/stresstest/src/stresstest.rs @@ -126,8 +126,10 @@ impl Stresstest { bar.finish_and_clear(); let mut total_metrics = WorkloadMetrics::default(); + let mut max_elapsed = Duration::ZERO; let workloads = finished_tasks.into_iter().map(|task| { - let (workload, metrics) = task.unwrap(); + let (workload, metrics, elapsed) = task.unwrap(); + max_elapsed = max_elapsed.max(elapsed); println!(); println!( @@ -137,7 +139,7 @@ impl Stresstest { workload.mode, workload.concurrency.bold() ); - print_metrics(&metrics, duration); + print_metrics(&metrics, elapsed); total_metrics.file_sizes.merge(&metrics.file_sizes).unwrap(); total_metrics.bytes_written += metrics.bytes_written; @@ -163,7 +165,7 @@ impl Stresstest { let workloads: Vec<_> = workloads.collect(); println!(); println!("{}", "## TOTALS".bold()); - print_metrics(&total_metrics, duration); + print_metrics(&total_metrics, max_elapsed); println!(); if !cleanup { @@ -230,7 +232,7 @@ async fn run_workload( workload: Workload, duration: Duration, ttl: Option, -) -> (Workload, WorkloadMetrics) { +) -> (Workload, WorkloadMetrics, Duration) { // In throughput mode, allow for a high concurrency value. let concurrency = match workload.mode { WorkloadMode::Weighted => workload.concurrency, @@ -238,6 +240,7 @@ async fn run_workload( }; let semaphore = Arc::new(Semaphore::new(concurrency)); + let start = Instant::now(); let deadline = tokio::time::Instant::now() + duration; let workload = Arc::new(Mutex::new(workload)); @@ -330,21 +333,23 @@ async fn run_workload( } } + // by acquiring *all* the semaphores, we essentially wait for all outstanding tasks to finish + let _permits = semaphore.acquire_many(concurrency as u32).await; + let metrics: WorkloadMetrics = { let mut metrics = metrics.lock().unwrap(); std::mem::take(&mut metrics) }; - // by acquiring *all* the semaphores, we essentially wait for all outstanding tasks to finish - let _permits = semaphore.acquire_many(concurrency as u32).await; - let workload = Arc::try_unwrap(workload) .map_err(|_| ()) .unwrap() .into_inner() .unwrap(); - (workload, metrics) + let elapsed = start.elapsed(); + + (workload, metrics, elapsed) } fn print_error(message: &str, error: &Error) {