diff --git a/.github/CONTRIBUTING.md b/.github/CONTRIBUTING.md index 3bff532e59..16a05db55f 100644 --- a/.github/CONTRIBUTING.md +++ b/.github/CONTRIBUTING.md @@ -1,7 +1,7 @@ ## Release Process * Run github_changelog_generator to update `CHANGELOG.md` - - `github_changelog_generator -u Locustio -p Locust -t $CHANGELOG_GITHUB_TOKEN` + - `github_changelog_generator -t $CHANGELOG_GITHUB_TOKEN locustio/locust` * Add highlights to changelog in docs: `locust/docs/changelog.rst` * Update `locust/__init__.py` with new version number: `__version__ = "VERSION"` * Tag master as "VERSION" in git diff --git a/CHANGELOG.md b/CHANGELOG.md index 9e95daf151..fd371b5f40 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,46 +1,40 @@ # Change Log -## [Unreleased](https://github.com/locustio/locust/tree/HEAD) - -[Full Changelog](https://github.com/locustio/locust/compare/0.12.1...HEAD) +## [0.12.2](https://github.com/locustio/locust/tree/0.12.2) (2019-10-26) +[Full Changelog](https://github.com/locustio/locust/compare/0.12.1...0.12.2) **Fixed bugs:** +- Installing 0.12.1 requires "pipenv lock --pre" [\#1116](https://github.com/locustio/locust/issues/1116) - Strange behavior of "Total Requests per Second" chart [\#889](https://github.com/locustio/locust/issues/889) - Response time graph seems to be an average of all data [\#667](https://github.com/locustio/locust/issues/667) - Totals Clarity in Web Results [\#629](https://github.com/locustio/locust/issues/629) - on\_request\_xxx checks exiting condition against the wrong number [\#399](https://github.com/locustio/locust/issues/399) -- \[0.7.3\] Total fails percentage calculated wrong on console [\#384](https://github.com/locustio/locust/issues/384) +- [0.7.3] Total fails percentage calculated wrong on console [\#384](https://github.com/locustio/locust/issues/384) - URL names in dashboard are not HTML escaped [\#374](https://github.com/locustio/locust/issues/374) - Percentage of fails in Total line is greater than 100% [\#357](https://github.com/locustio/locust/issues/357) - Exceptions tab not working for on\_start method [\#269](https://github.com/locustio/locust/issues/269) - Percentile response time anomalies at 100% [\#254](https://github.com/locustio/locust/issues/254) +- log.py's StdErrWrapper swallows fatal stacktraces [\#163](https://github.com/locustio/locust/issues/163) - Slave count doesn't get updated in the UI if no more slaves are alive [\#62](https://github.com/locustio/locust/issues/62) +- Exit with code 1 on greenlet exceptions [\#665](https://github.com/locustio/locust/pull/665) ([jnschulze](https://github.com/jnschulze)) **Closed issues:** -- 0.12 released on GitHub but not on PyPI [\#1109](https://github.com/locustio/locust/issues/1109) -- Test execution is getting aborted due to Memory Error : \[SSL\] malloc failure \(\_ssl.c:2508\) [\#1107](https://github.com/locustio/locust/issues/1107) -- Assert failures within a task do not fail the current task. How to force a fail? [\#1063](https://github.com/locustio/locust/issues/1063) +- Samples with response\_time None crashes stats.py [\#1087](https://github.com/locustio/locust/issues/1087) +- Requests Per Second Plot Breaks When There are too Many Unique URLs [\#1059](https://github.com/locustio/locust/issues/1059) - UI 'stop' button does not stop test [\#1047](https://github.com/locustio/locust/issues/1047) - Performance degradation for constant wait time [\#1042](https://github.com/locustio/locust/issues/1042) -- Is there a way to tell how many users/locusts are currently executing tasks in python? [\#1035](https://github.com/locustio/locust/issues/1035) -- Custom Credentials Example Broken? [\#987](https://github.com/locustio/locust/issues/987) -- Execute `locust` command, "ImportError: DLL load failed" [\#928](https://github.com/locustio/locust/issues/928) -- Locust slows down over time [\#876](https://github.com/locustio/locust/issues/876) -- New release to PyPi for gevent 1.3 compatibility [\#793](https://github.com/locustio/locust/issues/793) +- Failure percentage being reported incorrectly [\#1006](https://github.com/locustio/locust/issues/1006) +- Start on\_stop not before on\_start has finished [\#969](https://github.com/locustio/locust/issues/969) +- Possible typo in docs [\#946](https://github.com/locustio/locust/issues/946) - Control time window for RPS calculation [\#792](https://github.com/locustio/locust/issues/792) - 500 URL limit [\#786](https://github.com/locustio/locust/issues/786) -- Save responses to a file [\#774](https://github.com/locustio/locust/issues/774) - custom client from locust documentation doesn't work at all [\#771](https://github.com/locustio/locust/issues/771) - separate charts for requests per second and average response time [\#688](https://github.com/locustio/locust/issues/688) - schedule\_task and data driven load test methodology [\#633](https://github.com/locustio/locust/issues/633) -- stop/interrupt weighting/logic for nested tasks that execute a single task [\#632](https://github.com/locustio/locust/issues/632) -- self interrupt for inline nested TaskSets? [\#631](https://github.com/locustio/locust/issues/631) -- Add new members to Committers Team in the Locust.io Organization [\#628](https://github.com/locustio/locust/issues/628) - locust's statistic collect N/A records [\#626](https://github.com/locustio/locust/issues/626) - how to make all locust users wait after executing on\_start method ? [\#611](https://github.com/locustio/locust/issues/611) -- Adding name argument in the http post call with catch response argument [\#608](https://github.com/locustio/locust/issues/608) - EventHook\(\) fired when locust user has stopped [\#604](https://github.com/locustio/locust/issues/604) - Is there a way to de-register slave with master on a slave node shutdown? [\#603](https://github.com/locustio/locust/issues/603) - Unable to Stop locust from Web interface occasionally [\#602](https://github.com/locustio/locust/issues/602) @@ -48,7 +42,6 @@ - Can you add or can I create a Pull Request to accept a command line option that would enable ALL events \(http requests\) to be logged to a file/location? [\#576](https://github.com/locustio/locust/issues/576) - Median response times off [\#565](https://github.com/locustio/locust/issues/565) - Dedicated Vuser for each API [\#564](https://github.com/locustio/locust/issues/564) -- 'module' object has no attribute 'NSIG' [\#518](https://github.com/locustio/locust/issues/518) - running-locust-distributed missing information on worker model [\#492](https://github.com/locustio/locust/issues/492) - locust executes more number of times than I expected [\#455](https://github.com/locustio/locust/issues/455) - Some uncertain for RPS [\#367](https://github.com/locustio/locust/issues/367) @@ -59,19 +52,27 @@ - Locust can not run distributed with the web interface disabled [\#189](https://github.com/locustio/locust/issues/189) - Documentation on how to best configure a \(linux\) machine to run locust [\#128](https://github.com/locustio/locust/issues/128) - See what request generated a failure [\#103](https://github.com/locustio/locust/issues/103) +- Support for plugins [\#34](https://github.com/locustio/locust/issues/34) **Merged pull requests:** +- fix self.client call in code examples [\#1123](https://github.com/locustio/locust/pull/1123) ([cyberw](https://github.com/cyberw)) +- Escape HTML entities in endpoint names \#374 [\#1119](https://github.com/locustio/locust/pull/1119) ([peterdemin](https://github.com/peterdemin)) +- Revert "Exit with code 1 on greenlet exceptions" [\#1115](https://github.com/locustio/locust/pull/1115) ([heyman](https://github.com/heyman)) +- Table layout fix to use available space better [\#1114](https://github.com/locustio/locust/pull/1114) ([heyman](https://github.com/heyman)) +- Fix rounding error when spawning users from multiple locust classes [\#1113](https://github.com/locustio/locust/pull/1113) ([heyman](https://github.com/heyman)) - Add \_\_main\_\_.py file [\#1112](https://github.com/locustio/locust/pull/1112) ([jdufresne](https://github.com/jdufresne)) - Remove 'dist: xenial' from Travis configuration [\#1111](https://github.com/locustio/locust/pull/1111) ([jdufresne](https://github.com/jdufresne)) - Add Python 3.8 to the test matrix [\#1110](https://github.com/locustio/locust/pull/1110) ([jdufresne](https://github.com/jdufresne)) - Fix empty bytearray\(b''\) returned when using catch\_response=True [\#1105](https://github.com/locustio/locust/pull/1105) ([skivis](https://github.com/skivis)) +- Add an option \(--stop-timeout\) to allow tasks to finish running their iteration before exiting [\#1099](https://github.com/locustio/locust/pull/1099) ([cyberw](https://github.com/cyberw)) +- Allow None response time for requests [\#1088](https://github.com/locustio/locust/pull/1088) ([cyberw](https://github.com/cyberw)) - Fixed issue with Total Requests Per Second plot [\#1060](https://github.com/locustio/locust/pull/1060) ([williamlhunter](https://github.com/williamlhunter)) +- Tox: Add flake8 tests to find Python syntax errors and undefined names [\#1039](https://github.com/locustio/locust/pull/1039) ([cclauss](https://github.com/cclauss)) - Fix frontend bugs. [\#822](https://github.com/locustio/locust/pull/822) ([omittones](https://github.com/omittones)) -- Update all pypi.python.org URLs to pypi.org [\#818](https://github.com/locustio/locust/pull/818) ([jdufresne](https://github.com/jdufresne)) -- Update third-party-tools.rst [\#808](https://github.com/locustio/locust/pull/808) ([anhldbk](https://github.com/anhldbk)) - Switch from using optparse to argparse for command line arguments [\#769](https://github.com/locustio/locust/pull/769) ([jdufresne](https://github.com/jdufresne)) - Allow skipping the logging setup [\#738](https://github.com/locustio/locust/pull/738) ([Exide](https://github.com/Exide)) +- Added link to an Ansible role as a 3rd party tool. [\#704](https://github.com/locustio/locust/pull/704) ([tinx](https://github.com/tinx)) ## [0.12.1](https://github.com/locustio/locust/tree/0.12.1) (2019-10-18) [Full Changelog](https://github.com/locustio/locust/compare/0.12.0...0.12.1) @@ -83,10 +84,6 @@ **Closed issues:** - Throughput \(RPS\) value is not same in Locust WEBUI and http://localhost:8089/stats/requests/csv for same number of requests [\#1108](https://github.com/locustio/locust/issues/1108) -- Facing 'NewConnectionError' while installing steup wheel in windows [\#1106](https://github.com/locustio/locust/issues/1106) -- Disable SSL: CERITIFICATE\_VERIFY\_FAILED [\#1104](https://github.com/locustio/locust/issues/1104) -- Can't run locust on the remote server [\#1103](https://github.com/locustio/locust/issues/1103) -- How do i control diving into nested tasksets? [\#1097](https://github.com/locustio/locust/issues/1097) **Merged pull requests:** @@ -105,7 +102,6 @@ **Closed issues:** - Distributed load test k8s and openshift [\#1100](https://github.com/locustio/locust/issues/1100) -- http return code as 0 [\#1098](https://github.com/locustio/locust/issues/1098) - Official docker image does not actually exist [\#1092](https://github.com/locustio/locust/issues/1092) - Connection Refused for http://localhost:8089/stats/requests in Locust non web UI mode [\#1086](https://github.com/locustio/locust/issues/1086) - Sequence does not get past first nested sequence. [\#1080](https://github.com/locustio/locust/issues/1080) @@ -274,12 +270,12 @@ - Support horizontal scrolling for the stats table [\#938](https://github.com/locustio/locust/pull/938) ([mingrammer](https://github.com/mingrammer)) - Remove repeated imports of mock [\#936](https://github.com/locustio/locust/pull/936) ([Jonnymcc](https://github.com/Jonnymcc)) - Speed up task sequence tests [\#935](https://github.com/locustio/locust/pull/935) ([Jonnymcc](https://github.com/Jonnymcc)) -- Add heartbeat to detect down slaves [\#927](https://github.com/locustio/locust/pull/927) ([Jonnymcc](https://github.com/Jonnymcc)) - clarifying locust class usage when no classes specified on CLI [\#925](https://github.com/locustio/locust/pull/925) ([smadness](https://github.com/smadness)) - Include LICENSE in the sdist. [\#919](https://github.com/locustio/locust/pull/919) ([benjaminp](https://github.com/benjaminp)) - fix About link [\#914](https://github.com/locustio/locust/pull/914) ([cgoldberg](https://github.com/cgoldberg)) - Fixed \#903 to allow requests/sec UI column to display. [\#908](https://github.com/locustio/locust/pull/908) ([devmonkey22](https://github.com/devmonkey22)) - Update browse\_docs\_sequence\_test [\#904](https://github.com/locustio/locust/pull/904) ([Realsid](https://github.com/Realsid)) +- Use uuid4 to generate slave client\_id [\#895](https://github.com/locustio/locust/pull/895) ([mattbailey](https://github.com/mattbailey)) - Python37 [\#885](https://github.com/locustio/locust/pull/885) ([cgoldberg](https://github.com/cgoldberg)) - Official Docker image and documentation V2 [\#882](https://github.com/locustio/locust/pull/882) ([spayeur207](https://github.com/spayeur207)) - Fix links to changelog in changelog [\#877](https://github.com/locustio/locust/pull/877) ([dmand](https://github.com/dmand)) @@ -392,6 +388,7 @@ - Installation failure on Mac OS 10.12.4 [\#582](https://github.com/locustio/locust/issues/582) - Specify host header and send request against IP [\#581](https://github.com/locustio/locust/issues/581) - Ability to set a specific number of simulated users per Locust class [\#575](https://github.com/locustio/locust/issues/575) +- Slave hangs when started before master [\#571](https://github.com/locustio/locust/issues/571) - Is there a way to setup/teardown before running the load tests [\#553](https://github.com/locustio/locust/issues/553) - Never loads? [\#302](https://github.com/locustio/locust/issues/302) - Retrieving stats when running with --no-web [\#290](https://github.com/locustio/locust/issues/290) @@ -404,7 +401,7 @@ **Merged pull requests:** -- Use uuid4 to generate slave client\_id [\#895](https://github.com/locustio/locust/pull/895) ([mattbailey](https://github.com/mattbailey)) +- Add heartbeat to detect down slaves [\#927](https://github.com/locustio/locust/pull/927) ([Jonnymcc](https://github.com/Jonnymcc)) - update Makefile so build also creates a wheel [\#871](https://github.com/locustio/locust/pull/871) ([cgoldberg](https://github.com/cgoldberg)) - Fix deprecation warnings [\#870](https://github.com/locustio/locust/pull/870) ([cgoldberg](https://github.com/cgoldberg)) - Release 0.9.0 [\#867](https://github.com/locustio/locust/pull/867) ([aldenpeterson-wf](https://github.com/aldenpeterson-wf)) @@ -416,6 +413,8 @@ - response time doesn't need to be cast to int, as this is implicit in … [\#830](https://github.com/locustio/locust/pull/830) ([efology](https://github.com/efology)) - Add tasks sequence support [\#827](https://github.com/locustio/locust/pull/827) ([Ramshell](https://github.com/Ramshell)) - Fix some typos in events.py [\#820](https://github.com/locustio/locust/pull/820) ([felixonmars](https://github.com/felixonmars)) +- Update all pypi.python.org URLs to pypi.org [\#818](https://github.com/locustio/locust/pull/818) ([jdufresne](https://github.com/jdufresne)) +- Update third-party-tools.rst [\#808](https://github.com/locustio/locust/pull/808) ([anhldbk](https://github.com/anhldbk)) - Remove unused nosetest settings [\#806](https://github.com/locustio/locust/pull/806) ([cgoldberg](https://github.com/cgoldberg)) - Drop Python 3.3 support [\#804](https://github.com/locustio/locust/pull/804) ([ps-george](https://github.com/ps-george)) - docs: Syntax highlight code and commands [\#797](https://github.com/locustio/locust/pull/797) ([joar](https://github.com/joar)) @@ -472,7 +471,6 @@ - Infinite recursion error when testing https sites [\#655](https://github.com/locustio/locust/issues/655) - website SSL [\#644](https://github.com/locustio/locust/issues/644) -- Slave hangs when started before master [\#571](https://github.com/locustio/locust/issues/571) - Using locust to query Cassandra [\#569](https://github.com/locustio/locust/issues/569) **Merged pull requests:** @@ -646,6 +644,8 @@ - Add charts for RPS and average response time in the WebUI [\#509](https://github.com/locustio/locust/pull/509) ([myzhan](https://github.com/myzhan)) - docs: clarify locust invocation norms [\#501](https://github.com/locustio/locust/pull/501) ([pwnage101](https://github.com/pwnage101)) - Improve the language in writing-a-locustfile.rst [\#470](https://github.com/locustio/locust/pull/470) ([aknuds1](https://github.com/aknuds1)) +- Typo fix in locust API documentation [\#448](https://github.com/locustio/locust/pull/448) ([frntn](https://github.com/frntn)) +- Adds host name to the header [\#447](https://github.com/locustio/locust/pull/447) ([thaffenden](https://github.com/thaffenden)) - Allow --no-web together with --master for automation [\#333](https://github.com/locustio/locust/pull/333) ([undera](https://github.com/undera)) ## [v0.8a1](https://github.com/locustio/locust/tree/v0.8a1) (2016-11-24) @@ -709,8 +709,6 @@ - Fix document bug concerning '--host' option. [\#460](https://github.com/locustio/locust/pull/460) ([d6e](https://github.com/d6e)) - Python 3 compatibility with --no-web option [\#456](https://github.com/locustio/locust/pull/456) ([mrsanders](https://github.com/mrsanders)) - Changes to consolidate errors [\#451](https://github.com/locustio/locust/pull/451) ([schuSF](https://github.com/schuSF)) -- Typo fix in locust API documentation [\#448](https://github.com/locustio/locust/pull/448) ([frntn](https://github.com/frntn)) -- Adds host name to the header [\#447](https://github.com/locustio/locust/pull/447) ([thaffenden](https://github.com/thaffenden)) - Python 3 compatibility for slave mode. \(Fixes 'locust --slave'\) [\#443](https://github.com/locustio/locust/pull/443) ([mrsanders](https://github.com/mrsanders)) - Python 3 support [\#363](https://github.com/locustio/locust/pull/363) ([pmdarrow](https://github.com/pmdarrow)) @@ -802,7 +800,6 @@ - Fix handler argument names. [\#314](https://github.com/locustio/locust/pull/314) ([doctoryes](https://github.com/doctoryes)) - Update writing-a-locustfile.rst [\#306](https://github.com/locustio/locust/pull/306) ([reduxionist](https://github.com/reduxionist)) - Introduce docs for increasing the max number of open files limit [\#298](https://github.com/locustio/locust/pull/298) ([ericandrewlewis](https://github.com/ericandrewlewis)) -- add host cli arg to quickstart [\#250](https://github.com/locustio/locust/pull/250) ([groovecoder](https://github.com/groovecoder)) ## [v0.7.3](https://github.com/locustio/locust/tree/v0.7.3) (2015-05-30) [Full Changelog](https://github.com/locustio/locust/compare/v0.7.2...v0.7.3) @@ -843,12 +840,14 @@ - Update writing-a-locustfile.rst [\#268](https://github.com/locustio/locust/pull/268) ([cristopherchacon](https://github.com/cristopherchacon)) - changed how request\_meta\["method"\] is set [\#267](https://github.com/locustio/locust/pull/267) ([dantagg](https://github.com/dantagg)) - list supported Python versions [\#261](https://github.com/locustio/locust/pull/261) ([cgoldberg](https://github.com/cgoldberg)) +- add host cli arg to quickstart [\#250](https://github.com/locustio/locust/pull/250) ([groovecoder](https://github.com/groovecoder)) - Update what-is-locust.rst [\#247](https://github.com/locustio/locust/pull/247) ([frvi](https://github.com/frvi)) - Fixed typo in the quickstart doc. [\#245](https://github.com/locustio/locust/pull/245) ([hirokiky](https://github.com/hirokiky)) - Fixed link to ESN's Twitter page [\#227](https://github.com/locustio/locust/pull/227) ([gentlecat](https://github.com/gentlecat)) - Fix a missing backtick [\#221](https://github.com/locustio/locust/pull/221) ([chrisramsay](https://github.com/chrisramsay)) - Fix typo in docs [\#216](https://github.com/locustio/locust/pull/216) ([gregeinfrank](https://github.com/gregeinfrank)) - Typos in docs. [\#193](https://github.com/locustio/locust/pull/193) ([jfacorro](https://github.com/jfacorro)) +- recieve -\> receive; locsutfile -\> locustfile [\#183](https://github.com/locustio/locust/pull/183) ([stevetjoa](https://github.com/stevetjoa)) ## [v0.7.2](https://github.com/locustio/locust/tree/v0.7.2) (2014-09-25) [Full Changelog](https://github.com/locustio/locust/compare/v0.7.1...v0.7.2) @@ -957,7 +956,6 @@ **Merged pull requests:** -- recieve -\> receive; locsutfile -\> locustfile [\#183](https://github.com/locustio/locust/pull/183) ([stevetjoa](https://github.com/stevetjoa)) - fix typo [\#117](https://github.com/locustio/locust/pull/117) ([sanga](https://github.com/sanga)) - fix module and variable name clash \(traceback refers to a mod so it's a ... [\#115](https://github.com/locustio/locust/pull/115) ([sanga](https://github.com/sanga)) - Removes duplicate attribute documentation [\#106](https://github.com/locustio/locust/pull/106) ([djoume](https://github.com/djoume)) @@ -977,7 +975,6 @@ - Use shutdown function when num\_requests are done [\#60](https://github.com/locustio/locust/pull/60) ([afajl](https://github.com/afajl)) - Update docs/api.rst [\#55](https://github.com/locustio/locust/pull/55) ([cbrinley](https://github.com/cbrinley)) - Added argument to options parser indicating on which port to run the web UI [\#54](https://github.com/locustio/locust/pull/54) ([manova](https://github.com/manova)) -- Refactoring \(separation\) of ramping code; Added tooltips for ramping form in ui [\#28](https://github.com/locustio/locust/pull/28) ([HeyHugo](https://github.com/HeyHugo)) ## [v0.6.2](https://github.com/locustio/locust/tree/v0.6.2) (2013-01-10) [Full Changelog](https://github.com/locustio/locust/compare/v0.6.1...v0.6.2) @@ -1023,6 +1020,7 @@ - Quickstart example was missing import task [\#41](https://github.com/locustio/locust/pull/41) ([natancox](https://github.com/natancox)) - Use python-requests as HTTP client in Locust [\#40](https://github.com/locustio/locust/pull/40) ([heyman](https://github.com/heyman)) - Addfix [\#36](https://github.com/locustio/locust/pull/36) ([jukart](https://github.com/jukart)) +- Fixed CSV stats export order [\#23](https://github.com/locustio/locust/pull/23) ([quosa](https://github.com/quosa)) ## [v0.5.1](https://github.com/locustio/locust/tree/v0.5.1) (2012-07-01) [Full Changelog](https://github.com/locustio/locust/compare/v0.5...v0.5.1) @@ -1041,9 +1039,9 @@ **Merged pull requests:** +- Refactoring \(separation\) of ramping code; Added tooltips for ramping form in ui [\#28](https://github.com/locustio/locust/pull/28) ([HeyHugo](https://github.com/HeyHugo)) - Support Basic HTTP Authorization for https requests [\#27](https://github.com/locustio/locust/pull/27) ([corbinbs](https://github.com/corbinbs)) - Add content-disposition with a filename. Fix missing import \(warnings\). [\#24](https://github.com/locustio/locust/pull/24) ([benjaminws](https://github.com/benjaminws)) -- Fixed CSV stats export order [\#23](https://github.com/locustio/locust/pull/23) ([quosa](https://github.com/quosa)) - request timing csv endpoint was returning Internal Error [\#22](https://github.com/locustio/locust/pull/22) ([pedronis](https://github.com/pedronis)) - fix continuous resetting on of stats in master+slaves mode [\#19](https://github.com/locustio/locust/pull/19) ([pedronis](https://github.com/pedronis)) @@ -1067,4 +1065,4 @@ -\* *This Change Log was automatically generated by [github_changelog_generator](https://github.com/skywinder/Github-Changelog-Generator)* \ No newline at end of file +\* *This Change Log was automatically generated by [github_changelog_generator](https://github.com/skywinder/Github-Changelog-Generator)* diff --git a/docs/changelog.rst b/docs/changelog.rst index 7501935ce5..cc8e7785be 100644 --- a/docs/changelog.rst +++ b/docs/changelog.rst @@ -4,12 +4,26 @@ Changelog Highlights For full details of the Locust changelog, please see https://github.com/locustio/locust/blob/master/CHANGELOG.md +0.12.2 +========== + +* Added `--skip-log-setup` to disable Locust's default logging setup. +* Allow custom clients to set request response time to None. Those requests will be excluded + when calculating median, average, min, max and percentile response times. +* Renamed the last row in statistics table from "Total" to "Aggregated" (since the values aren't + a sum of the individual table rows). +* Some visual improvements to the web UI. +* Fixed issue with simulating fewer number of locust users than the number of slave/worker nodes. +* Fixed bugs in the web UI related to the fact that the stats table is truncated at 500 entries. +* Various other bug fixes and improvements. + + 0.12.1 ====== -* Added new :code:`FastHttpLocust` class that uses a faster HTTP client, which should be 5-6 times faster +* Added new :code:`FastHttpLocust` class that uses a faster HTTP client, which should be 5-6 times faster than the normal :code:`HttpLocust` class. For more info see the documentation on :ref:`increasing performance `. -* Added ability to set the exit code of the locust process when exceptions has occurred within the user code, +* Added ability to set the exit code of the locust process when exceptions has occurred within the user code, using the :code:`--exit-code-on-error` parameter. * Added TCP keep alive to master/slave communication sockets to avoid broken connections in some environments. * Dropped support for Python 3.4 @@ -20,7 +34,7 @@ For full details of the Locust changelog, please see https://github.com/locustio ====== * Python 3.7 support -* Added a status page to the web UI when running Locust distributed showing the status of slave nodes +* Added a status page to the web UI when running Locust distributed showing the status of slave nodes and detect down slaves using heartbeats * Numerous bugfixes/documentation updates (see detailed changelog) @@ -32,8 +46,8 @@ For full details of the Locust changelog, please see https://github.com/locustio * Numerous bugfixes (see detailed changelog) * Added sequential task support - https://github.com/locustio/locust/pull/827 * Added support for user-defined wait_function - https://github.com/locustio/locust/pull/785 -* By default, Locust no longer resets the statistics when the hatching is complete. - Therefore :code:`--no-reset-stats` has been deprected (since it's now the default behaviour), +* By default, Locust no longer resets the statistics when the hatching is complete. + Therefore :code:`--no-reset-stats` has been deprected (since it's now the default behaviour), and instead a new :code:`--reset-stats` option has been added. * Dropped support for Python 3.3 * Updated documentation @@ -50,14 +64,14 @@ For full details of the Locust changelog, please see https://github.com/locustio * Python 3 support * Dropped support for Python 2.6 -* Added :code:`--no-reset-stats` option for controling if the statistics should be reset once +* Added :code:`--no-reset-stats` option for controling if the statistics should be reset once the hatching is complete -* Added charts to the web UI for requests per second, average response time, and number of +* Added charts to the web UI for requests per second, average response time, and number of simulated users. * Updated the design of the web UI. * Added ability to write a CSV file for results via command line flag * Added the URL of the host that is currently being tested to the web UI. -* We now also apply gevent's monkey patching of threads. This fixes an issue when +* We now also apply gevent's monkey patching of threads. This fixes an issue when using Locust to test Cassandra (https://github.com/locustio/locust/issues/569). * Various bug fixes and improvements @@ -71,7 +85,7 @@ For full details of the Locust changelog, please see https://github.com/locustio 0.7.4 ===== -* Use a newer version of requests, which fixed an issue for users with older versions of +* Use a newer version of requests, which fixed an issue for users with older versions of requests getting ConnectionErrors (https://github.com/locustio/locust/issues/273). * Various fixes to documentation. @@ -79,9 +93,9 @@ For full details of the Locust changelog, please see https://github.com/locustio 0.7.3 ===== -* Fixed bug where POST requests (and other methods as well) got incorrectly reported as +* Fixed bug where POST requests (and other methods as well) got incorrectly reported as GET requests, if the request resulted in a redirect. -* Added ability to download exceptions in CSV format. Download links has also been moved +* Added ability to download exceptions in CSV format. Download links has also been moved to it's own tab in the web UI. @@ -89,16 +103,16 @@ For full details of the Locust changelog, please see https://github.com/locustio ===== * Locust now returns an exit code of 1 when any failed requests were reported. -* When making an HTTP request to an endpoint that responds with a redirect, the original - URL that was requested is now used as the name for that entry in the statistics (unless - an explicit override is specified through the *name* argument). Previously, the last +* When making an HTTP request to an endpoint that responds with a redirect, the original + URL that was requested is now used as the name for that entry in the statistics (unless + an explicit override is specified through the *name* argument). Previously, the last URL in the redirect chain was used to label the request(s) in the statistics. -* Fixed bug which caused only the time of the last request in a redirect chain to be +* Fixed bug which caused only the time of the last request in a redirect chain to be included in the reported time. -* Fixed bug which caused the download time of the request body not to be included in the - reported response time. -* Fixed bug that occurred on some linux dists that were tampering with the python-requests - system package (removing dependencies which requests is bundling). This bug only occured +* Fixed bug which caused the download time of the request body not to be included in the + reported response time. +* Fixed bug that occurred on some linux dists that were tampering with the python-requests + system package (removing dependencies which requests is bundling). This bug only occured when installing Locust in the python system packages, and not when using virtualenv. * Various minor fixes and improvements. @@ -110,7 +124,7 @@ For full details of the Locust changelog, please see https://github.com/locustio * Fixed bug which caused Min response time to always be 0 after all locusts had been hatched and the statistics had been reset. * Minor UI improvements in the web interface. -* Handle messages from "zombie" slaves by ignoring the message and making a log entry +* Handle messages from "zombie" slaves by ignoring the message and making a log entry in the master process. @@ -121,31 +135,31 @@ For full details of the Locust changelog, please see https://github.com/locustio HTTP client functionality moved to HttpLocust --------------------------------------------- -Previously, the Locust class instantiated a :py:class:`HttpSession ` -under the client attribute that was used to make HTTP requests. This funcionality has -now been moved into the :py:class:`HttpLocust ` class, in an -effort to make it more obvious how one can use Locust to +Previously, the Locust class instantiated a :py:class:`HttpSession ` +under the client attribute that was used to make HTTP requests. This funcionality has +now been moved into the :py:class:`HttpLocust ` class, in an +effort to make it more obvious how one can use Locust to :doc:`load test non-HTTP systems `. -To make existing locust scripts compatible with the new version you should make your +To make existing locust scripts compatible with the new version you should make your locust classes inherit from HttpLocust instead of the base Locust class. msgpack for serializing master/slave data ----------------------------------------- -Locust now uses `msgpack `_ for serializing data that is sent between -a master node and it's slaves. This adresses a possible attack that can be used to execute -code remote, if one has access to the internal locust ports that are used for master-slave -communication. The reason for this exploit was due to the fact that pickle was used. +Locust now uses `msgpack `_ for serializing data that is sent between +a master node and it's slaves. This adresses a possible attack that can be used to execute +code remote, if one has access to the internal locust ports that are used for master-slave +communication. The reason for this exploit was due to the fact that pickle was used. .. warning:: - Anyone who uses an older version should make sure that their Locust machines are not publicly + Anyone who uses an older version should make sure that their Locust machines are not publicly accessible on port 5557 and 5558. Also, one should never run Locust as root. -Anyone who uses the :py:class:`report_to_master ` and -:py:class:`slave_report ` events, needs to make sure that +Anyone who uses the :py:class:`report_to_master ` and +:py:class:`slave_report ` events, needs to make sure that any data that is attached to the slave reports is serializable by msgpack. requests updated to version 2.2 @@ -194,19 +208,19 @@ Locust Event hooks now takes keyword argument --------------------------------------------- When :doc:`extending-locust` by listening to :ref:`events`, the listener functions should now expect -the arguments to be passed in as keyword arguments. It's also highly recommended to add an extra -wildcard keyword arguments to listener functions, since they're then less likely to break if extra +the arguments to be passed in as keyword arguments. It's also highly recommended to add an extra +wildcard keyword arguments to listener functions, since they're then less likely to break if extra arguments are added to that event in some future version. For example:: from locust import events - + def on_request(request_type, name, response_time, response_length, **kw): print "Got request!" - + locust.events.request_success += on_request -The *method* and *path* arguments to :py:obj:`request_success ` and -:py:obj:`request_failure ` are now called *request_type* and *name*, +The *method* and *path* arguments to :py:obj:`request_success ` and +:py:obj:`request_failure ` are now called *request_type* and *name*, since it's less HTTP specific. @@ -231,10 +245,10 @@ Other changes 0.6.2 ===== -* Made Locust compatible with gevent 1.0rc2. This allows user to step around a problem - with running Locust under some versions of CentOS, that can be fixed by upgrading +* Made Locust compatible with gevent 1.0rc2. This allows user to step around a problem + with running Locust under some versions of CentOS, that can be fixed by upgrading gevent to 1.0. -* Added :py:attr:`parent ` attribute to TaskSet class that +* Added :py:attr:`parent ` attribute to TaskSet class that refers to the parent TaskSet, or Locust, instance. Contributed by Aaron Daubman. @@ -250,18 +264,18 @@ Other changes .. warning:: - This version comes with non backward compatible changes to the API. + This version comes with non backward compatible changes to the API. Anyone who is currently using existing locust scripts and want to upgrade to 0.6 - should read through these changes. + should read through these changes. :py:class:`SubLocust ` replaced by :py:class:`TaskSet ` and :py:class:`Locust ` class behaviour changed ----------------------------------------------------------------------------------------------------------------------------------------------------------------------- -:py:class:`Locust ` classes does no longer control task scheduling and execution. -Therefore, you no longer define tasks within Locust classes, instead the Locust class has a -:py:attr:`task_set ` attribute which should point to a -:py:class:`TaskSet ` class. Tasks should now be defined in TaskSet -classes, in the same way that was previously done in Locust and SubLocust classes. TaskSets can be +:py:class:`Locust ` classes does no longer control task scheduling and execution. +Therefore, you no longer define tasks within Locust classes, instead the Locust class has a +:py:attr:`task_set ` attribute which should point to a +:py:class:`TaskSet ` class. Tasks should now be defined in TaskSet +classes, in the same way that was previously done in Locust and SubLocust classes. TaskSets can be nested just like SubLocust classes could. So the following code for 0.5.1:: @@ -269,27 +283,27 @@ So the following code for 0.5.1:: class User(Locust): min_wait = 10000 max_wait = 120000 - + @task(10) def index(self): self.client.get("/") - + @task(2) class AboutPage(SubLocust): min_wait = 10000 max_wait = 120000 - + def on_init(self): self.client.get("/about/") - + @task def team_page(self): self.client.get("/about/team/") - + @task def press_page(self): self.client.get("/about/press/") - + @task def stop(self): self.interrupt() @@ -300,46 +314,46 @@ Should now be written like:: @task(10) def index(self): self.client.get("/") - + @task(2) class AboutPage(TaskSet): def on_init(self): self.client.get("/about/") - + @task def team_page(self): self.client.get("/about/team/") - + @task def press_page(self): self.client.get("/about/press/") - + @task def stop(self): self.interrupt() - + class User(Locust): min_wait = 10000 max_wait = 120000 task_set = BrowsePage -Each TaskSet instance gets a :py:attr:`locust ` attribute, which refers to the +Each TaskSet instance gets a :py:attr:`locust ` attribute, which refers to the Locust class. - + Locust now uses Requests ------------------------ -Locust's own HttpBrowser class (which was typically accessed through *self.client* from within a locust class) -has been replaced by a thin wrapper around the requests library (http://python-requests.org). This comes with -a number of advantages. Users can now take advantage of a well documented, well written, fully fledged -library for making HTTP requests. However, it also comes with some small API changes wich will require users +Locust's own HttpBrowser class (which was typically accessed through *self.client* from within a locust class) +has been replaced by a thin wrapper around the requests library (http://python-requests.org). This comes with +a number of advantages. Users can now take advantage of a well documented, well written, fully fledged +library for making HTTP requests. However, it also comes with some small API changes wich will require users to update their existing load testing scripts. Gzip encoding turned on by default ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -The HTTP client now sends headers for accepting gzip encoding by default. The **--gzip** command line argument -has been removed and if someone want to disable the *Accept-Encoding* that the HTTP client uses, or any +The HTTP client now sends headers for accepting gzip encoding by default. The **--gzip** command line argument +has been removed and if someone want to disable the *Accept-Encoding* that the HTTP client uses, or any other HTTP headers you can do:: class MyWebUser(Locust): @@ -350,53 +364,53 @@ other HTTP headers you can do:: Improved HTTP client ^^^^^^^^^^^^^^^^^^^^ -Because of the switch to using python-requests in the HTTP client, the API for the client has also +Because of the switch to using python-requests in the HTTP client, the API for the client has also gotten a few changes. -* Additionally to the :py:meth:`get `, :py:meth:`post `, - :py:meth:`put `, :py:meth:`delete ` and - :py:meth:`head ` methods, the :py:class:`HttpSession ` class +* Additionally to the :py:meth:`get `, :py:meth:`post `, + :py:meth:`put `, :py:meth:`delete ` and + :py:meth:`head ` methods, the :py:class:`HttpSession ` class now also has :py:meth:`patch ` and :py:meth:`options ` methods. * All arguments to the HTTP request methods, except for **url** and **data** should now be specified as keyword arguments. For example, previously one could specify headers using:: - + client.get("/path", {"User-Agent":"locust"}) # this will no longer work - + And should now be specified like:: - + client.get("/path", headers={"User-Agent":"locust"}) * In general the whole HTTP client is now more powerful since it leverages on python-requests. Features that we're now able to use in Locust includes file upload, SSL, connection keep-alive, and more. See the `python-requests documentation `_ for more details. -* The new :py:class:`HttpSession ` class' methods now return python-request - :py:class:`Response ` objects. This means that accessing the content of the response - is no longer made using the **data** attribute, but instead the **content** attribute. The HTTP response +* The new :py:class:`HttpSession ` class' methods now return python-request + :py:class:`Response ` objects. This means that accessing the content of the response + is no longer made using the **data** attribute, but instead the **content** attribute. The HTTP response code is now accessed through the **status_code** attribute, instead of the **code** attribute. HttpSession methods' catch_response argument improved and allow_http_error argument removed ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ * When doing HTTP requests using the **catch_response** argument, the context manager that is returned now - provides two functions, :py:meth:`success ` and - :py:meth:`failure ` that can be used to manually control + provides two functions, :py:meth:`success ` and + :py:meth:`failure ` that can be used to manually control what the request should be reported as in Locust's statistics. - + .. autoclass:: locust.clients.ResponseContextManager :members: success, failure :noindex: -* The **allow_http_error** argument of the HTTP client's methods has been removed. Instead one can use the +* The **allow_http_error** argument of the HTTP client's methods has been removed. Instead one can use the **catch_response** argument to get a context manager, which can be used together with a with statement. - + The following code in the previous Locust version:: - + client.get("/does/not/exist", allow_http_error=True) - + Can instead now be written like:: - + with client.get("/does/not/exist", catch_response=True) as response: response.success() @@ -405,7 +419,7 @@ Other improvements and bug fixes -------------------------------- * Scheduled task callables can now take keyword arguments and not only normal function arguments. -* SubLocust classes that are scheduled using :func:`locust.core.Locust.schedule_task` can now take +* SubLocust classes that are scheduled using :func:`locust.core.Locust.schedule_task` can now take arguments and keyword arguments (available in *self.args* and *self.kwargs*). * Fixed bug where the average content size would be zero when doing requests against a server that didn't set the content-length header (i.e. server that uses *Transfer-Encoding: chunked*) @@ -415,22 +429,22 @@ Other improvements and bug fixes Smaller API Changes ------------------- -* The *require_once* decorator has been removed. It was an old legacy function that no longer fit into - the current way of writing Locust tests, where tasks are either methods under a Locust class or SubLocust +* The *require_once* decorator has been removed. It was an old legacy function that no longer fit into + the current way of writing Locust tests, where tasks are either methods under a Locust class or SubLocust classes containing task methods. * Changed signature of :func:`locust.core.Locust.schedule_task`. Previously all extra arguments that was given to the method was passed on to the task when it was called. It no longer accepts extra arguments. - Instead, it takes an *args* argument (list) and a *kwargs* argument (dict) which are be passed to the task when + Instead, it takes an *args* argument (list) and a *kwargs* argument (dict) which are be passed to the task when it's called. -* Arguments for :py:class:`request_success ` event hook has been changed. - Previously it took an HTTP Response instance as argument, but this has been changed to take the +* Arguments for :py:class:`request_success ` event hook has been changed. + Previously it took an HTTP Response instance as argument, but this has been changed to take the content-length of the response instead. This makes it easier to write custom clients for Locust. 0.5.1 ===== -* Fixed bug which caused --logfile and --loglevel command line parameters to not be respected when running +* Fixed bug which caused --logfile and --loglevel command line parameters to not be respected when running locust without zeromq. 0.5 @@ -450,7 +464,7 @@ Improvements and bug fixes "brain". * The HTTP method used when a client requests a URL is now displayed in the web UI * Some fixes and improvements in the stats exporting: - + * A file name is now set (using content-disposition header) when downloading stats. * The order of the column headers for request stats was wrong. * Thanks Benjamin W. Smith, Jussi Kuosa and Samuele Pedroni! @@ -461,9 +475,9 @@ Improvements and bug fixes API changes ----------- -* WebLocust class has been deprecated and is now called just Locust. The class that was previously +* WebLocust class has been deprecated and is now called just Locust. The class that was previously called Locust is now called LocustBase. -* The *catch_http_error* argument to HttpClient.get() and HttpClient.post() has been renamed to +* The *catch_http_error* argument to HttpClient.get() and HttpClient.post() has been renamed to *allow_http_error*. Improvements and bug fixes diff --git a/docs/running-locust-distributed.rst b/docs/running-locust-distributed.rst index 1299949e7f..c9071056d6 100644 --- a/docs/running-locust-distributed.rst +++ b/docs/running-locust-distributed.rst @@ -20,6 +20,16 @@ processor core, on the slave machines. Both the master and each slave machine, must have a copy of the locust test scripts when running Locust distributed. +.. note:: + It's recommended that you start a number of simulated users that are greater than + ``number of locust classes * number of slaves`` when running Locust distributed. + + Otherwise - due to the current implementation - + you might end up with a distribution of the Locust classes that doesn't correspond to the + Locust classes' ``weight`` attribute. And if the hatch rate is lower than the number of slave + nodes, the hatching would occur in "bursts" where all slave node would hatch a single user and + then sleep for multiple seconds, hatch another user, sleep and repeat. + Example ======= diff --git a/docs/writing-a-locustfile.rst b/docs/writing-a-locustfile.rst index 5c1460fffe..e25feb1848 100644 --- a/docs/writing-a-locustfile.rst +++ b/docs/writing-a-locustfile.rst @@ -421,7 +421,7 @@ One can mark requests as failed, even when the response code is OK, by using the .. code-block:: python - with client.get("/", catch_response=True) as response: + with self.client.get("/", catch_response=True) as response: if response.content != b"Success": response.failure("Got wrong response") @@ -431,7 +431,7 @@ be reported as a success in the statistics: .. code-block:: python - with client.get("/does_not_exist/", catch_response=True) as response: + with self.client.get("/does_not_exist/", catch_response=True) as response: if response.status_code == 404: response.success() @@ -450,7 +450,7 @@ Example: # Statistics for these requests will be grouped under: /blog/?id=[id] for i in range(10): - client.get("/blog?id=%i" % i, name="/blog?id=[id]") + self.client.get("/blog?id=%i" % i, name="/blog?id=[id]") Common libraries ================= diff --git a/examples/fast_http_locust.py b/examples/fast_http_locust.py new file mode 100644 index 0000000000..3cc47b3d73 --- /dev/null +++ b/examples/fast_http_locust.py @@ -0,0 +1,24 @@ +from locust import HttpLocust, TaskSet, task +from locust.contrib.fasthttp import FastHttpLocust + + +class UserTasks(TaskSet): + @task + def index(self): + self.client.get("/") + + @task + def stats(self): + self.client.get("/stats/requests") + + +class WebsiteUser(FastHttpLocust): + """ + Locust user class that does requests to the locust web server running on localhost, + using the fast HTTP client + """ + host = "http://127.0.0.1:8089" + min_wait = 2000 + max_wait = 5000 + task_set = UserTasks + diff --git a/examples/nested_inline_tasksets.py b/examples/nested_inline_tasksets.py new file mode 100644 index 0000000000..01dc40f2ac --- /dev/null +++ b/examples/nested_inline_tasksets.py @@ -0,0 +1,26 @@ +from locust import HttpLocust, TaskSet, task + + +class WebsiteUser(HttpLocust): + """ + Example of the ability of inline nested TaskSet classes + """ + host = "http://127.0.0.1:8089" + min_wait = 2000 + max_wait = 5000 + + class task_set(TaskSet): + @task + class IndexTaskSet(TaskSet): + @task(10) + def index(self): + self.client.get("/") + + @task(1) + def stop(self): + self.interrupt() + + @task + def stats(self): + self.client.get("/stats/requests") + diff --git a/locust/__init__.py b/locust/__init__.py index 657092e222..d85072a123 100644 --- a/locust/__init__.py +++ b/locust/__init__.py @@ -1,4 +1,4 @@ from .core import HttpLocust, Locust, TaskSet, TaskSequence, task, seq_task from .exception import InterruptTaskSet, ResponseError, RescheduleTaskImmediately -__version__ = "0.12.1" +__version__ = "0.12.2" diff --git a/locust/core.py b/locust/core.py index 6c5ce620f5..1004ba0931 100644 --- a/locust/core.py +++ b/locust/core.py @@ -20,7 +20,7 @@ from .clients import HttpSession from .exception import (InterruptTaskSet, LocustError, RescheduleTask, RescheduleTaskImmediately, StopLocust) -from .runners import STATE_CLEANUP +from .runners import STATE_CLEANUP, LOCUST_STATE_RUNNING, LOCUST_STATE_STOPPING, LOCUST_STATE_WAITING logger = logging.getLogger(__name__) @@ -134,6 +134,7 @@ class Locust(object): _setup_has_run = False # Internal state to see if we have already run _teardown_is_set = False # Internal state to see if we have already run _lock = gevent.lock.Semaphore() # Lock to make sure setup is only run once + _state = False def __init__(self): super(Locust, self).__init__() @@ -360,6 +361,9 @@ def run(self, *args, **kwargs): while (True): try: + if self.locust._state == LOCUST_STATE_STOPPING: + raise GreenletExit() + if self.locust.stop_timeout is not None and time() - self._time_start > self.locust.stop_timeout: return @@ -432,7 +436,9 @@ def get_wait_secs(self): return millis / 1000.0 def wait(self): + self.locust._state = LOCUST_STATE_WAITING self._sleep(self.get_wait_secs()) + self.locust._state = LOCUST_STATE_RUNNING def _sleep(self, seconds): gevent.sleep(seconds) diff --git a/locust/main.py b/locust/main.py index dfc9e8d6ba..9078c63261 100644 --- a/locust/main.py +++ b/locust/main.py @@ -242,6 +242,15 @@ def parse_options(): help="sets the exit code to post on error" ) + parser.add_argument( + '-s', '--stop-timeout', + action='store', + type=int, + dest='stop_timeout', + default=None, + help="number of seconds to wait for a simulated user to complete any executing task before exiting. Default is to terminate immediately." + ) + parser.add_argument( 'locust_classes', nargs='*', diff --git a/locust/rpc/zmqrpc.py b/locust/rpc/zmqrpc.py index b41c1c42f1..ec3f1c48b0 100644 --- a/locust/rpc/zmqrpc.py +++ b/locust/rpc/zmqrpc.py @@ -35,7 +35,11 @@ def recv_from_client(self): class Server(BaseSocket): def __init__(self, host, port): BaseSocket.__init__(self, zmq.ROUTER) - self.socket.bind("tcp://%s:%i" % (host, port)) + if port == 0: + self.port = self.socket.bind_to_random_port("tcp://%s" % host) + else: + self.socket.bind("tcp://%s:%i" % (host, port)) + self.port = port class Client(BaseSocket): def __init__(self, host, port, identity): diff --git a/locust/runners.py b/locust/runners.py index 97df507fdd..c9b85aee74 100644 --- a/locust/runners.py +++ b/locust/runners.py @@ -26,6 +26,7 @@ STATE_INIT, STATE_HATCHING, STATE_RUNNING, STATE_CLEANUP, STATE_STOPPING, STATE_STOPPED, STATE_MISSING = ["ready", "hatching", "running", "cleanup", "stopping", "stopped", "missing"] SLAVE_REPORT_INTERVAL = 3.0 +LOCUST_STATE_RUNNING, LOCUST_STATE_WAITING, LOCUST_STATE_STOPPING = ["running", "waiting", "stopping"] class LocustRunner(object): def __init__(self, locust_classes, options): @@ -125,12 +126,13 @@ def hatch(): locust = bucket.pop(random.randint(0, len(bucket)-1)) occurrence_count[locust.__name__] += 1 + new_locust = locust() def start_locust(_): try: - locust().run(runner=self) + new_locust.run(runner=self) except GreenletExit: pass - new_locust = self.locusts.spawn(start_locust, locust) + self.locusts.spawn(start_locust, new_locust) if len(self.locusts) % 10 == 0: logger.debug("%i locusts hatched" % len(self.locusts)) gevent.sleep(sleep_time) @@ -151,7 +153,7 @@ def kill_locusts(self, kill_count): dying = [] for g in self.locusts: for l in bucket: - if l == g.args[0]: + if l == type(g.args[0]): dying.append(g) bucket.remove(l) break @@ -193,6 +195,15 @@ def stop(self): # if we are currently hatching locusts we need to kill the hatching greenlet first if self.hatching_greenlet and not self.hatching_greenlet.ready(): self.hatching_greenlet.kill(block=True) + if self.options.stop_timeout: + for locust_greenlet in self.locusts: + locust = locust_greenlet.args[0] + if locust._state == LOCUST_STATE_WAITING: + locust_greenlet.kill() + else: + locust._state = LOCUST_STATE_STOPPING + if not self.locusts.join(timeout=self.options.stop_timeout): + logger.info("Not all locusts finished their tasks & terminated in %s seconds. Killing them..." % self.options.stop_timeout) self.locusts.kill(block=True) self.state = STATE_STOPPED events.locust_stop_hatching.fire() diff --git a/locust/static/locust.js b/locust/static/locust.js index a78b8cc368..5a51f1f938 100644 --- a/locust/static/locust.js +++ b/locust/static/locust.js @@ -103,6 +103,7 @@ var report; function renderTable(report) { var totalRow = report.stats.pop(); + totalRow.is_aggregated = true; var sortedStats = (report.stats).sort(sortBy(sortAttribute, desc)); sortedStats.push(totalRow); $('#stats tbody').empty(); diff --git a/locust/stats.py b/locust/stats.py index b49beaf3c9..4f80b851e1 100644 --- a/locust/stats.py +++ b/locust/stats.py @@ -52,6 +52,8 @@ def calculate_response_time_percentile(response_times, num_requests, percent): processed_count += response_times[response_time] if(num_requests - processed_count <= num_of_request): return response_time + # if all response times were None + return 0 def diff_response_time_dicts(latest, old): @@ -74,13 +76,17 @@ class RequestStats(object): def __init__(self): self.entries = {} self.errors = {} - self.total = StatsEntry(self, "Total", None, use_response_times_cache=True) + self.total = StatsEntry(self, "Aggregated", None, use_response_times_cache=True) self.start_time = None @property def num_requests(self): return self.total.num_requests + @property + def num_none_requests(self): + return self.total.num_none_requests + @property def num_failures(self): return self.total.num_failures @@ -129,7 +135,7 @@ def clear_all(self): """ Remove all stats entries and errors """ - self.total = StatsEntry(self, "Total", None, use_response_times_cache=True) + self.total = StatsEntry(self, "Aggregated", None, use_response_times_cache=True) self.entries = {} self.errors = {} self.start_time = None @@ -155,6 +161,9 @@ class StatsEntry(object): num_requests = None """ The number of requests made """ + num_none_requests = None + """ The number of requests made with a None response time (typically async requests) """ + num_failures = None """ Number of failed request """ @@ -214,6 +223,7 @@ def __init__(self, stats, name, method, use_response_times_cache=False): def reset(self): self.start_time = time.time() self.num_requests = 0 + self.num_none_requests = 0 self.num_failures = 0 self.total_response_time = 0 self.response_times = {} @@ -246,6 +256,9 @@ def _log_time_of_request(self, t): self.last_request_timestamp = t def _log_response_time(self, response_time): + if response_time is None: + self.num_none_requests += 1 + return self.total_response_time += response_time @@ -287,7 +300,7 @@ def fail_ratio(self): @property def avg_response_time(self): try: - return float(self.total_response_time) / self.num_requests + return float(self.total_response_time) / (self.num_requests - self.num_none_requests) except ZeroDivisionError: return 0 @@ -295,7 +308,7 @@ def avg_response_time(self): def median_response_time(self): if not self.response_times: return 0 - median = median_from_dict(self.num_requests, self.response_times) + median = median_from_dict(self.num_requests - self.num_none_requests, self.response_times) or 0 # Since we only use two digits of precision when calculating the median response time # while still using the exact values for min and max response times, the following checks @@ -340,6 +353,7 @@ def extend(self, other): self.start_time = min(self.start_time, other.start_time) self.num_requests = self.num_requests + other.num_requests + self.num_none_requests = self.num_none_requests + other.num_none_requests self.num_failures = self.num_failures + other.num_failures self.total_response_time = self.total_response_time + other.total_response_time self.max_response_time = max(self.max_response_time, other.max_response_time) @@ -362,6 +376,7 @@ def serialize(self): "last_request_timestamp": self.last_request_timestamp, "start_time": self.start_time, "num_requests": self.num_requests, + "num_none_requests": self.num_none_requests, "num_failures": self.num_failures, "total_response_time": self.total_response_time, "max_response_time": self.max_response_time, @@ -378,6 +393,7 @@ def unserialize(cls, data): "last_request_timestamp", "start_time", "num_requests", + "num_none_requests", "num_failures", "total_response_time", "max_response_time", @@ -633,7 +649,7 @@ def print_stats(stats): except ZeroDivisionError: fail_percent = 0 - console_logger.info((" %-" + str(STATS_NAME_WIDTH) + "s %7d %12s %42.2f") % ('Total', total_reqs, "%d(%.2f%%)" % (total_failures, fail_percent), total_rps)) + console_logger.info((" %-" + str(STATS_NAME_WIDTH) + "s %7d %12s %42.2f") % ('Aggregated', total_reqs, "%d(%.2f%%)" % (total_failures, fail_percent), total_rps)) console_logger.info("") def print_percentile_stats(stats): diff --git a/locust/templates/index.html b/locust/templates/index.html index 463bc9c387..a249169d5f 100644 --- a/locust/templates/index.html +++ b/locust/templates/index.html @@ -217,9 +217,9 @@

Version