# I used Python for a load test, and look what happened next…

on
jun 12, 2021
in

After I added the Google Cloud Storage backend to PrivateBin, I was
interested in its performance characteristics. To fake a real PrivateBin client, I needed to generate an encrypted message.
I searched for a client library and found PBinCLI. As it is written in Python,
I could write a simple load test in Locust. Nice and easy. How wrong was I.

## create the test

Within a few minutes I had a test script to create, retrieve and delete a paste.
To validate the functional correctness, I ran it against https://privatebin.net:

$locust -f locustfile.py \ --users 1 \ --spawn-rate 1 \ --run-time 1m \ --headless \ --host https://privatebin.net The following table shows measured response times: Name Avg (ms) Min (ms) Max (ms) Median (ms) create-paste 237 140 884 200 get-paste 28 27 30 29 delete-paste 27 27 29 27 Now, I had a working load test script and a baseline to compare the performance with. ## baseline on Google Cloud Storage After deploying the service to Google Cloud Run, I ran the single user test. And it was promising. $ locust -f locustfile.py \
--users 1 \
--spawn-rate 1 \
--run-time 1m \
--headless \
--host https://privatebin-deadbeef-ez.a.run.app

Sure, it is not lightning fast, but I did not expect that. The response times looked
acceptable to me. After all, privatebin is not used often nor heavily.

Name Avg (ms) Min (ms) Max (ms) Median (ms)
create-paste 506 410 664 500
get-paste 394 335 514 380
delete-paste 587 443 974 550

## multi-user run on Google Cloud Storage

next I wanted to know the performance of PrivateBin with my Google Cloud Storage onder moderate load. So, I scaled the load test to 5 concurrent users.

$locust -f locustfile.py \ --users 5 \ --spawn-rate 1 \ --run-time 1m \ --headless The results were shocking! Name Avg (ms) Min (ms) Max (ms) Median (ms) create-paste 4130 662 10666 4500 delete-paste 3449 768 6283 3800 get-paste 2909 520 5569 2400 How? Why? Was there a bottleneck at the storage level? I checked the logs and saw steady response times reported by Cloud Run: POST 200 1.46 KB 142 ms python-requests/2.25.1 https://privatebin-37ckwey3cq-ez.a.run.app/ POST 200 1.25 KB 382 ms python-requests/2.25.1 https://privatebin-37ckwey3cq-ez.a.run.app/ GET 200 1.46 KB 348 ms python-requests/2.25.1 https://privatebin-37ckwey3cq-ez.a.run.app/?d7e4c494ce4f613f It took me a while to discover that locust was trashing my little M1. It was running at 100% CPU without even blowing a fan to create the encrypted messages! So I needed something more efficient. ## k6 to the rescue! When my brain thinks fast, it thinks golang. So I downloaded k6. The user scripts are written in JavaScript, but the engine is pure go. Unfortunately, the interpreter is custom built and has limited compatibility with nodejs and browser JavaScript engines. This meant that I could not use any existing JavaScript libraries to create an encrypted message. Fortunately with xk6 you can call a golang function from your JavaScript code. This is what I needed! So I created the k6 privatebin extension and wrote an equivalent load test script. ## build a customized k6 To use this new extension, I build k6 locally with the following commands: go install github.com/k6io/xk6/cmd/xk6@latest xk6 build --with github.com/binxio/xk6-privatebin@v0.1.2 ## k6 baseline run on Google Cloud Storage Now I was ready to run the baseline for the single user, using k6: $ ./k6 --log-output stderr run -u 1 -i 100  test.js

The baseline result looked pretty much like the Locust run:

Name Avg (ms) Min (ms) Max (ms) Median (ms)
create-paste 440 396 590 429
get-paste 355 320 407 353
delete-paste 382 322 599 357

## k6 multi-user run on Google Cloud Storage

What would happen, I i scaled up to 5 concurrent users?

\$ ./k6 --log-output stderr run -u 5 -i 100  test.js

Woohoo! The response times stayed pretty flat.

Name Avg (ms) Min (ms) Max (ms) Median (ms)
create-paste 584 350 2612 555
get-paste 484 316 808 490
delete-paste 460 295 843 436

The chart below shows the sum of the medians for the single- and multi-user load test on Locust and k6:

It is clear that Locust was skewing the results way too much. With k6, I could even simulate 20 concurrent users, and still only use 25% of my M1.

Name Avg (ms) Min (ms) Max (ms) Median (ms)
create-paste 713 414 1204 671
get-paste 562 352 894 540
delete-paste 515 351 818 495

These are way more users than i expect on my privatebin installation and these response times are very acceptable for me. Mission accomplished!

## conclusion

In the future, my goto tool for load and performance tests will be k6. It is a single binary which you can run anywhere. It is fast, and the golang extensions makes it easy to include any compute intensive tasks in a very user-friendly manner.

Mark van Holsteijn is a senior software systems architect, and CTO of binx.io. He is passionate about removing waste in the software delivery process and keeping things clear and simple.
Share this article: Tweet this post / Post on LinkedIn