Web application stress testing with K6

When deploying an application to the web performance can be (and usually is) an important factor. Running it on your dev machine isn’t really a good indicator of how well your application will run, however. Finding out how well your application performs before you deploy is a good idea. One way we can accomplish that is by stress testing your web application using a tool called K6.

What is stress testing?

While stress testing could be considered spending large amounts of time with your toddler and infant children, that’s not what we’re actually talking about.

In terms of software, stress testing can be defined as testing a software beyond normal operation to determine a breaking point. This is important for a couple of reasons: 1) you can identify maximum load 2) you can ensure your application will perform to expectations.

How do you stress test?

This is sort of a loaded question, of course. One way is you get a whole bunch of people to hit the application all at the same time. I remember doing this at some previous workplaces. This isn’t necessarily an invalid technique but it certainly isn’t easy to organize nor terribly effective. First off, you likely can’t get enough people together to actually exceed the expected load of the system. Second, you’re misusing a bunch of resources.

Different industries handle this in different ways but I venture to say that all of them have developed automated testing/simulation routines for this purpose.

For today however, we’re going to focus on K6.io. K6 is built using Go and JavaScript. You write tests using JavaScript and then execute the tests. Since they can be individual files it wouldn’t be difficult to include them into your development or continuous integration workflows. K6 is developed specifically for stress testing web applications.

Honorable Mention

K6 is not the only tool out there by any means. I wanted to briefly mention another tool I used for our stress testing during the big deploy last year. Rick Strahl has written a tool called WebSurge that will hammer a url or set of urls given certain parameters. For our particular purposes it didn’t identify enough information nor was customizable (scriptable) enough, but it is absolutely a good tool to have available to you.

Let’s get started with K6

Web application stress testing with K6 is pretty simple. This is especially true if you have experience with “modern” JavaScript. Well, maybe I oversimplified that. The tool has a lot of things it can do. Writing simple tests is pretty simple, how about that? Look below for a simple script (changed the url to protect the innocent) we run on our test server:

import http from "k6/http";
import { sleep } from "k6";
import { check } from "k6";
import { Trend } from "k6/metrics";

var myTrend = new Trend("waiting_time");

export default function () {
	let res = http.get("https://localhost/");
	myTrend.add(res.timings.waiting);
	check(res, {
		"response code was 200": (res) => res.status == 200,
		"total request time <10s": (res) => res.timings.waiting < 10 * 1000
	});
	sleep(Math.random() * 2);
};

Allow me to explain what is happening above. I’m importing a few different pieces from the K6 library — http, sleep, check, and Trend. Trend is going to track different things for me — in this case, I want to track waiting_time (how long each http request waited). The meat of the code I instruct a url I want to load, add my result timings to the trend.

Next I want to check my results. Think of that sort of like an assert in a unit test except they don’t halt execution. In our case we felt like 10s was our maximum acceptable waiting time and we, of course, wanted response code 200.

Lastly we want to randomize access just a small bit when running multiple concurrent requests (to better simulate real web traffic) so we simply add a small sleep.

If I wanted to run this script and assuming I had K6 installed and on my path, I could execute it like so: k6 run --vus 25 -d 60s test_script.js. This will run my script with 25 virtual users for 60 seconds. Since this is command line you can pipe your output to a file instead, btw (which I personally like to do). That might look like k6 run --vus 25 -d 60s test_script.js > test_script_results.txt. See also K6 documentation.

History (how I found them)

I’ve briefly mentioned K6 in two different occasions here on my blog. The first was when discussing Throttling requests in DotNetCore web applications under the heading “K6 Load Testing”. The second time was in my article Using WCF with DotNetCore under the “My Findings” heading. Both were pretty brief nods to the fact we were using it.

Background

To expand on that, however, I’ll give a little background. Back in late 2017 I began working on a project where we took an application previously written on ASP.NET MVC 4 (later converted to 5) and were rewriting it more or less from the ground up on ASP.NET Core. We reused most of the backend services and a lot of the logic from the previous website. Overall we expected that the new site would perform better than the previous site since .NET Core had that reputation/expectation being a more lean platform.

We had performed some “hallway” testing where the client had a large portion of employees hitting it at the same time to verify various aspects of the system. Those tests seemed “good enough” at the time. Unfortunately while the site was initially doing quite well after deployment, after about two hours it suddenly came to a halt. We restarted the website and it went another couple hours and died. Ended up rolling back to the previous website and spending a week trying to diagnose the issue.

The one glaring thing we were missing, however, is that we were not doing true stress testing on our web application. Prior to this event in my career it hadn’t been a priority nor an obvious necessity.

Piecing it together

To make a really long story shorter we found some things and changed some things and redeployed again only to have it fail again. Did it a third time and, again. All of the things we found and changed were really good changes but none of them addressed the core issue. I will mention, by the way, that by this time I had found and started using WebSurge and it *seemed* to indicate success on the test server. Going live once again showed us it was not fixed yet, whatever “it” was.

At this point we knew we needed to do some heavy testing. Once we involved our boss he wrote some test scripts on K6. Running said tests helped identify a few pages in particular that were not doing so well. That was the break we needed and we finally traced it down to some completely brand new code added in this new website. I won’t go into details because they aren’t relevant right now, but suffice it to say K6 was the hero in this story.

Conclusion

Web application stress testing with K6 can be a fairly simple and should be an integral part of your workflow. We briefly discussed some simple scripts and tests you can run to check load on your application.

Credits

Photo by Tom Pumford on Unsplash