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

Ability to set a specific number of simulated users per Locust class #575

Closed
barrywhart opened this issue Apr 17, 2017 · 4 comments
Closed

Comments

@barrywhart
Copy link

We are using Locust to test an internal web service where there are two sources of load:

  • Varying load generated by end users. Existing Locust works well for this; we simply enter the number of simulated users in the Locust UI before launching the test.
  • Steady load generated by internal clients. This load does not vary because there is a queue between the clients and the service, and the queue always has a known number of works.

For the latter case, it would be useful to have a way to explicitly set the number of users rather than having Locust compute it from the weights.

@barrywhart
Copy link
Author

Here is a preliminary implementation of this feature for discussion/review. It is implemented as a monkey patch which extends the existing behavior of LocustRunner.weight_locusts(). If this suggestion sounds useful and the implementation looks like a good start, I'll be happy to implement this more properly (i.e. as changes to Locust, rather than a monkey patch) and create a PR for it.

##########################################################################
# Monkey patch the code that creates locusts (i.e. simulated users) so
# we can implement a behavior that works better for Orion. Orion is an API,
# not a web site with users, so the Locust concepts don't necessarily make
# sense for us.
##########################################################################

old_weight_locusts = runners.LocustRunner.weight_locusts


def weight_locusts(self, amount, stop_timeout=None):
    """
    Returns a list of classes from self.locust_classes, one entry per "user"
    that should be launched.

    The default implementation weights all the classes based on their "weight"
    field. This version implements a "force_users" property on the Locust
    classes. If that property is present, that *exact* number of users will
    be created. The remaining classes will be allocated normally, based on
    amount.

    :param self:
    :param amount:
    :param stop_timeout:
    :return:
    """
    # First handle the "force_users" classes.
    force_users_result = []
    for locust in self.locust_classes:
        force_users = getattr(locust, 'force_users', None)
        if force_users:
            force_users_result += [locust for x in xrange(0, force_users)]

            # Set weight to 0 so the "parent" function will ignore this class.
            locust.weight = 0

    # Now do normal parent processing.
    result = old_weight_locusts(self, amount, stop_timeout)
    return force_users_result + result

runners.LocustRunner.weight_locusts = weight_locusts

@aldenpeterson-wf
Copy link
Contributor

I am closing this as it appears the monkey-patch approach works - LMK if I need to reopen this.

@zoharm
Copy link

zoharm commented Jan 31, 2019

Hi all, apologies if I am incorrectly resurrecting this, I tried out this monkey patch (simply added it to the locustfile.py) but not surprisingly got "name 'runners' is not defined"

What am I doing wrong? Any help please?

Thank you!

@Grimlek
Copy link

Grimlek commented Jun 1, 2021

Here is an example for locust 1.5.3. Definitely not a perfect solution but something to get anyone started if they stumble upon this.

from locust import events, runners

"""
    Monkey Patch Users class to allow specifying a specific number of users. 
    
    Add max_users_count attribute to user class.
"""
weight_old_users = runners.Runner.weight_users


def weight_users(self, amount):
    # now do normal parent processing and get bucket
    bucket = weight_old_users(self, amount)

    for user in self.user_classes:
        max_users_count = getattr(user, 'max_users_count', None)
        if max_users_count:
            bucket_users = [x for x in bucket if repr(x) == repr(user)]
            
            # modify bucket to force a limited number of users
            # in the event locust count is modified in the UI, don't add more users to new greenlets (quick approach)
            if len(bucket_users) > max_users_count and len(self.user_greenlets) == 0:
                new_bucket = [x for x in bucket if repr(x) != repr(user)]
                bucket_forced_users = bucket_users[-max_users_count:]
                bucket = new_bucket + bucket_forced_users
            elif len(self.user_greenlets) > 0:
                new_bucket = [x for x in bucket if repr(x) != repr(user)]
                bucket = new_bucket
    return bucket


runners.Runner.weight_users = weight_users
#############################################################
#              END MONKEY PATCH
#############################################################

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

No branches or pull requests

4 participants