Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Remove step load feature #1584

Merged
merged 3 commits into from
Oct 12, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion docs/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,6 @@ Other functionalities
:maxdepth: 2

generating-custom-load-shape
running-locust-in-step-load-mode
retrieving-stats
testing-other-systems
extending-locust
Expand Down
28 changes: 11 additions & 17 deletions docs/running-locust-distributed.rst
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,12 @@
Running Locust distributed
===========================

Once a single machine isn't enough to simulate the number of users that you need, Locust supports
running load tests distributed across multiple machines.
Once a single machine isn't enough to simulate the number of users that you need, Locust supports
running load tests distributed across multiple machines.

To do this, you start one instance of Locust in master mode using the ``--master`` flag. This is
the instance that will be running Locust's web interface where you start the test and see live
statistics. The master node doesn't simulate any users itself. Instead you have to start one or
To do this, you start one instance of Locust in master mode using the ``--master`` flag. This is
the instance that will be running Locust's web interface where you start the test and see live
statistics. The master node doesn't simulate any users itself. Instead you have to start one or
-most likely-multiple worker Locust nodes using the ``--worker`` flag, together with the
``--master-host`` (to specify the IP/hostname of the master node).

Expand All @@ -18,13 +18,13 @@ processor core** on the worker machines.

.. note::
Both the master and each worker machine, must have a copy of the locust test scripts
when running Locust distributed.
when running Locust distributed.

.. note::
It's recommended that you start a number of simulated users that are greater than
It's recommended that you start a number of simulated users that are greater than
``number of user classes * number of workers`` when running Locust distributed.
Otherwise - due to the current implementation -

Otherwise - due to the current implementation -
you might end up with a distribution of the User classes that doesn't correspond to the
User classes' ``weight`` attribute. And if the spawn rate is lower than the number of worker
nodes, the spawning would occur in "bursts" where all worker nodes would spawn a single user and
Expand Down Expand Up @@ -72,7 +72,7 @@ Optionally used together with ``--worker`` to set the port number of the master
``--master-bind-host=X.X.X.X``
------------------------------

Optionally used together with ``--master``. Determines what network interface that the master node
Optionally used together with ``--master``. Determines what network interface that the master node
will bind to. Defaults to * (all available interfaces).

``--master-bind-port=5557``
Expand Down Expand Up @@ -106,14 +106,8 @@ Generating a custom load shape using a `LoadTestShape` class
See :ref:`generating-custom-load-shape`


Running Locust distributed in Step Load mode
=============================================

See :ref:`running-locust-in-step-load-mode`


Increase Locust's performance
=============================

If you're planning to run large-scale load tests you might be interested to use the alternative
If you're planning to run large-scale load tests you might be interested to use the alternative
HTTP client that's shipped with Locust. You can read more about it here: :ref:`increase-performance`
52 changes: 0 additions & 52 deletions docs/running-locust-in-step-load-mode.rst

This file was deleted.

20 changes: 3 additions & 17 deletions locust/argument_parser.py
Original file line number Diff line number Diff line change
Expand Up @@ -367,24 +367,10 @@ def setup_parser_arguments(parser):
)

step_load_group = parser.add_argument_group("Step load options")
step_load_group.add_argument(
"--step-load",
action="store_true",
help="Enable Step Load mode to monitor how performance metrics varies when user load increases. Requires --step-users and --step-time to be specified.",
env_var="LOCUST_STEP_LOAD",
)
step_load_group.add_argument(
"--step-users",
type=int,
help="User count to increase by step in Step Load mode. Only used together with --step-load",
env_var="LOCUST_STEP_USERS",
)
step_load_group.add_argument("--step-load", action="store_true", help=configargparse.SUPPRESS)
step_load_group.add_argument("--step-users", type=int, help=configargparse.SUPPRESS)
step_load_group.add_argument("--step-clients", action="store_true", help=configargparse.SUPPRESS)
step_load_group.add_argument(
"--step-time",
help="Step duration in Step Load mode, e.g. (300s, 20m, 3h, 1h30m, etc.). Only used together with --step-load",
env_var="LOCUST_STEP_TIME",
)
step_load_group.add_argument("--step-time", help=configargparse.SUPPRESS)

other_group = parser.add_argument_group("Other options")
other_group.add_argument(
Expand Down
5 changes: 0 additions & 5 deletions locust/env.py
Original file line number Diff line number Diff line change
Expand Up @@ -43,9 +43,6 @@ class Environment:
reset_stats = False
"""Determines if stats should be reset once all simulated users have been spawned"""

step_load = False
"""Determines if we're running in step load mode"""

stop_timeout = None
"""
If set, the runner will try to stop the running users gracefully and wait this many seconds
Expand Down Expand Up @@ -76,7 +73,6 @@ def __init__(
events=None,
host=None,
reset_stats=False,
step_load=False,
stop_timeout=None,
catch_exceptions=True,
parsed_options=None,
Expand All @@ -93,7 +89,6 @@ def __init__(
self.stats = RequestStats()
self.host = host
self.reset_stats = reset_stats
self.step_load = step_load
self.stop_timeout = stop_timeout
self.catch_exceptions = catch_exceptions
self.parsed_options = parsed_options
Expand Down
28 changes: 8 additions & 20 deletions locust/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,6 @@ def create_environment(user_classes, options, events=None, shape_class=None):
events=events,
host=options.host,
reset_stats=options.reset_stats,
step_load=options.step_load,
stop_timeout=options.stop_timeout,
parsed_options=options,
)
Expand All @@ -130,6 +129,12 @@ def main():
sys.stderr.write("The --slave/--expect-slaves parameters have been renamed --worker/--expect-workers\n")
sys.exit(1)

if options.step_time or options.step_load or options.step_users or options.step_clients:
sys.stderr.write(
"The step load feature has been deprecated in favour of using a LoadTestShape class. See https://docs.locust.io/en/stable/generating-custom-load-shape.html\n"
)
sys.exit(1)

if options.hatch_rate:
sys.stderr.write("[DEPRECATED] The --hatch-rate parameter has been renamed --spawn-rate\n")
options.spawn_rate = options.hatch_rate
Expand Down Expand Up @@ -192,9 +197,9 @@ def main():
# create locust Environment
environment = create_environment(user_classes, options, events=locust.events, shape_class=shape_class)

if shape_class and (options.num_users or options.spawn_rate or options.step_load):
if shape_class and (options.num_users or options.spawn_rate):
logger.warning(
"The specified locustfile contains a shape class but a conflicting argument was specified: users, spawn-rate or step-load. Ignoring arguments"
"The specified locustfile contains a shape class but a conflicting argument was specified: users or spawn-rate. Ignoring arguments"
)

if options.show_task_ratio:
Expand All @@ -215,19 +220,6 @@ def main():
print(dumps(task_data))
sys.exit(0)

if options.step_time:
if not options.step_load:
logger.error("The --step-time argument can only be used together with --step-load")
sys.exit(1)
if options.worker:
logger.error("--step-time should be specified on the master node, and not on worker nodes")
sys.exit(1)
try:
options.step_time = parse_timespan(options.step_time)
except ValueError:
logger.error("Valid --step-time formats are: 20, 20s, 3m, 2h, 1h20m, 3h30m10s, etc.")
sys.exit(1)

if options.master:
runner = environment.create_master_runner(
master_bind_host=options.master_bind_host,
Expand Down Expand Up @@ -331,12 +323,8 @@ def timelimit_stop():
options.num_users = 1
if options.spawn_rate is None:
options.spawn_rate = 1
if options.step_users is None:
options.step_users = 1

# start the test
if options.step_time:
runner.start_stepload(options.num_users, options.spawn_rate, options.step_users, options.step_time)
if environment.shape_class:
environment.runner.start_shape()
else:
Expand Down
32 changes: 0 additions & 32 deletions locust/runners.py
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,6 @@ def __init__(self, environment):
self.greenlet = Group()
self.state = STATE_INIT
self.spawning_greenlet = None
self.stepload_greenlet = None
self.shape_greenlet = None
self.shape_last_state = None
self.current_cpu_usage = 0
Expand Down Expand Up @@ -312,37 +311,6 @@ def start(self, user_count, spawn_rate, wait=False):
self.spawn_rate = spawn_rate
self.spawn_users(user_count, spawn_rate=spawn_rate, wait=wait)

def start_stepload(self, user_count, spawn_rate, step_user_count, step_duration):
if user_count < step_user_count:
logger.error(
"Invalid parameters: total user count of %d is smaller than step user count of %d"
% (user_count, step_user_count)
)
return
self.total_users = user_count

if self.stepload_greenlet:
logger.info("There is an ongoing swarming in Step Load mode, will stop it now.")
self.stepload_greenlet.kill()
logger.info(
"Start a new swarming in Step Load mode: total user count of %d, spawn rate of %d, step user count of %d, step duration of %d "
% (user_count, spawn_rate, step_user_count, step_duration)
)
self.update_state(STATE_INIT)
self.stepload_greenlet = self.greenlet.spawn(self.stepload_worker, spawn_rate, step_user_count, step_duration)
self.stepload_greenlet.link_exception(greenlet_exception_handler)

def stepload_worker(self, spawn_rate, step_users_growth, step_duration):
current_num_users = 0
while self.state == STATE_INIT or self.state == STATE_SPAWNING or self.state == STATE_RUNNING:
current_num_users += step_users_growth
if current_num_users > int(self.total_users):
logger.info("Step Load is finished")
break
self.start(current_num_users, spawn_rate)
logger.info("Step loading: start spawn job of %d user" % (current_num_users))
gevent.sleep(step_duration)

def start_shape(self):
if self.shape_greenlet:
logger.info("There is an ongoing shape test running. Editing is disabled")
Expand Down
12 changes: 0 additions & 12 deletions locust/templates/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -78,12 +78,6 @@ <h2>Start new load test</h2>
{% endif %}
</label>
<input type="text" name="host" id="host" class="val" autocapitalize="off" autocorrect="off" value="{{ host or "" }}"/><br>
{% if is_step_load %}
<label for="step_user_count">Number of users to increase by step</label>
<input type="text" name="step_user_count" id="step_user_count" class="val" value="{{ step_users or "" }}"/><br>
<label for="step_duration">Step duration <span style="color:#8a8a8a;">(300s, 20m, 3h, 1h30m, etc.)</span></label>
<input type="text" name="step_duration" id="step_duration" class="val" value="{{ step_time or "" }}"/><br>
{% endif %}
<button type="submit">Start swarming</button>
</form>
<div style="clear:right;"></div>
Expand All @@ -108,12 +102,6 @@ <h2>Edit running load test</h2>
<label for="spawn_rate">Spawn rate <span style="color:#8a8a8a;">(users to start per second (or stop))</span></label>
<input type="text" name="spawn_rate" id="new_spawn_rate" class="val" value="{{ spawn_rate or "" }}"/><br>
{% endif %}
{% if is_step_load %}
<label for="step_user_count">Number of users to increase by step</label>
<input type="text" name="step_user_count" id="step_user_count" class="val" value="{{ step_users or "" }}"/><br>
<label for="step_duration">Step duration <span style="color:#8a8a8a;">(300s, 20m, 3h, 1h30m, etc.)</span></label>
<input type="text" name="step_duration" id="step_duration" class="val" value="{{ step_time or "" }}"/><br>
{% endif %}
{% if is_shape %}
<button type="submit" disabled>Start swarming</button>
{% else %}
Expand Down
35 changes: 0 additions & 35 deletions locust/test/test_runners.py
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,6 @@ def __init__(self):
self.heartbeat_liveness = 3
self.heartbeat_interval = 1
self.stop_timeout = None
self.step_load = True
self.connection_broken = False

def reset_stats(self):
Expand Down Expand Up @@ -1163,40 +1162,6 @@ def tick(self):
sleep(3)
self.assertEqual("stopped", master.state, "The test has not been stopped by the shape class")

def test_spawn_locusts_in_stepload_mode(self):
with mock.patch("locust.rpc.rpc.Server", mocked_rpc()) as server:
master = self.get_runner()
for i in range(5):
server.mocked_send(Message("client_ready", None, "fake_client%i" % i))

# start a new swarming in Step Load mode: total locust count of 10, spawn rate of 2, step locust count of 5, step duration of 2s
master.start_stepload(10, 2, 5, 2)

# make sure the first step run is started
sleep(0.5)
self.assertEqual(5, len(server.outbox))

num_users = 0
end_of_last_step = len(server.outbox)
for _, msg in server.outbox:
num_users += msg.data["num_users"]

self.assertEqual(
5, num_users, "Total number of locusts that would have been spawned for first step is not 5"
)

# make sure the first step run is complete
sleep(2)
num_users = 0
idx = end_of_last_step
while idx < len(server.outbox):
msg = server.outbox[idx][1]
num_users += msg.data["num_users"]
idx += 1
self.assertEqual(
10, num_users, "Total number of locusts that would have been spawned for second step is not 10"
)

def test_exception_in_task(self):
class MyUser(User):
@task
Expand Down
20 changes: 0 additions & 20 deletions locust/test/test_web.py
Original file line number Diff line number Diff line change
Expand Up @@ -79,10 +79,7 @@ def test_index_with_spawn_options(self):
html_to_option = {
"user_count": ["-u", "100"],
"spawn_rate": ["-r", "10.0"],
"step_user_count": ["--step-users", "20"],
"step_duration": ["--step-time", "15"],
}
self.environment.step_load = True
for html_name_to_test in html_to_option.keys():
# Test that setting each spawn option individually populates the corresponding field in the html, and none of the others
self.environment.parsed_options = parse_options(html_to_option[html_name_to_test])
Expand Down Expand Up @@ -300,23 +297,6 @@ class MyUser2(User):
self.assertNotIn("http://example.com", response.content.decode("utf-8"))
self.assertIn("setting this will override the host on all User classes", response.content.decode("utf-8"))

def test_swarm_in_step_load_mode(self):
class MyUser(User):
wait_time = constant(1)

@task(1)
def my_task(self):
pass

self.environment.user_classes = [MyUser]
self.environment.step_load = True
response = requests.post(
"http://127.0.0.1:%i/swarm" % self.web_port,
data={"user_count": 5, "spawn_rate": 2, "step_user_count": 2, "step_duration": "2m"},
)
self.assertEqual(200, response.status_code)
self.assertIn("Step Load Mode", response.text)

def test_report_page(self):
self.stats.log_request("GET", "/test", 120, 5612)
r = requests.get("http://127.0.0.1:%i/stats/report" % self.web_port)
Expand Down
Loading