🏗️ Load Testing loadtest

AlmeríaJS, 2023-12-27

🦍 Your Host Tonight

Alex "pinchito" Fernández

Silverback Backend Developer at Tinybird

🗂️ What We Will See

🤔 What Is loadtest

 

💃 Bringing It Up to Date

 

⏱️ Microprofiling Demo

 

☁️ Performance in the Cloud

 

🫓 Bun.sh: the Future?

🧓 Some History

10+ years of history no less

🚫 JS Didn't have

ES Modules

 

Classes

 

Testing package

 

async/await

 

Command-line processing

 

2.5M modules

✅ JS Had

CommonJS

 

object.prototype

 

package testing

 

callbacks

 

package stdio

 

~30k modules

🦕 Node.js v0.10

📦 30k modules

to choose from

🤔 What is loadtest?

👯‍♂️ Send a lot of requests per second

 

⚡ Verify performance of your endpoints

 

🔌 Has a very complete API

 

⭐ 2500 stars on GitHub

⚡ Welcome to the wonderful world of requests per second

loadtest v5 was not recommended over 1 krps

 

1 krps = 1000 rps

 

1 krps = 1 milisecond (ms) per request

⛷️ Competition

ab: Apache Bench

 

wrk, wrk2

 

autocannon

 

hey

🥞 Secret Sauce: --rps

Option to send a fixed number of rps

 

Forces the server to respond in time

 

Does not allow for wiggle room

🧟 loadtest was so-so

Not dead, not alive

🛝 Had some downtime...

💃 Decided to have some fun

🍇 Use Multiple Cores

Virtually all modern computers have multiple cores

 

Use the cluster module

 

Half for the test server

 

Half for the load tester

 

Configurable with --cores

🔌 Use TCP Sockets

An idea by Matteo Collina

 

It is what autocannon does

 

Use net instead of http

 

Implement HTTP 1.1

 

Is it so hard?

 

Full story

⏱️ Micro-profiling

Use microprofiler

 

Start with this commit

 

See where every microsecond goes!

 

60 krps: 16.7 µs

 

80 krps: 12.5 µs

 

~4 µs of difference

📊 Performance Comparison

⏱️ Synthetic benchmark

 

🙏 Request on Meetup, Mastodon, Twitter

 

📜 Created a special script

performance vs price

⚡ tcp-performance.js

☁️ Performance in the Cloud

🫓 Bun.sh: The Future?

🛠️ Fixed loadtest for bun

😲 Took me 1 hour 😲

🔭 Preliminary Impressions

🍇 No cluster module

 

Test in single-core mode

 

Similar to node?

runner mode krps
bun http 14
node http 19
bun tcp 62
node tcp 60

🔬 We Got Those Pesky µs

Ready for the nanoseconds?

⚡ Use performance

The performance package can measure nanoseconds, example:

 

$ performance
Running benchmarks for 1000 ms
Function nil running for 1.00 seconds: 9.65211e+8 iterations per second, 1 ns per iteration
Function util._extend() running for 1.00 seconds: 7.1558e+7 iterations per second, 14 ns per iteration
Function Object.keys() running for 1.09 seconds: 6.4279155188246095e+3 iterations per second, 155571 ns per iteration
Function sha1-token running for 1.00 seconds: 3.19e+5 iterations per second, 3135 ns per iteration
Function sha256-token running for 1.00 seconds: 3.1137724550898204e+5 iterations per second, 3212 ns per iteration
Function replace-regexp() running for 1.00 seconds: 1.654e+6 iterations per second, 605 ns per iteration
Function match().join() running for 1.00 seconds: 2.211e+6 iterations per second, 452 ns per iteration
Function for().toLowerCase() running for 1.00 seconds: 2.101e+6 iterations per second, 476 ns per iteration
Function for().charCodeAt() running for 1.00 seconds: 3.065e+6 iterations per second, 326 ns per iteration
Function timestamp running for 1.00 seconds: 2.565e+6 iterations per second, 390 ns per iteration
Function array-access running for 1.05 seconds: 6.653992395437262e+3 iterations per second, 150286 ns per iteration
Function object-access running for 1.09 seconds: 6.433823529411764e+3 iterations per second, 155429 ns per iteration
Function string+ running for 1.00 seconds: 1.9588e+8 iterations per second, 5 ns per iteration
Function string-replace running for 1.00 seconds: 3.1248e+7 iterations per second, 32 ns per iteration
Function string-replace-regexp running for 1.00 seconds: 1.4403e+7 iterations per second, 69 ns per iteration
Function parseInt running for 1.00 seconds: 1.96069e+8 iterations per second, 5 ns per iteration
Function |0 running for 1.00 seconds: 1.96167e+8 iterations per second, 5 ns per iteration
Function Math.random() running for 1.00 seconds: 5.194e+6 iterations per second, 193 ns per iteration
Function buffer-concat running for 1.00 seconds: 1.079e+6 iterations per second, 927 ns per iteration
Function string-concat running for 1.00 seconds: 1.332e+7 iterations per second, 75 ns per iteration
Function string-array-concat running for 1.00 seconds: 3.833e+6 iterations per second, 261 ns per iteration
Function for-loop running for 1.01 seconds: 1.0515873015873016e+5 iterations per second, 9509 ns per iteration
Function forEach() running for 1.01 seconds: 3.766105054509416e+4 iterations per second, 26553 ns per iteration
Function for..in running for 1.32 seconds: 3.02571860816944e+3 iterations per second, 330500 ns per iteration
Function for-function running for 1.02 seconds: 3.428011753183154e+4 iterations per second, 29171 ns per iteration
Function Date.now() running for 1.00 seconds: 1.2858e+7 iterations per second, 78 ns per iteration
Function process.hrtime() running for 1.00 seconds: 1.1091e+7 iterations per second, 90 ns per iteration

🤯 Conclusions

⏱️ Use benchmarks when needed

 

🛠️ Learn to use (and create) your tools

 

🫰 Cloud: rip-off unless you are careful

 

🔥 Intel's 13th generation is smoking hot

 

🫓 Bun: needs more time in the oven

 Vendo Seat Panda 🚙

🧮 LibreCounter: Visit Counter

 

🥸 GDPR compliant

 

🔧 Super-simple setup

 

💅 Rebranded

<a href="https://librecounter.org/referer/show" target="_blank">
<img src="https://librecounter.org/counter.svg" referrerPolicy="unsafe-url" />
</a>

📈 LibreCounter

🙏 Thanks!

🗣️ Let's Talk