Skip to content

Reject a non-positive spawn_rate in new_dispatch() - #3528

Merged
cyberw merged 1 commit into
locustio:masterfrom
Lesereingrape:fix/spawn-rate-validation
Oct 3, 2026
Merged

cyberw merged 1 commit into
locustio:masterfrom
Lesereingrape:fix/spawn-rate-validation

Conversation

@Lesereingrape

Copy link
Copy Markdown
Contributor

What

UsersDispatcher.new_dispatch() already rejects a negative target_user_count, but never looks at spawn_rate. This adds the missing sibling check:

if spawn_rate <= 0:
    raise ValueError(f"spawn_rate must be > 0, got {spawn_rate}")

plus a regression test in locust/test/test_dispatch.py.

Fixes #3527

Why

new_dispatch() feeds the rate straight into the ramp-up math (locust/dispatch.py:206-208), and _wait_between_dispatch_iteration_context() clamps the result with max(0.0, ...) (:291), so two silent/lethal things happen for callers that don't go through the CLI parser (web UI /swarm, programmatic use per docs/use-as-lib.rst, LoadTestShape.tick()):

  • a negative rate produces a negative wait, which the clamp turns into 0 — the entire load is spawned in one instant burst instead of the requested ramp, while /swarm still replies 200 {"success": true, "message": "Swarming started"}. Measured with 5 users: spawn_rate=1 → 4.05 s to reach 5 users, spawn_rate=-1 → 0.00 s.
  • spawn_rate=0 raises ZeroDivisionError: division by zero from dispatch.py:208, killing the shape/start greenlet with an error that never mentions spawn_rate; the measured result is that the test never starts (user_count stays 0, state stays ready).

> 0 is the contract the project already states for this value — --spawn-rate is parsed with gt_zero(float) in argument_parser.py:492, including in the released 2.46.6 — so the guard only moves that existing contract down to the entry points that bypass the parser. It raises before any dispatcher state is mutated, and no internal caller passes a non-positive rate (runners.py:505 / :783 only relay values that already passed the CLI guard, or the master's stored spawn_rate), so CLI-driven and distributed runs are unaffected.

How it was checked

  • New TestSpawnRateValidation covers 0, 0.0, -0.5, -1, -1000: RED on 555ad9e5 (ValueError not raised for all 5 subtests) → GREEN with the change (2 passed, 5 subtests passed).
  • End-to-end on the real /swarm endpoint and on a LoadTestShape returning (5, 0), both before and after the change (before: instant burst / ZeroDivisionError; after: ValueError: spawn_rate must be > 0, got ...), as pasted in spawn_rate is never validated: a negative rate spawns the whole load instantly and 0 raises ZeroDivisionError #3527.
  • locust/test/test_dispatch.py full suite passes; uv run ruff check locust/, ruff format --check and mypy locust/dispatch.py are clean.
  • The wider test_runners.py + test_web.py run has failures on my Windows machine, but they are pre-existing and identical with and without this change — I re-ran both suites against the untouched 555ad9e5 and got the same set (the web ones are 500/PermissionError on temp-file teardown in this environment, i.e. the / page and CSV/TLS artifacts; one TestStopTimeout case is timing-flaky and passes when run alone).

AI assistance disclosure: this change was produced by an AI coding assistant in a scripted session. The
reproductions, the RED/GREEN runs and the ruff / mypy / pytest output quoted above come from that
session, and the numbers were copied from the actual command output rather than estimated.

A negative spawn rate made the wait between dispatch iterations negative, so
ramping-up turned into an instant burst of the whole load, and a zero rate
raised ZeroDivisionError. Both come from the same missing check that
target_user_count already has.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

spawn_rate is never validated: a negative rate spawns the whole load instantly and 0 raises ZeroDivisionError

2 participants