-
Notifications
You must be signed in to change notification settings - Fork 3k
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
Start web_ui later to avoid race adding UI routes #1585
Conversation
Flask throws an exception `AssertionError: A setup function was called after the first request was handled…` if it serves a request before all the routes are added, creating a race condition. Starting the web UI after firing `init` event avoids this race. Requires `web_ui.start()` to be called manually when `create_web_ui()` or `WebUI()` are called or instantiated manually.
Hmm.. I'm not a huge fan of this change as it does complicate things a little for users of the api. Shouldnt it be really unlikely for someone to have time to execute a request before init? |
Perhaps we can just block flask from serving requests until init has been run? |
In theory it should be unlikely. In practice, it's quite possible. My coworkers hit it frequently in situations such as leaving a tab open with the web UI showing results from a previous run, stopping Locust, starting it again, and then attempting to open another tab to start and control a new test. The old tab seems to continue to attempt to communicate with Locust UI even after it's torn down and if it happens to get a successful request in at the right moment when Locust is starting again, this happens. We also use Locust as part of a larger set of tools that includes a status page about whether or not the UI is up and ready to be used. That service periodically pings the I'm open to other ideas. Do you know of a way to block Flask from serving requests until we tell it to even if it's been started? Functionally, I'm not sure what the difference would be if there's still some other function that needs to be called before the UI is ready. I thought about adding an artificial delay to give the init time to complete first, but that felt hacky and may not necessarily solve the issue 100% as this does. |
I see. Maybe you can launch the greenlet using an init listener defined in locust? or would it be triggered before user defined events? (I dont know the order they are fired) |
v2 web_ui.start() no longer required to be called. `delayed_start` added as optional parameter to WebUI and `create_web_ui()` that gates starting web UI immediately or after `init` event is fired.
Codecov Report
@@ Coverage Diff @@
## master #1585 +/- ##
==========================================
- Coverage 82.08% 81.89% -0.20%
==========================================
Files 28 28
Lines 2596 2601 +5
Branches 395 397 +2
==========================================
- Hits 2131 2130 -1
- Misses 367 373 +6
Partials 98 98
Continue to review full report at Codecov.
|
With this new approach, web_ui.start() is no longer required to be called. Only downside I can think of is if you're starting the web UI manually via |
I think the new solution is good, or at least as good as can be done without doing a major rewrite :) Can you remove the commented out line? |
Or actually, maybe the old solution is more robust & clean. Using events is kind of a heavy solution, and when it doesnt solve 100% of the cases it probably isnt worth it. Which one do you prefer? |
Solution 3. It's a bit of a combination of the two previous ones. Still uses |
I think the new solution looks nice. The reason it felt ”heavy” was because I dont think we use events internally (and it shouldnt really be necessary, it is just an integration point for external code). One last thing, can you remove the **kwargs argument? |
Hmmm. I've run all tests multiple times following my changes and I haven't hit that error Travis did. All mine are passing clean. |
Looks like flakyness, nothing to worry about. Thanks! |
Flask throws an exception
AssertionError: A setup function was called after the first request was handled…
if it serves a request before all the routes are added, creating a race condition. Starting the web UI after firinginit
event avoids this race.Requires
web_ui.start()
to be called manually whencreate_web_ui()
orWebUI()
are called or instantiated manually.Technically this is a bit of a breaking change because anyone currently relying on
create_web_ui()
orWebUI()
to immediately start the web UI will have to add one more call afterward to do so. I'm interested in feedback about this. In one small way, it's still consistent with how the rest of Locust works sincerunner.start()
is required even after instantiation before it does anything.Thoughts?