From 42d6ecfddfeb94c758f4d3cf0707d725b9562278 Mon Sep 17 00:00:00 2001 From: Lars Holmberg Date: Wed, 30 Oct 2019 16:08:36 +0100 Subject: [PATCH 1/4] Add test for when locusts dont actually exit at end of iteration during stop timeout. --- locust/test/test_runners.py | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/locust/test/test_runners.py b/locust/test/test_runners.py index 089be8c851..1c5dd3746d 100644 --- a/locust/test/test_runners.py +++ b/locust/test/test_runners.py @@ -502,6 +502,8 @@ def my_task(self): class MyTestLocust(Locust): task_set = MyTaskSet + min_wait = 0 + max_wait = 0 options = mocked_options() runner = LocalLocustRunner([MyTestLocust], options) @@ -517,11 +519,19 @@ class MyTestLocust(Locust): runner.quit() self.assertEqual("second", MyTaskSet.state) - options.stop_timeout = short_time * 2 # allow task iteration to complete, with some margin + options.stop_timeout = short_time * 3 # allow task iteration to complete, with some margin runner = LocalLocustRunner([MyTestLocust], options) runner.start_hatching(1, 1) gevent.sleep(short_time) - runner.quit() + timeout = gevent.Timeout(short_time * 2) + timeout.start() + try: + runner.quit() + runner.greenlet.join() + except gevent.Timeout: + self.fail("Got Timeout exception. Some locusts must have kept runnining after iteration finish") + finally: + timeout.cancel() self.assertEqual("third", MyTaskSet.state) def test_stop_timeout_exit_during_wait(self): From 3f0ea59078be1ac52046e6b32aeba51d78eb5587 Mon Sep 17 00:00:00 2001 From: Lars Holmberg Date: Wed, 30 Oct 2019 16:18:01 +0100 Subject: [PATCH 2/4] Reverse commit 96d6759 (Only raise GreenletExit in TaskSet.run(), because that is the only place it is really needed) --- locust/core.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/locust/core.py b/locust/core.py index 7038446056..ce69ef34f7 100644 --- a/locust/core.py +++ b/locust/core.py @@ -358,15 +358,14 @@ def run(self, *args, **kwargs): while (True): try: - if self.locust._state == LOCUST_STATE_STOPPING: - raise GreenletExit() - if not self._task_queue: self.schedule_task(self.get_next_task()) try: self.execute_next_task() except RescheduleTaskImmediately: + if self.locust._state == LOCUST_STATE_STOPPING: + raise GreenletExit() pass except RescheduleTask: self.wait() @@ -430,6 +429,8 @@ def get_wait_secs(self): return millis / 1000.0 def wait(self): + if self.locust._state == LOCUST_STATE_STOPPING: + raise GreenletExit() self.locust._state = LOCUST_STATE_WAITING self._sleep(self.get_wait_secs()) self.locust._state = LOCUST_STATE_RUNNING From 9ed12b87f2a01e61064b1b76dbc5e20a5b504a03 Mon Sep 17 00:00:00 2001 From: Lars Holmberg Date: Thu, 31 Oct 2019 09:54:37 +0100 Subject: [PATCH 3/4] Move check if the locust is stopping and should be exited to a more sensible place. --- locust/core.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/locust/core.py b/locust/core.py index ce69ef34f7..b88c51eff0 100644 --- a/locust/core.py +++ b/locust/core.py @@ -363,6 +363,8 @@ def run(self, *args, **kwargs): try: self.execute_next_task() + if self.locust._state == LOCUST_STATE_STOPPING: + raise GreenletExit() except RescheduleTaskImmediately: if self.locust._state == LOCUST_STATE_STOPPING: raise GreenletExit() @@ -429,8 +431,6 @@ def get_wait_secs(self): return millis / 1000.0 def wait(self): - if self.locust._state == LOCUST_STATE_STOPPING: - raise GreenletExit() self.locust._state = LOCUST_STATE_WAITING self._sleep(self.get_wait_secs()) self.locust._state = LOCUST_STATE_RUNNING From b5c808bc9e418703c57330610e089558043858c4 Mon Sep 17 00:00:00 2001 From: Lars Holmberg Date: Thu, 31 Oct 2019 11:15:59 +0100 Subject: [PATCH 4/4] While using stop-timeout, ensure we dont accidentally run one iteration of the task if the test is stopped during setup. --- locust/core.py | 2 ++ locust/test/test_runners.py | 28 ++++++++++++++++++++++++++++ 2 files changed, 30 insertions(+) diff --git a/locust/core.py b/locust/core.py index b88c51eff0..4af35bc2e6 100644 --- a/locust/core.py +++ b/locust/core.py @@ -362,6 +362,8 @@ def run(self, *args, **kwargs): self.schedule_task(self.get_next_task()) try: + if self.locust._state == LOCUST_STATE_STOPPING: + raise GreenletExit() self.execute_next_task() if self.locust._state == LOCUST_STATE_STOPPING: raise GreenletExit() diff --git a/locust/test/test_runners.py b/locust/test/test_runners.py index 1c5dd3746d..ec34e4f064 100644 --- a/locust/test/test_runners.py +++ b/locust/test/test_runners.py @@ -534,6 +534,34 @@ class MyTestLocust(Locust): timeout.cancel() self.assertEqual("third", MyTaskSet.state) + def test_stop_timeout_during_on_start(self): + short_time = 0.05 + class MyTaskSet(TaskSet): + finished_on_start = False + my_task_run = False + def on_start(self): + gevent.sleep(short_time) + MyTaskSet.finished_on_start = True + + @task + def my_task(self): + MyTaskSet.my_task_run = True + + class MyTestLocust(Locust): + task_set = MyTaskSet + min_wait = 0 + max_wait = 0 + + options = mocked_options() + options.stop_timeout = short_time + runner = LocalLocustRunner([MyTestLocust], options) + runner.start_hatching(1, 1) + gevent.sleep(short_time / 2) + runner.quit() + + self.assertTrue(MyTaskSet.finished_on_start) + self.assertFalse(MyTaskSet.my_task_run) + def test_stop_timeout_exit_during_wait(self): short_time = 0.05 class MyTaskSet(TaskSet):