From b32361522c61c43f8c0ce9648531a4875039dbf6 Mon Sep 17 00:00:00 2001 From: unknown Date: Fri, 11 May 2012 15:04:01 +0200 Subject: [PATCH 1/7] Added tooltips to ramping form --- locust/templates/index.html | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/locust/templates/index.html b/locust/templates/index.html index 50af099bde..dd18c80cde 100644 --- a/locust/templates/index.html +++ b/locust/templates/index.html @@ -99,25 +99,25 @@

Change the locust count

Ramping

- +
- +
- +
- +
- +
- +
- +
- +
- +


From 9f2c9ccb5925e93817b8377b8dd8605ec86739a3 Mon Sep 17 00:00:00 2001 From: Hugo Heyman Date: Fri, 11 May 2012 15:17:35 +0200 Subject: [PATCH 2/7] Refactoring of ramping functionality to make it as separate at possible --- locust/main.py | 9 +--- locust/ramping.py | 135 ++++++++++++++++++++++++++++++++++++++++++++++ locust/runners.py | 78 +-------------------------- locust/web.py | 3 +- 4 files changed, 139 insertions(+), 86 deletions(-) create mode 100644 locust/ramping.py diff --git a/locust/main.py b/locust/main.py index 769bf15c54..f800d6ddaa 100644 --- a/locust/main.py +++ b/locust/main.py @@ -383,14 +383,7 @@ def main(): main_greenlet = runners.locust_runner.greenlet if options.ramp: - import rampstats - from rampstats import on_request_success, on_report_to_master, on_slave_report - if options.slave: - events.report_to_master += on_report_to_master - if options.master: - events.slave_report += on_slave_report - else: - events.request_success += on_request_success + import ramping if options.print_stats or (options.no_web and not options.slave): # spawn stats printing greenlet diff --git a/locust/ramping.py b/locust/ramping.py new file mode 100644 index 0000000000..d6195b2d50 --- /dev/null +++ b/locust/ramping.py @@ -0,0 +1,135 @@ +""" +This module adds a tool to locust with intention to help find a highest amount of simulated users, a system can handle. +Parameters to define thresholds for this tool are configured in the web-user-interface + +When this module is used, additional response time -data is recorded. +This so that we can calculate a percentile value of the current response times, +meaning we account for the response times recorded in a moving time window. + +""" + +from stats import percentile, RequestStats +from runners import locust_runner, DistributedLocustRunner, SLAVE_REPORT_INTERVAL, STATE_HATCHING +from collections import deque +import events +import math +import gevent +import logging + +logger = logging.getLogger(__name__) + +response_times = deque([]) + +# Are we running in distributed mode or not? +is_distributed = isinstance(locust_runner, DistributedLocustRunner) + +# The time window in seconds that current_percentile use data from +PERCENTILE_TIME_WINDOW = 15.0 + +def current_percentile(percent): + if is_distributed: + # Flatten out the deque of lists and calculate the percentile to be returned + return percentile(sorted([item for sublist in response_times for item in sublist]), percent) + else: + return percentile(sorted(response_times), percent) + +def on_request_success(_, _1, response_time, _2): + if is_distributed: + response_times.append(response_time) + else: + response_times.append(response_time) + + # remove from the queue + rps = RequestStats.sum_stats().current_rps + if len(response_times) > rps*PERCENTILE_TIME_WINDOW: + for i in xrange(len(response_times) - int(math.ceil(rps*PERCENTILE_TIME_WINDOW))): + response_times.popleft() + +def on_report_to_master(_, data): + global response_times + data["current_responses"] = response_times + response_times = [] + +def on_slave_report(_, data): + if "current_responses" in data: + response_times.append(data["current_responses"]) + + # remove from the queue + slaves = locust_runner.slave_count + response_times_per_slave_count = PERCENTILE_TIME_WINDOW/SLAVE_REPORT_INTERVAL + if len(response_times) > slaves * response_times_per_slave_count: + response_times.popleft() + +events.report_to_master += on_report_to_master +events.slave_report += on_slave_report +events.request_success += on_request_success + +def start_ramping(hatch_rate=None, max_locusts=1000, hatch_stride=100, + percent=0.95, response_time_limit=2000, acceptable_fail=0.05, + precision=200, start_count=0, calibration_time=15): + + def ramp_up(clients, hatch_stride, boundery_found=False): + while True: + if locust_runner.state != STATE_HATCHING: + if locust_runner.num_clients >= max_locusts: + logger.info("Ramp up halted; Max locusts limit reached: %d" % max_locusts) + return ramp_down(clients, hatch_stride) + + gevent.sleep(calibration_time) + fail_ratio = RequestStats.sum_stats().fail_ratio + if fail_ratio > acceptable_fail: + logger.info("Ramp up halted; Acceptable fail ratio %d%% exceeded with fail ratio %d%%" % (acceptable_fail*100, fail_ratio*100)) + return ramp_down(clients, hatch_stride) + + p = current_percentile(percent) + if p >= response_time_limit: + logger.info("Ramp up halted; Percentile response times getting high: %d" % p) + return ramp_down(clients, hatch_stride) + + if boundery_found and hatch_stride <= precision: + logger.info("Sweet spot found! Ramping stopped at %i locusts" % (locust_runner.num_clients)) + return + + logger.info("Ramping up...") + if boundery_found: + hatch_stride = max((hatch_stride/2),precision) + clients += hatch_stride + locust_runner.start_hatching(clients, locust_runner.hatch_rate) + gevent.sleep(1) + + def ramp_down(clients, hatch_stride): + while True: + if locust_runner.state != STATE_HATCHING: + if locust_runner.num_clients < max_locusts: + gevent.sleep(calibration_time) + fail_ratio = RequestStats.sum_stats().fail_ratio + if fail_ratio <= acceptable_fail: + p = current_percentile(percent) + if p <= response_time_limit: + if hatch_stride <= precision: + logger.info("Sweet spot found! Ramping stopped at %i locusts" % (locust_runner.num_clients)) + return + + logger.info("Ramping up...") + hatch_stride = max((hatch_stride/2),precision) + clients += hatch_stride + locust_runner.start_hatching(clients, locust_runner.hatch_rate) + return ramp_up(clients, hatch_stride, True) + + logger.info("Ramping down...") + hatch_stride = max((hatch_stride/2),precision) + clients -= hatch_stride + if clients > 0: + locust_runner.start_hatching(clients, locust_runner.hatch_rate) + else: + logger.warning("No responses met the ramping thresholds, check your ramp configuration, locustfile and \"--host\" address") + logger.info("RAMING STOPPED") + return + gevent.sleep(1) + + if hatch_rate: + locust_runner.hatch_rate = hatch_rate + if start_count > 0: + locust_runner.start_hatching(start_count, hatch_rate) + logger.info("RAMPING STARTED") + ramp_up(start_count, hatch_stride) \ No newline at end of file diff --git a/locust/runners.py b/locust/runners.py index db750b25fc..477dccda20 100644 --- a/locust/runners.py +++ b/locust/runners.py @@ -131,7 +131,7 @@ def kill_locusts(self, kill_count): bucket = self.weight_locusts(kill_count) kill_count = len(bucket) self.num_clients -= kill_count - logger.debug("killing locusts: %i", kill_count) + logger.info("Killing %i locusts" % kill_count) dying = [] for g in self.locusts: for l in bucket: @@ -175,82 +175,6 @@ def stop(self): self.locusts.kill(block=True) self.state = STATE_STOPPED - - def start_ramping(self, hatch_rate=None, max_locusts=1000, hatch_stride=100, - percent=0.95, response_time_limit=2000, acceptable_fail=0.05, - precision=200, start_count=0, calibration_time=15): - - from rampstats import current_percentile - if hatch_rate: - self.hatch_rate = hatch_rate - - def ramp_down_help(clients, hatch_stride): - print "ramping down..." - hatch_stride = max(hatch_stride/2, precision) - clients -= hatch_stride - self.start_hatching(clients, self.hatch_rate) - return clients, hatch_stride - - def ramp_up(clients, hatch_stride, boundery_found=False): - while True: - if self.state != STATE_HATCHING: - if self.num_clients >= max_locusts: - print "ramp up stopped due to max locusts limit reached:", max_locusts - client, hatch_stride = ramp_down_help(clients, hatch_stride) - return ramp_down(clients, hatch_stride) - gevent.sleep(calibration_time) - fail_ratio = RequestStats.sum_stats().fail_ratio - if fail_ratio > acceptable_fail: - print "ramp up stopped due to acceptable fail ratio %d%% exceeded with fail ratio %d%%" % (acceptable_fail*100, fail_ratio*100) - client, hatch_stride = ramp_down_help(clients, hatch_stride) - return ramp_down(clients, hatch_stride) - p = current_percentile(percent) - if p >= response_time_limit: - print "ramp up stopped due to percentile response times getting high:", p - client, hatch_stride = ramp_down_help(clients, hatch_stride) - return ramp_down(clients, hatch_stride) - if boundery_found and hatch_stride <= precision: - print "sweet spot found, ramping stopped!" - return - print "ramping up..." - if boundery_found: - hatch_stride = max((hatch_stride/2),precision) - clients += hatch_stride - self.start_hatching(clients, self.hatch_rate) - gevent.sleep(1) - - def ramp_down(clients, hatch_stride): - while True: - if self.state != STATE_HATCHING: - if self.num_clients < max_locusts: - gevent.sleep(calibration_time) - fail_ratio = RequestStats.sum_stats().fail_ratio - if fail_ratio <= acceptable_fail: - p = current_percentile(percent) - if p <= response_time_limit: - if hatch_stride <= precision: - print "sweet spot found, ramping stopped!" - return - print "ramping up..." - hatch_stride = max((hatch_stride/2),precision) - clients += hatch_stride - self.start_hatching(clients, self.hatch_rate) - return ramp_up(clients, hatch_stride, True) - print "ramping down..." - hatch_stride = max((hatch_stride/2),precision) - clients -= hatch_stride - if clients > 0: - self.start_hatching(clients, self.hatch_rate) - else: - print "WARNING: no responses met the ramping thresholds, check your ramp configuration, locustfile and \"--host\" address" - print "ramping stopped!" - return - gevent.sleep(1) - - if start_count > self.num_clients: - self.start_hatching(start_count, hatch_rate) - ramp_up(start_count, hatch_stride) - class LocalLocustRunner(LocustRunner): def start_hatching(self, locust_count=None, hatch_rate=None, wait=False): self.hatching_greenlet = gevent.spawn(lambda: super(LocalLocustRunner, self).start_hatching(locust_count, hatch_rate, wait=wait)) diff --git a/locust/web.py b/locust/web.py index 1551137bd0..a332f72b51 100644 --- a/locust/web.py +++ b/locust/web.py @@ -11,6 +11,7 @@ import runners from runners import MasterLocustRunner +from rampstats import start_ramping from locust.stats import RequestStats, median_from_dict from locust import version import gevent @@ -78,7 +79,7 @@ def ramp(): percentile = float(int(request.form["percentile"]) / 100.0) fail_rate = float(int(request.form["fail_rate"]) / 100.0) calibration_time = int(request.form["wait_time"]) - gevent.spawn(runners.locust_runner.start_ramping, hatch_rate, max_clients, hatch_stride, percentile, response_time, fail_rate, precision, init_clients, calibration_time) + gevent.spawn(start_ramping, hatch_rate, max_clients, hatch_stride, percentile, response_time, fail_rate, precision, init_clients, calibration_time) response = make_response(json.dumps({'success':True, 'message': 'Ramping started'})) response.headers["Content-type"] = "application/json" return response From 4c9750ca1ef71056b11d4654125545c687b3bd4b Mon Sep 17 00:00:00 2001 From: Hugo Heyman Date: Fri, 11 May 2012 16:29:28 +0200 Subject: [PATCH 3/7] forgot to commit the name change of rampstats.py to ramping.py --- locust/ramping.py | 268 ++++++++++++++++++++++---------------------- locust/rampstats.py | 48 -------- 2 files changed, 134 insertions(+), 182 deletions(-) delete mode 100644 locust/rampstats.py diff --git a/locust/ramping.py b/locust/ramping.py index d6195b2d50..bf960629d4 100644 --- a/locust/ramping.py +++ b/locust/ramping.py @@ -1,135 +1,135 @@ -""" -This module adds a tool to locust with intention to help find a highest amount of simulated users, a system can handle. -Parameters to define thresholds for this tool are configured in the web-user-interface - -When this module is used, additional response time -data is recorded. -This so that we can calculate a percentile value of the current response times, -meaning we account for the response times recorded in a moving time window. - -""" - -from stats import percentile, RequestStats -from runners import locust_runner, DistributedLocustRunner, SLAVE_REPORT_INTERVAL, STATE_HATCHING -from collections import deque -import events -import math -import gevent -import logging - -logger = logging.getLogger(__name__) - -response_times = deque([]) - -# Are we running in distributed mode or not? -is_distributed = isinstance(locust_runner, DistributedLocustRunner) - -# The time window in seconds that current_percentile use data from -PERCENTILE_TIME_WINDOW = 15.0 - -def current_percentile(percent): - if is_distributed: - # Flatten out the deque of lists and calculate the percentile to be returned - return percentile(sorted([item for sublist in response_times for item in sublist]), percent) - else: - return percentile(sorted(response_times), percent) - -def on_request_success(_, _1, response_time, _2): - if is_distributed: - response_times.append(response_time) - else: - response_times.append(response_time) - - # remove from the queue - rps = RequestStats.sum_stats().current_rps - if len(response_times) > rps*PERCENTILE_TIME_WINDOW: - for i in xrange(len(response_times) - int(math.ceil(rps*PERCENTILE_TIME_WINDOW))): - response_times.popleft() - -def on_report_to_master(_, data): - global response_times - data["current_responses"] = response_times - response_times = [] - -def on_slave_report(_, data): - if "current_responses" in data: - response_times.append(data["current_responses"]) - - # remove from the queue - slaves = locust_runner.slave_count - response_times_per_slave_count = PERCENTILE_TIME_WINDOW/SLAVE_REPORT_INTERVAL - if len(response_times) > slaves * response_times_per_slave_count: - response_times.popleft() - -events.report_to_master += on_report_to_master -events.slave_report += on_slave_report -events.request_success += on_request_success - -def start_ramping(hatch_rate=None, max_locusts=1000, hatch_stride=100, - percent=0.95, response_time_limit=2000, acceptable_fail=0.05, - precision=200, start_count=0, calibration_time=15): - - def ramp_up(clients, hatch_stride, boundery_found=False): - while True: - if locust_runner.state != STATE_HATCHING: - if locust_runner.num_clients >= max_locusts: - logger.info("Ramp up halted; Max locusts limit reached: %d" % max_locusts) - return ramp_down(clients, hatch_stride) - - gevent.sleep(calibration_time) - fail_ratio = RequestStats.sum_stats().fail_ratio - if fail_ratio > acceptable_fail: - logger.info("Ramp up halted; Acceptable fail ratio %d%% exceeded with fail ratio %d%%" % (acceptable_fail*100, fail_ratio*100)) - return ramp_down(clients, hatch_stride) - - p = current_percentile(percent) - if p >= response_time_limit: - logger.info("Ramp up halted; Percentile response times getting high: %d" % p) - return ramp_down(clients, hatch_stride) - - if boundery_found and hatch_stride <= precision: - logger.info("Sweet spot found! Ramping stopped at %i locusts" % (locust_runner.num_clients)) - return - - logger.info("Ramping up...") - if boundery_found: - hatch_stride = max((hatch_stride/2),precision) - clients += hatch_stride - locust_runner.start_hatching(clients, locust_runner.hatch_rate) - gevent.sleep(1) - - def ramp_down(clients, hatch_stride): - while True: - if locust_runner.state != STATE_HATCHING: - if locust_runner.num_clients < max_locusts: - gevent.sleep(calibration_time) - fail_ratio = RequestStats.sum_stats().fail_ratio - if fail_ratio <= acceptable_fail: - p = current_percentile(percent) - if p <= response_time_limit: - if hatch_stride <= precision: - logger.info("Sweet spot found! Ramping stopped at %i locusts" % (locust_runner.num_clients)) - return - - logger.info("Ramping up...") - hatch_stride = max((hatch_stride/2),precision) - clients += hatch_stride - locust_runner.start_hatching(clients, locust_runner.hatch_rate) - return ramp_up(clients, hatch_stride, True) - - logger.info("Ramping down...") - hatch_stride = max((hatch_stride/2),precision) - clients -= hatch_stride - if clients > 0: - locust_runner.start_hatching(clients, locust_runner.hatch_rate) - else: - logger.warning("No responses met the ramping thresholds, check your ramp configuration, locustfile and \"--host\" address") - logger.info("RAMING STOPPED") - return - gevent.sleep(1) - - if hatch_rate: - locust_runner.hatch_rate = hatch_rate - if start_count > 0: - locust_runner.start_hatching(start_count, hatch_rate) - logger.info("RAMPING STARTED") +""" +This module adds a tool to locust with intention to help find a highest amount of simulated users, a system can handle. +Parameters to define thresholds for this tool are configured in the web-user-interface + +When this module is used, additional response time -data is recorded. +This so that we can calculate a percentile value of the current response times, +meaning we account for the response times recorded in a moving time window. + +""" + +from stats import percentile, RequestStats +from runners import locust_runner, DistributedLocustRunner, SLAVE_REPORT_INTERVAL, STATE_HATCHING +from collections import deque +import events +import math +import gevent +import logging + +logger = logging.getLogger(__name__) + +response_times = deque([]) + +# Are we running in distributed mode or not? +is_distributed = isinstance(locust_runner, DistributedLocustRunner) + +# The time window in seconds that current_percentile use data from +PERCENTILE_TIME_WINDOW = 15.0 + +def current_percentile(percent): + if is_distributed: + # Flatten out the deque of lists and calculate the percentile to be returned + return percentile(sorted([item for sublist in response_times for item in sublist]), percent) + else: + return percentile(sorted(response_times), percent) + +def on_request_success(_, _1, response_time, _2): + if is_distributed: + response_times.append(response_time) + else: + response_times.append(response_time) + + # remove from the queue + rps = RequestStats.sum_stats().current_rps + if len(response_times) > rps*PERCENTILE_TIME_WINDOW: + for i in xrange(len(response_times) - int(math.ceil(rps*PERCENTILE_TIME_WINDOW))): + response_times.popleft() + +def on_report_to_master(_, data): + global response_times + data["current_responses"] = response_times + response_times = [] + +def on_slave_report(_, data): + if "current_responses" in data: + response_times.append(data["current_responses"]) + + # remove from the queue + slaves = locust_runner.slave_count + response_times_per_slave_count = PERCENTILE_TIME_WINDOW/SLAVE_REPORT_INTERVAL + if len(response_times) > slaves * response_times_per_slave_count: + response_times.popleft() + +events.report_to_master += on_report_to_master +events.slave_report += on_slave_report +events.request_success += on_request_success + +def start_ramping(hatch_rate=None, max_locusts=1000, hatch_stride=100, + percent=0.95, response_time_limit=2000, acceptable_fail=0.05, + precision=200, start_count=0, calibration_time=15): + + def ramp_up(clients, hatch_stride, boundery_found=False): + while True: + if locust_runner.state != STATE_HATCHING: + if locust_runner.num_clients >= max_locusts: + logger.info("Ramp up halted; Max locusts limit reached: %d" % max_locusts) + return ramp_down(clients, hatch_stride) + + gevent.sleep(calibration_time) + fail_ratio = RequestStats.sum_stats().fail_ratio + if fail_ratio > acceptable_fail: + logger.info("Ramp up halted; Acceptable fail ratio %d%% exceeded with fail ratio %d%%" % (acceptable_fail*100, fail_ratio*100)) + return ramp_down(clients, hatch_stride) + + p = current_percentile(percent) + if p >= response_time_limit: + logger.info("Ramp up halted; Percentile response times getting high: %d" % p) + return ramp_down(clients, hatch_stride) + + if boundery_found and hatch_stride <= precision: + logger.info("Sweet spot found! Ramping stopped at %i locusts" % (locust_runner.num_clients)) + return + + logger.info("Ramping up...") + if boundery_found: + hatch_stride = max((hatch_stride/2),precision) + clients += hatch_stride + locust_runner.start_hatching(clients, locust_runner.hatch_rate) + gevent.sleep(1) + + def ramp_down(clients, hatch_stride): + while True: + if locust_runner.state != STATE_HATCHING: + if locust_runner.num_clients < max_locusts: + gevent.sleep(calibration_time) + fail_ratio = RequestStats.sum_stats().fail_ratio + if fail_ratio <= acceptable_fail: + p = current_percentile(percent) + if p <= response_time_limit: + if hatch_stride <= precision: + logger.info("Sweet spot found! Ramping stopped at %i locusts" % (locust_runner.num_clients)) + return + + logger.info("Ramping up...") + hatch_stride = max((hatch_stride/2),precision) + clients += hatch_stride + locust_runner.start_hatching(clients, locust_runner.hatch_rate) + return ramp_up(clients, hatch_stride, True) + + logger.info("Ramping down...") + hatch_stride = max((hatch_stride/2),precision) + clients -= hatch_stride + if clients > 0: + locust_runner.start_hatching(clients, locust_runner.hatch_rate) + else: + logger.warning("No responses met the ramping thresholds, check your ramp configuration, locustfile and \"--host\" address") + logger.info("RAMING STOPPED") + return + gevent.sleep(1) + + if hatch_rate: + locust_runner.hatch_rate = hatch_rate + if start_count > 0: + locust_runner.start_hatching(start_count, hatch_rate) + logger.info("RAMPING STARTED") ramp_up(start_count, hatch_stride) \ No newline at end of file diff --git a/locust/rampstats.py b/locust/rampstats.py deleted file mode 100644 index ba7908aa47..0000000000 --- a/locust/rampstats.py +++ /dev/null @@ -1,48 +0,0 @@ -from stats import percentile, RequestStats -from runners import locust_runner, DistributedLocustRunner, SLAVE_REPORT_INTERVAL -from collections import deque -import events -import math - -master_response_times = deque([]) -slave_response_times = [] - -# Are we running in distributed mode or not? -is_distributed = isinstance(locust_runner, DistributedLocustRunner) - -# The time window in seconds that current_percentile use data from -PERCENTILE_TIME_WINDOW = 15.0 - -def current_percentile(percent): - if is_distributed: - # Flatten out the deque of lists and calculate the percentile to be returned - return percentile(sorted([item for sublist in master_response_times for item in sublist]), percent) - else: - return percentile(sorted(master_response_times), percent) - -def on_request_success(_, response_time, _2): - if is_distributed: - slave_response_times.append(response_time) - else: - master_response_times.append(response_time) - - # remove from the queue - rps = RequestStats.sum_stats().current_rps - if len(master_response_times) > rps*PERCENTILE_TIME_WINDOW: - for i in xrange(len(master_response_times) - int(math.ceil(rps*PERCENTILE_TIME_WINDOW))): - master_response_times.popleft() - -def on_report_to_master(_, data): - global slave_response_times - data["current_responses"] = slave_response_times - slave_response_times = [] - -def on_slave_report(_, data): - if "current_responses" in data: - master_response_times.append(data["current_responses"]) - - # remove from the queue - slaves = locust_runner.slave_count - response_times_per_slave_count = PERCENTILE_TIME_WINDOW/SLAVE_REPORT_INTERVAL - if len(master_response_times) > slaves * response_times_per_slave_count: - master_response_times.popleft() From 21d81736da09e7814ab79f11b1dd8586e33825fd Mon Sep 17 00:00:00 2001 From: Hugo Heyman Date: Sun, 13 May 2012 14:31:10 +0200 Subject: [PATCH 4/7] fixed tabbed indentation to spaces --- locust/ramping.py | 138 +++++++++++++++++++++++----------------------- 1 file changed, 69 insertions(+), 69 deletions(-) diff --git a/locust/ramping.py b/locust/ramping.py index bf960629d4..15d49e88c8 100644 --- a/locust/ramping.py +++ b/locust/ramping.py @@ -59,77 +59,77 @@ def on_slave_report(_, data): response_times_per_slave_count = PERCENTILE_TIME_WINDOW/SLAVE_REPORT_INTERVAL if len(response_times) > slaves * response_times_per_slave_count: response_times.popleft() - + events.report_to_master += on_report_to_master events.slave_report += on_slave_report events.request_success += on_request_success def start_ramping(hatch_rate=None, max_locusts=1000, hatch_stride=100, - percent=0.95, response_time_limit=2000, acceptable_fail=0.05, - precision=200, start_count=0, calibration_time=15): - - def ramp_up(clients, hatch_stride, boundery_found=False): - while True: - if locust_runner.state != STATE_HATCHING: - if locust_runner.num_clients >= max_locusts: - logger.info("Ramp up halted; Max locusts limit reached: %d" % max_locusts) - return ramp_down(clients, hatch_stride) - - gevent.sleep(calibration_time) - fail_ratio = RequestStats.sum_stats().fail_ratio - if fail_ratio > acceptable_fail: - logger.info("Ramp up halted; Acceptable fail ratio %d%% exceeded with fail ratio %d%%" % (acceptable_fail*100, fail_ratio*100)) - return ramp_down(clients, hatch_stride) - - p = current_percentile(percent) - if p >= response_time_limit: - logger.info("Ramp up halted; Percentile response times getting high: %d" % p) - return ramp_down(clients, hatch_stride) - - if boundery_found and hatch_stride <= precision: - logger.info("Sweet spot found! Ramping stopped at %i locusts" % (locust_runner.num_clients)) - return - - logger.info("Ramping up...") - if boundery_found: - hatch_stride = max((hatch_stride/2),precision) - clients += hatch_stride - locust_runner.start_hatching(clients, locust_runner.hatch_rate) - gevent.sleep(1) - - def ramp_down(clients, hatch_stride): - while True: - if locust_runner.state != STATE_HATCHING: - if locust_runner.num_clients < max_locusts: - gevent.sleep(calibration_time) - fail_ratio = RequestStats.sum_stats().fail_ratio - if fail_ratio <= acceptable_fail: - p = current_percentile(percent) - if p <= response_time_limit: - if hatch_stride <= precision: - logger.info("Sweet spot found! Ramping stopped at %i locusts" % (locust_runner.num_clients)) - return - - logger.info("Ramping up...") - hatch_stride = max((hatch_stride/2),precision) - clients += hatch_stride - locust_runner.start_hatching(clients, locust_runner.hatch_rate) - return ramp_up(clients, hatch_stride, True) - - logger.info("Ramping down...") - hatch_stride = max((hatch_stride/2),precision) - clients -= hatch_stride - if clients > 0: - locust_runner.start_hatching(clients, locust_runner.hatch_rate) - else: - logger.warning("No responses met the ramping thresholds, check your ramp configuration, locustfile and \"--host\" address") - logger.info("RAMING STOPPED") - return - gevent.sleep(1) - - if hatch_rate: - locust_runner.hatch_rate = hatch_rate - if start_count > 0: - locust_runner.start_hatching(start_count, hatch_rate) - logger.info("RAMPING STARTED") - ramp_up(start_count, hatch_stride) \ No newline at end of file + percent=0.95, response_time_limit=2000, acceptable_fail=0.05, + precision=200, start_count=0, calibration_time=15): + + def ramp_up(clients, hatch_stride, boundery_found=False): + while True: + if locust_runner.state != STATE_HATCHING: + if locust_runner.num_clients >= max_locusts: + logger.info("Ramp up halted; Max locusts limit reached: %d" % max_locusts) + return ramp_down(clients, hatch_stride) + + gevent.sleep(calibration_time) + fail_ratio = RequestStats.sum_stats().fail_ratio + if fail_ratio > acceptable_fail: + logger.info("Ramp up halted; Acceptable fail ratio %d%% exceeded with fail ratio %d%%" % (acceptable_fail*100, fail_ratio*100)) + return ramp_down(clients, hatch_stride) + + p = current_percentile(percent) + if p >= response_time_limit: + logger.info("Ramp up halted; Percentile response times getting high: %d" % p) + return ramp_down(clients, hatch_stride) + + if boundery_found and hatch_stride <= precision: + logger.info("Sweet spot found! Ramping stopped at %i locusts" % (locust_runner.num_clients)) + return + + logger.info("Ramping up...") + if boundery_found: + hatch_stride = max((hatch_stride/2),precision) + clients += hatch_stride + locust_runner.start_hatching(clients, locust_runner.hatch_rate) + gevent.sleep(1) + + def ramp_down(clients, hatch_stride): + while True: + if locust_runner.state != STATE_HATCHING: + if locust_runner.num_clients < max_locusts: + gevent.sleep(calibration_time) + fail_ratio = RequestStats.sum_stats().fail_ratio + if fail_ratio <= acceptable_fail: + p = current_percentile(percent) + if p <= response_time_limit: + if hatch_stride <= precision: + logger.info("Sweet spot found! Ramping stopped at %i locusts" % (locust_runner.num_clients)) + return + + logger.info("Ramping up...") + hatch_stride = max((hatch_stride/2),precision) + clients += hatch_stride + locust_runner.start_hatching(clients, locust_runner.hatch_rate) + return ramp_up(clients, hatch_stride, True) + + logger.info("Ramping down...") + hatch_stride = max((hatch_stride/2),precision) + clients -= hatch_stride + if clients > 0: + locust_runner.start_hatching(clients, locust_runner.hatch_rate) + else: + logger.warning("No responses met the ramping thresholds, check your ramp configuration, locustfile and \"--host\" address") + logger.info("RAMING STOPPED") + return + gevent.sleep(1) + + if hatch_rate: + locust_runner.hatch_rate = hatch_rate + if start_count > 0: + locust_runner.start_hatching(start_count, hatch_rate) + logger.info("RAMPING STARTED") + ramp_up(start_count, hatch_stride) \ No newline at end of file From baad2fb089f1a091ffd263769fba4e8d2c3f8b05 Mon Sep 17 00:00:00 2001 From: Hugo Heyman Date: Sun, 13 May 2012 14:33:21 +0200 Subject: [PATCH 5/7] fixed import to match the new name of 'rampstats.py' -> 'ramping.py' --- locust/web.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/locust/web.py b/locust/web.py index a332f72b51..1f5e9c9701 100644 --- a/locust/web.py +++ b/locust/web.py @@ -11,7 +11,7 @@ import runners from runners import MasterLocustRunner -from rampstats import start_ramping +from ramping import start_ramping from locust.stats import RequestStats, median_from_dict from locust import version import gevent From 43ba72359350dd164b0291a3e4b39a5ef9a0d39d Mon Sep 17 00:00:00 2001 From: Hugo Heyman Date: Wed, 30 May 2012 11:07:58 +0200 Subject: [PATCH 6/7] Ramping is now imported only when ramping is used --- locust/main.py | 3 --- locust/web.py | 3 ++- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/locust/main.py b/locust/main.py index f800d6ddaa..87d84be257 100644 --- a/locust/main.py +++ b/locust/main.py @@ -382,9 +382,6 @@ def main(): runners.locust_runner = SlaveLocustRunner(locust_classes, options.hatch_rate, options.num_clients, num_requests=options.num_requests, host=options.host, master_host=options.master_host) main_greenlet = runners.locust_runner.greenlet - if options.ramp: - import ramping - if options.print_stats or (options.no_web and not options.slave): # spawn stats printing greenlet gevent.spawn(stats_printer) diff --git a/locust/web.py b/locust/web.py index 1f5e9c9701..b5a6405138 100644 --- a/locust/web.py +++ b/locust/web.py @@ -11,7 +11,6 @@ import runners from runners import MasterLocustRunner -from ramping import start_ramping from locust.stats import RequestStats, median_from_dict from locust import version import gevent @@ -70,6 +69,8 @@ def stop(): @app.route("/ramp", methods=["POST"]) def ramp(): + from ramping import start_ramping + init_clients = int(request.form["init_count"]) hatch_rate = int(request.form["hatch_rate"]) hatch_stride = int(request.form["hatch_stride"]) From 525f413e23d9a8332d319f56e5ed7930ca53b77e Mon Sep 17 00:00:00 2001 From: Hugo Heyman Date: Wed, 30 May 2012 11:33:28 +0200 Subject: [PATCH 7/7] Stats event listeners are now registered only during ramping --- locust/ramping.py | 28 ++++++++++++++++++---------- 1 file changed, 18 insertions(+), 10 deletions(-) diff --git a/locust/ramping.py b/locust/ramping.py index 15d49e88c8..925ee78ff2 100644 --- a/locust/ramping.py +++ b/locust/ramping.py @@ -33,7 +33,7 @@ def current_percentile(percent): else: return percentile(sorted(response_times), percent) -def on_request_success(_, _1, response_time, _2): +def on_request_success_ramping(_, _1, response_time, _2): if is_distributed: response_times.append(response_time) else: @@ -45,12 +45,12 @@ def on_request_success(_, _1, response_time, _2): for i in xrange(len(response_times) - int(math.ceil(rps*PERCENTILE_TIME_WINDOW))): response_times.popleft() -def on_report_to_master(_, data): +def on_report_to_master_ramping(_, data): global response_times data["current_responses"] = response_times response_times = [] -def on_slave_report(_, data): +def on_slave_report_ramping(_, data): if "current_responses" in data: response_times.append(data["current_responses"]) @@ -60,14 +60,22 @@ def on_slave_report(_, data): if len(response_times) > slaves * response_times_per_slave_count: response_times.popleft() -events.report_to_master += on_report_to_master -events.slave_report += on_slave_report -events.request_success += on_request_success +def register_listeners(): + events.report_to_master += on_report_to_master_ramping + events.slave_report += on_slave_report_ramping + events.request_success += on_request_success_ramping + +def remove_listeners(): + events.report_to_master.__idec__(on_report_to_master_ramping) + events.slave_report.__idec__(on_slave_report_ramping) + events.request_success.__idec__(on_request_success_ramping) def start_ramping(hatch_rate=None, max_locusts=1000, hatch_stride=100, percent=0.95, response_time_limit=2000, acceptable_fail=0.05, precision=200, start_count=0, calibration_time=15): - + + register_listeners() + def ramp_up(clients, hatch_stride, boundery_found=False): while True: if locust_runner.state != STATE_HATCHING: @@ -88,7 +96,7 @@ def ramp_up(clients, hatch_stride, boundery_found=False): if boundery_found and hatch_stride <= precision: logger.info("Sweet spot found! Ramping stopped at %i locusts" % (locust_runner.num_clients)) - return + return remove_listeners() logger.info("Ramping up...") if boundery_found: @@ -108,7 +116,7 @@ def ramp_down(clients, hatch_stride): if p <= response_time_limit: if hatch_stride <= precision: logger.info("Sweet spot found! Ramping stopped at %i locusts" % (locust_runner.num_clients)) - return + return remove_listeners() logger.info("Ramping up...") hatch_stride = max((hatch_stride/2),precision) @@ -124,7 +132,7 @@ def ramp_down(clients, hatch_stride): else: logger.warning("No responses met the ramping thresholds, check your ramp configuration, locustfile and \"--host\" address") logger.info("RAMING STOPPED") - return + return remove_listeners() gevent.sleep(1) if hatch_rate: