Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(charts): prevent displaying stats before requests are made #1853

Merged
merged 7 commits into from
Aug 19, 2021
99 changes: 79 additions & 20 deletions locust/static/locust.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,11 @@ function appearStopped() {

$("#box_stop a.stop-button").click(function(event) {
event.preventDefault();
$.get($(this).attr("href"));
$.get($(this).attr("href")).done(() => {
markerFlags.stop = true;
});
$("body").attr("class", "stopped");
appearStopped()
appearStopped();
});

$("#box_stop a.reset-button").click(function(event) {
Expand Down Expand Up @@ -79,6 +81,11 @@ $('#swarm_form').submit(function(event) {
function(response) {
if (response.success) {
setHostName(response.host);

// only mark run starts if at least 1 run has been reported
if (stats_history["time"].length > 0) {
markerFlags.start = true;
}
}
}
);
Expand Down Expand Up @@ -174,30 +181,39 @@ $("#workers .stats_label").click(function(event) {
renderWorkerTable(window.report);
});

function createMarkLine() {
return {
symbol: "none",
label: {
formatter: params => `Run #${params.dataIndex + 1}`
},
lineStyle: {color: "#5b6f66"},
data: stats_history["markers"],
}
}

function update_stats_charts(){
if(stats_history["time"].length > 0){
rpsChart.chart.setOption({
xAxis: {data: stats_history["time"]},
series: [
{data: stats_history["current_rps"]},
{data: stats_history["current_rps"], markLine: createMarkLine()},
{data: stats_history["current_fail_per_sec"]},
]
});

responseTimeChart.chart.setOption({
xAxis: {data: stats_history["time"]},
series: [
{data: stats_history["response_time_percentile_50"]},
{data: stats_history["response_time_percentile_50"], markLine: createMarkLine()},
{data: stats_history["response_time_percentile_95"]},
]
});

usersChart.chart.setOption({
xAxis: {
data: stats_history["time"]
},
xAxis: {data: stats_history["time"]},
series: [
{data: stats_history["user_count"]},
{data: stats_history["user_count"], markLine: createMarkLine()},
]
});
}
Expand All @@ -210,27 +226,70 @@ var usersChart = new LocustLineChart($(".charts-container"), "Number of Users",
charts.push(rpsChart, responseTimeChart, usersChart);
update_stats_charts()

const markerFlags = {
start: false,
stop: false,
}

function updateStats() {
$.get('./stats/requests', function (report) {
window.report = report;
try{
renderTable(report);
renderWorkerTable(report);

if (report.state !== "stopped"){
// get total stats row
var total = report.stats[report.stats.length-1];
// update charts
stats_history["time"].push(new Date().toLocaleTimeString());
stats_history["user_count"].push({"value": report.user_count});
stats_history["current_rps"].push({"value": total.current_rps, "users": report.user_count});
stats_history["current_fail_per_sec"].push({"value": total.current_fail_per_sec, "users": report.user_count});
stats_history["response_time_percentile_50"].push({"value": report.current_response_time_percentile_50, "users": report.user_count});
stats_history["response_time_percentile_95"].push({"value": report.current_response_time_percentile_95, "users": report.user_count});
update_stats_charts()
} else {
const time = new Date().toLocaleTimeString();

if (report.state === "stopped") {
if (markerFlags.stop) {
markerFlags.stop = false;

// placeholders to show a skip in the lines between test runs
stats_history["time"].push(time);
stats_history["user_count"].push({"value": null});
stats_history["current_rps"].push({"value": null});
stats_history["current_fail_per_sec"].push({"value": null});
stats_history["response_time_percentile_50"].push({"value": null});
stats_history["response_time_percentile_95"].push({"value": null});
}

// update stats chart to ensure the stop spacing appears as part
// of the update loop, otherwise we will "jump" 2 plots on the next run
update_stats_charts();

appearStopped();
return;
}

// add markers between test runs, based on a new run being started
if (stats_history["time"].length > 0 && markerFlags.start) {
markerFlags.start = false;

// mark the first run when we start the second run
if (stats_history["markers"].length === 0) {
stats_history["markers"].push({xAxis: stats_history["time"][0]});
}

stats_history["markers"].push({xAxis: time});
}

// get total stats row
var total = report.stats[report.stats.length-1];

// ignore stats without requests
if (total.num_requests < 1) {
return;
}

// update charts
stats_history["time"].push(time);
stats_history["user_count"].push({"value": report.user_count});
stats_history["current_rps"].push({"value": total.current_rps, "users": report.user_count});
stats_history["current_fail_per_sec"].push({"value": total.current_fail_per_sec, "users": report.user_count});
stats_history["response_time_percentile_50"].push({"value": report.current_response_time_percentile_50, "users": report.user_count});
stats_history["response_time_percentile_95"].push({"value": report.current_response_time_percentile_95, "users": report.user_count});
update_stats_charts();

} catch(i){
console.debug(i);
}
Expand Down
4 changes: 3 additions & 1 deletion locust/stats.py
Original file line number Diff line number Diff line change
Expand Up @@ -579,7 +579,7 @@ def get_current_response_time_percentile(self, percent):
break

if cached:
# If we fond an acceptable cached response times, we'll calculate a new response
# If we found an acceptable cached response times, we'll calculate a new response
# times dict of the last 10 seconds (approximately) by diffing it with the current
# total response times. Then we'll use that to calculate a response time percentile
# for that timeframe
Expand All @@ -588,6 +588,8 @@ def get_current_response_time_percentile(self, percent):
self.num_requests - cached.num_requests,
percent,
)
# if time was not in response times cache window
return None

def percentile(self):
if not self.num_requests:
Expand Down
1 change: 1 addition & 0 deletions locust/templates/stats_data.html
Original file line number Diff line number Diff line change
Expand Up @@ -6,4 +6,5 @@
"current_fail_per_sec": {{ current_fail_per_sec_data | tojson }},
"response_time_percentile_50": {{ response_time_percentile_50_data | tojson }},
"response_time_percentile_95": {{ response_time_percentile_95_data | tojson }},
"markers": [],
};
6 changes: 6 additions & 0 deletions locust/test/test_stats.py
Original file line number Diff line number Diff line change
Expand Up @@ -580,6 +580,12 @@ def test_get_current_response_time_percentile(self):

self.assertEqual(95, s.get_current_response_time_percentile(0.95))

def test_get_current_response_time_percentile_outside_cache_window(self):
s = StatsEntry(self.stats, "/", "GET", use_response_times_cache=True)
# an empty response times cache, current time will not be in this cache
s.response_times_cache = {}
self.assertEqual(None, s.get_current_response_time_percentile(0.95))

def test_diff_response_times_dicts(self):
self.assertEqual(
{1: 5, 6: 8},
Expand Down
2 changes: 1 addition & 1 deletion locust/web.py
Original file line number Diff line number Diff line change
Expand Up @@ -426,7 +426,7 @@ def update_template_args(self):
"user_count": self.environment.runner.user_count,
"version": version,
"host": host,
"history": stats.history,
"history": stats.history if stats.num_requests > 0 else {},
"override_host_warning": override_host_warning,
"num_users": options and options.num_users,
"spawn_rate": options and options.spawn_rate,
Expand Down