question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

Locust does not stop all users

See original GitHub issue

Describe the bug

When there is more than 1 user defined in locust file, Locust does not stop users correctly - there are always 10%-20% left in running state. I’m adding the scenario, that causes problem - there is a background task that periodically checks the number of users and print sit to console.

Expected behavior

All users stopped correctly

Actual behavior

Not all users stopped

Steps to reproduce

Run scenario attached (containing 2 user classes)

Environment

  • OS: Windows/MAC
  • Python version: 3.9
  • Locust version: 2.5
  • Locust command line that you ran: locust -f bug_scenario.py
  • Locust file contents (anonymized if necessary):
from logging import getLogger
from random import randrange

from gevent import sleep, spawn
from locust import task, constant_pacing, SequentialTaskSet, events, User
from locust.env import Environment

logger = getLogger('Scenario')


def add_request_event(request_type, name, duration, length=0, response=None, context=None, exception=None):
    events.request.fire(
        request_type=request_type,
        name=name,
        response_time=duration,
        response_length=length,
        response=response,
        context=context or {},
        exception=exception
    )


def run_environment_checker(environment: Environment):
    while True:
        user_count = environment.runner.user_count
        logger.info(f'User count: {user_count}')
        sleep(3)


@events.init.add_listener
def hadle_environment_checker(environment: Environment, **_):
    spawn(lambda: run_environment_checker(environment))


class TestScenario(SequentialTaskSet):

    def __init__(self, parent):
        super().__init__(parent)

    def on_start(self):
        logger.info('on_start')

    @task
    def open_homepage(self):
        add_request_event(
            request_type='PUT',
            name='3d',
            duration=randrange(1000),
            length=randrange(1000)
        )

    @task
    def open_dashboard(self):
        add_request_event(
            request_type='GET',
            name='2d',
            duration=randrange(1000),
            length=randrange(1000)
        )

    @task
    def get_open_positions(self):
        add_request_event(
            request_type='POST',
            name='1d',
            duration=randrange(1000),
            length=randrange(1000)
        )

    def on_stop(self):
        logger.info('on_stop')


class ExampleUser(User):
    tasks = [TestScenario]
    wait_time = constant_pacing(10)


class ExampleUser2(ExampleUser):
    pass

Issue Analytics

  • State:closed
  • Created 2 years ago
  • Comments:10 (4 by maintainers)

github_iconTop GitHub Comments

1reaction
marcinhcommented, Feb 23, 2022

Hi, sorry for late response. I finally got some time to investigate this a little bit more. So I created a unit test, to cover this case:

    def test_bug_1947(self):
        class BaseUser1(User):
            wait_time = constant(1)

            @task
            def task_a(self):
                sleep(0.1)

        class BaseUser2(BaseUser1):
            wait_time = constant(1)

        runner = Environment(user_classes=[BaseUser1, BaseUser2]).create_local_runner()
        runner.start(user_count=user_count, spawn_rate=4)

        sleep(5)

        self.assertEqual(user_count, runner.user_count)

        runner.stop()
        sleep(5)
        self.assertEqual(0, runner.user_count)

Every time there are 2 users left after stopping. I think this has something to do with Users inheritance. Tomorrow I’ll investigate it further

0reactions
marcinhcommented, Feb 24, 2022

I think problematic is line 253 in runners.py:

                if isinstance(user, self.user_classes_by_name[user_class]):
                    to_stop.append(user)

if we change it to:

                if isinstance(user, self.user_classes_by_name[user_class]) and user.__class__.__name__ == user_class:
                    to_stop.append(user)

or:

                if type(user) == self.user_classes_by_name[user_class]:
                    to_stop.append(user)

test starts passing

Read more comments on GitHub >

github_iconTop Results From Across the Web

Stop Locust When Specified Number Of User Tasks Complete
By default, a Locust is set to never end and will continuously pick a task from the available task sets provided to it....
Read more >
API — Locust 1.4.4 documentation
API¶. User class¶. class User (environment)¶. Represents a “user” which is to be spawned and attack the system that is to be load...
Read more >
How we manipulated Locust to test system performance under ...
Locust is an open-source load testing tool that gauges the number of concurrent users a system can handle. Testers can write simple behaviors...
Read more >
Locust (@locustio) / Twitter
Locust. @locustio. The Python-based load testing framework ... missing worker by @radhakrishnaakamat in #2010 Bugfix/1947 locust does not stop all users by.
Read more >
locustio/locust - Gitter
However, looking at the code now, it looks like the running Locusts won't stop until it does another request, which doesn't feel optimal...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found