diff --git a/loadtest/multiprocess.py b/loadtest/multiprocess.py index 1f72acb..565a326 100644 --- a/loadtest/multiprocess.py +++ b/loadtest/multiprocess.py @@ -12,6 +12,7 @@ import asyncio import random from concurrent.futures import ProcessPoolExecutor +from time import perf_counter from uuid import UUID from loadtest.harness import StrategyReport, violation_magnitude @@ -75,6 +76,13 @@ async def multiprocess_sweep( slices = [ids[p::processes] for p in range(processes)] loop = asyncio.get_running_loop() with ProcessPoolExecutor(max_workers=processes) as pool: + # Parent-side wall spans the whole parallel run — from dispatch until + # the last slice returns — so it is the correct denominator for an + # aggregate throughput across overlapping workers (summing per-slice + # walls would double-count concurrent time). The comparative table's + # published numbers still come from the single-process run; this is + # the multi-process path's own end-to-end throughput. + start = perf_counter() futures = [ loop.run_in_executor( pool, driven_slice, database_url, sl, f"mp-{p}", concurrency, seed + p @@ -82,10 +90,9 @@ async def multiprocess_sweep( for p, sl in enumerate(slices) ] results = await asyncio.gather(*futures) + wall = perf_counter() - start samples = [s for slice_samples in results for s in slice_samples] - # Wall here is approximate (post-hoc); the comparative table's throughput - # comes from the single-process run. summarize over the union for tallies. - metrics = summarize("guarded-mp", samples, wall_seconds=0.0) + metrics = summarize("guarded-mp", samples, wall_seconds=wall) oracle = await run_oracle(postgres_read_uow_factory(engine)) finally: await engine.dispose() diff --git a/tests/integration/test_load_multiprocess.py b/tests/integration/test_load_multiprocess.py index cd91965..dc46e3c 100644 --- a/tests/integration/test_load_multiprocess.py +++ b/tests/integration/test_load_multiprocess.py @@ -20,3 +20,6 @@ async def test_multiprocess_guarded_stays_clean(postgres_url: str) -> None: assert report.oversell == 0 assert report.metrics.count == 48 assert report.metrics.errors == 0 + # The multi-process path measures its own end-to-end wall, so it reports a + # real aggregate throughput rather than a degenerate 0.0 (issue #96). + assert report.metrics.throughput > 0.0