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

Wrong distribution of virtual users by user classes #2662

Closed
2 tasks done
llirrikk opened this issue Apr 1, 2024 · 5 comments · Fixed by #2663
Closed
2 tasks done

Wrong distribution of virtual users by user classes #2662

llirrikk opened this issue Apr 1, 2024 · 5 comments · Fixed by #2663
Labels

Comments

@llirrikk
Copy link
Contributor

llirrikk commented Apr 1, 2024

Prerequisites

Description

Hi, thanks for the awesome project. I came across the behavior of locust that is unclear to me.
When I create two user classes TestUser1 and TestUser2 with weights of 20 and 25 respectively, the actual distribution shows that 23 and 22 virtual users were created respectively. The sum of 45 is identical, but it is the distribution, I consider, that is being performed incorrectly. I think there should be 20 VUsers for TestUser1 and 25 for TestUser2.

Documentation says:

If you wish to simulate more users of a certain type you can set a weight attribute on those classes.

Locust logs:

[2024-04-01 20:43:46,470] cab-wsm-0023164/INFO/locust.main: Starting web interface at http://0.0.0.0:8089
[2024-04-01 20:43:46,483] cab-wsm-0023164/INFO/locust.main: Starting Locust 2.24.1
[2024-04-01 20:43:50,643] cab-wsm-0023164/INFO/locust.runners: Ramping to 45 users at a rate of 50.00 per second
[2024-04-01 20:43:50,643] cab-wsm-0023164/DEBUG/locust.runners: Ramping to {"TestUser1": 23, "TestUser2": 22} (45 total users)
[2024-04-01 20:43:50,644] cab-wsm-0023164/DEBUG/locust.runners: Spawning additional {"TestUser1": 23, "TestUser2": 22} ({"TestUser1": 0, "TestUser2": 0} already running)...
[2024-04-01 20:43:50,645] cab-wsm-0023164/DEBUG/locust.runners: 10 users spawned
[2024-04-01 20:43:50,646] cab-wsm-0023164/DEBUG/locust.runners: 20 users spawned
[2024-04-01 20:43:50,646] cab-wsm-0023164/DEBUG/locust.runners: 23 users spawned
[2024-04-01 20:43:50,646] cab-wsm-0023164/DEBUG/locust.runners: All users of class TestUser1 spawned
[2024-04-01 20:43:50,647] cab-wsm-0023164/DEBUG/locust.runners: 33 users spawned
[2024-04-01 20:43:50,648] cab-wsm-0023164/DEBUG/locust.runners: 43 users spawned
[2024-04-01 20:43:50,648] cab-wsm-0023164/DEBUG/locust.runners: 45 users spawned
[2024-04-01 20:43:50,648] cab-wsm-0023164/DEBUG/locust.runners: All users of class TestUser2 spawned
[2024-04-01 20:43:50,648] cab-wsm-0023164/DEBUG/locust.runners: 0 users have been stopped, 45 still running
[2024-04-01 20:43:50,648] cab-wsm-0023164/INFO/locust.runners: All users spawned: {"TestUser1": 23, "TestUser2": 22} (45 total users)

Command line

locust --host http://localhost:5000 --users 45 --spawn-rate 50 --loglevel "DEBUG"

Locustfile contents

from locust import FastHttpUser, constant_pacing, task


class TestUser1(FastHttpUser):
    wait_time = constant_pacing(7200)
    weight = 20

    @task
    def hello_world(self):
        self.client.get("http://localhost:5000/1")


class TestUser2(FastHttpUser):
    wait_time = constant_pacing(7200)
    weight = 25

    @task
    def hello_world2(self):
        self.client.get("http://localhost:5000/2")

Python version

3.11.6

Locust version

2.24.1

Operating system

MacOS 13.6.4

@llirrikk llirrikk added the bug label Apr 1, 2024
@cyberw
Copy link
Collaborator

cyberw commented Apr 1, 2024

Thats very strange and a significant issue. Is it consistently 22/23?

@llirrikk
Copy link
Contributor Author

llirrikk commented Apr 2, 2024

The result is always reproduced with the same input data. The bug is consistently, not floating. I've done some tests, here's the result:

# Weight for TestUser1 Weight for TestUser2 Total VU Expected VU for TestUser1 Expected VU for TestUser2 Actual VU for TestUser1 Actual VU for TestUser2 Is correct?
1 4 5 9 4 5 5 4 No
2 5 4 9 5 4 5 4 Yes
3 4 5 45 20 25 23 22 No
4 8 10 18 8 10 9 9 No
5 8 10 36 16 20 18 18 No
6 20 25 45 20 25 23 22 No
7 25 20 45 25 20 23 22 No
8 2 5 7 2 5 2 5 Yes
9 2 3 5 2 3 2 3 Yes

@llirrikk
Copy link
Contributor Author

llirrikk commented Apr 2, 2024

When the VU ratio TestUser1/TestUser2 = 0.8, the distribution doesn't work correctly. It feels like the algorithm is trying to equate virtual users of two user classes (only if it's 4/5).
In # test 8 and 9 I tried the ratios 2/5 and 2/3 - everything works correctly.

@cyberw
Copy link
Collaborator

cyberw commented Apr 2, 2024

Interesting (that’s one word for it, right? ;)

I’ll have a look at it once I’m back from holiday. @mboutet I dont know if you’re still around and have time to take a look :)

@llirrikk
Copy link
Contributor Author

llirrikk commented Apr 2, 2024

Sent the PR. I think one of the problems was in the rounding algorithm, it rounded to an even number ("round half to even" or "banker's rounding"). For weights 4 and 5 there are:

locust/locust/dispatch.py

Lines 369 to 381 in a214363

# Normalize the weights so that the smallest weight will be equal to "target_min_weight".
# The value "2" was experimentally determined because it gave a better distribution especially
# when dealing with weights which are close to each others, e.g. 1.5, 2, 2.4, etc.
target_min_weight = 2
# 'Value' here means weight or fixed count
normalized_values = [
(
user.__name__,
round(target_min_weight * value / min(u[1] for u in users)),
)
for user, value in users
]

Pseudocode:
target_min_weight = 2
min_of_weights = min(4, 5) = 4

for 4: round(target_min_weight * value / min_of_weights) = round(2 * 4 / 4) = round(2) = 2
for 5: round(target_min_weight * value / min_of_weights) = round(2 * 5 / 4) = round(2.5) = 2

As I noted above, that's why the VU values tended to be the same (because the normalized weights are the same). I reworked the algorithm using the greatest common divisor, because I think it's more logical in this case during normalization. Instead of target_min_weight, the meaning of the algorithm that I couldn't figure out.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging a pull request may close this issue.

2 participants