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

Ensure to wait a second before next tick() #2465

Merged
merged 6 commits into from Nov 15, 2023

Conversation

wiatrak2
Copy link
Contributor

When developing a custom TestShape, a developer needs to implement tick() method, that manages Users spawning. As mentioned in the official documentation:

Locust will call the tick() method approximately once per second.

Therefore, a developer may design a TestShape with a strong assumption that the number of Users will be adjusted with ~1 second interval. Unfortunately, the interval is indeed kept only when no adjustment in Users number is made - hence it seems useless, as any interval is meaningful only during some changes. Here's an example of logs from Locust master when a custom TestShape was implemented and the tick() method is clearly triggered too often:

locust_logs

the timestamps of log messages show, that 20 Users were spawned within ~6 seconds, while the spawn rate is set to 1.0. In fact I was able to achieve much bigger discrepancies between expected/declared and actual number of users.

In this PR I provide a small adjustment in Runner's code to ensure the ~1 second interval is maintained. Also a small code optimization in handling the current_tick Tuple was added to keep the file length almost unchanged :)

@@ -367,6 +366,9 @@ def shape_worker(self) -> None:
# of each load test shape stage.
self.start(user_count=user_count, spawn_rate=spawn_rate, user_classes=user_classes)
self.shape_last_tick = current_tick
shape_adjustment_time_ms = time.time() - shape_adjustment_start
if shape_adjustment_time_ms < 1:
Copy link
Collaborator

@cyberw cyberw Nov 14, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

<1 feels strange :)

Can you maybe change it to not use an if statement at all and instead do:

gevent.sleep(max(0, 1 - shape_adjustment_time_ms))

Also, a test case seems to be breaking...

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yep, just noticed the test, sorry! I'll fix that. the max(..., 0) seems good, will adjust the commit then :)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I haven't noticed that overriding current_tick leads to unwanted behavior. Fix was commited

else:
user_count, spawn_rate, user_classes = current_tick # type: ignore
shape_adjustment_start = time.time()
user_count, spawn_rate, user_classes = current_tick if len(current_tick) == 3 else (*current_tick, None) # type: ignore
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this is less readable than the old way. Does it really need to change or is it unrelated to the issue you are trying to fix?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You're right, it's not related. I wanted to make it more "pythonic" and readable with my previous change (where I modified current_tick when its length was 2), but eventually it indeed got not very clear :) I restored the code from master for this part. Also, I changed a bit the gevent.sleep usage, to call it once for entire tick() handling - seems more understable for me now

@wiatrak2
Copy link
Contributor Author

@cyberw I've also added a dedicated test case that doesn't base on get_run_time when modifying the Users count, but relies on 1-sec interval

@cyberw cyberw merged commit abf3551 into locustio:master Nov 15, 2023
11 checks passed
@cyberw
Copy link
Collaborator

cyberw commented Nov 15, 2023

nice!!

@cyberw
Copy link
Collaborator

cyberw commented Nov 19, 2023

@wiatrak2 is it possible that one of the test cases got a little flaky from this change? Perhaps it should be adjusted? https://github.com/locustio/locust/actions/runs/6919408511/job/18822743361?pr=2472

@cyberw
Copy link
Collaborator

cyberw commented Nov 24, 2023

@wiatrak2 Can you have a look at the occasionally failing test case? While I do believe your change is good, it did break the test case and I dont have time to analyze why it is breaking, so I might have to revert it...

@wiatrak2
Copy link
Contributor Author

@cyberw it happened in the worst possible moment - I'm on vacation and away from my computer until December 16th :( I won't be able to have a look before

@cyberw
Copy link
Collaborator

cyberw commented Nov 25, 2023

No worries! I can disable the tests for now (I dont think anything is actually broken, and if it is its a small issue)

@wiatrak2
Copy link
Contributor Author

Yep, the change was not a sufficient one. If you can disable the test - let's do it. I will have a (very fresh) look once I'm back to the business ;)

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.

None yet

2 participants