Load testing in 2026: revisit Grafana k6, Locust and Gatling before you run another performance test
Picture a company in Lucknow that hosts exam results and admit cards for several state boards and universities across Uttar Pradesh and Bihar (an invented example, not a real client). On results day the board announces at 12 noon IST, the link goes out on news channels and WhatsApp groups, and lakhs of students and parents open the same page within minutes. Last year the portal slowed badly at 12:04 PM. This year the CTO wants a load test that answers one question before the date is announced. The Java backend team wants Gatling, the Python QA team wants Locust, and the platform engineer, already on Grafana dashboards, wants Grafana k6. Nobody has yet agreed on what “the test passed” should mean.
This post compares the three open-source load testing tools as they stand on 15 October 2026: scripting, load generation, distributed runs, CI gates, and what is open source versus commercial cloud. The biggest lesson is not syntax. It is the load model: whether your test keeps sending traffic when the system slows down, or quietly waits along with it. Reading published benchmarks is covered in the Redis, Valkey and Dragonfly benchmark method post; this post is about designing your own test.
Facts come from GitHub releases, LICENSE files, PyPI, npm and Maven Central metadata, official docs, advisory databases, and a drill of k6 v2.3.0, Locust 2.46.7 and Gatling 3.16.0 on this Linux box against a tiny local API with a deliberate 3-second stall. The drill is not a speed comparison between tools.
The short version
- Decide the load model first. An open model controls the arrival rate regardless of how slow the system is. A closed model controls the number of concurrent virtual users, each waiting for its response. Public traffic such as results day is mostly open. A closed test of an open system hides slowdowns: coordinated omission.
- Grafana k6 is at v2.3.0 (21 September 2026, 8:47 PM IST), previous major patched as v1.8.2 (12 October 2026). AGPL-3.0. One Go binary, JavaScript or TypeScript scripts, arrival-rate executors, thresholds that fail the run. Grafana Cloud k6 is the paid service.
- Locust is at 2.46.7 (5 October 2026, 1:44 AM IST). MIT. Plain Python; users are closed by nature, with pacing wait times; built-in master and worker mode. Azure Load Testing is the hosted option the docs name.
- Gatling is at 3.16.0 (29 September 2026, 5:51 PM IST), JS SDK 3.16.1. Core Apache 2.0, but the default HTML report module is under the non-open-source Gatling-Highcharts License. Java, Kotlin, Scala, JS or TS; explicit
injectOpenandinjectClosed; assertions. Multi-node runs and live dashboards are Gatling Enterprise features. - Write the capacity question before the script. A test without one produces graphs, not answers.
- Security: no published GitHub repository advisories for any of the three; OSV lists one old Locust web UI XSS (CVE-2020-28364, fixed in 1.3.2).
Where things stand on 15 October 2026
Dates are GitHub release published_at (or tag date where noted), converted from UTC to IST (UTC+5:30). Licences are from the LICENSE files on each default branch and the vendor licence pages.
Project
Release
Date (IST)
Notes
Licence
Grafana k6 (grafana/k6)
v2.3.0
21 September 2026, 8:47 PM
Current major; adds --scenario and --once
AGPL-3.0
Grafana k6
v1.8.2
12 October 2026, 6:02 PM
Supported previous major; security dependency updates
AGPL-3.0
Grafana k6
v2.0.0
11 May 2026, 2:57 PM
Removes deprecated commands and the externally-controlled executor
AGPL-3.0
Grafana k6
v1.0.0
6 May 2025, 4:23 PM
SemVer, native TypeScript, support policy
AGPL-3.0
k6-operator (grafana/k6-operator)
v1.6.0
18 August 2026, 3:21 PM
Kubernetes TestRun CRD
Apache 2.0
Locust (locustio/locust)
2.46.7
5 October 2026, 1:44 AM
Current; needs Python 3.11+
MIT
Locust Kubernetes operator (locustio/k8s-operator)
helm-chart-0.1.6
14 January 2026, 5:04 PM
LocustTest CRD
MIT
Gatling (gatling/gatling)
v3.16.0
29 September 2026, 5:51 PM
GraphQL SDK (JVM languages), shared queues
Apache 2.0 (reports: Gatling-Highcharts License)
Gatling
v3.15.0
27 February 2026, 5:38 PM
Tag date; httpConcurrentRequests
Apache 2.0
Gatling JS SDK (gatling/gatling-js)
v3.16.1
6 October 2026, 3:28 PM
npm @gatling.io/cli; needs Node.js 24+
Apache 2.0
Practical notes. k6’s policy is one major a year and a minor about every six weeks, with the previous major getting only critical and security patches, which is why v1.8.2 followed v2.3.0. Gatling’s older versions are tags and Maven Central artefacts. GitHub stars were about 31,932 (k6), 28,209 (Locust) and 6,955 (Gatling); stars measure attention, not fit.
Timeline
Date (IST)
Event
17 February 2011, 4:38 PM
locustio/locust repository created
31 May 2011, 6:10 PM
gatling/gatling repository created
21 March 2016, 9:00 PM
grafana/k6 repository created
23 October 2018
Gatling 3.0.0 on Maven Central (directory date)
16 May 2020, 4:01 PM
Locust 1.0 on PyPI
17 June 2021
Grafana Labs announces the acquisition of k6
23 November 2021, 4:38 PM
Gatling 3.7.0 (Java and Kotlin DSL alongside Scala, per install docs)
7 May 2024, 8:19 PM
First npm publish of @gatling.io/cli (JavaScript SDK)
6 May 2025, 4:23 PM
k6 v1.0.0
11 May 2026, 2:57 PM
k6 v2.0.0
29 September 2026, 5:51 PM
Gatling 3.16.0
12 October 2026, 6:02 PM
k6 v1.8.2 (supported 1.x line)
First question: what capacity question are you answering?
Before comparing syntax, write one sentence on the whiteboard:
At X arrivals per second of this journey mix, sustained for Y minutes, the p95 (or p99) latency of this endpoint stays under Z ms, errors stay under E percent, and this server-side resource stays under its limit. We also want to know the arrival rate at which that stops being true.
For results day (invented numbers): 600 result lookups per second for 20 minutes after noon, p95 under 800 ms, errors under 0.5 percent, database CPU under 70 percent, and the break point above that.
Then decide whether your system is open or closed. Gatling’s docs put it plainly: closed systems cap concurrency (a call centre, a ticketing site with a waiting room); open systems keep receiving users even when they struggle, which is how most websites behave. Students do not stop refreshing because p95 went up. Results day is an open workload.
A closed test is still right when the real system is closed: a fixed pool of call-centre agents, or a batch job with N workers. The mistake is using virtual users for an open system and reading the latency numbers as if they described real users.
Translating between users and arrival rate
If someone gives you “concurrent users”, convert it first. Little’s law says concurrency equals arrival rate multiplied by time in the system. With 600 lookups per second, 0.8 s response and 4 s reading time, that is about 600 × 4.8 = 2,880 concurrent students. Concurrency rises when response time rises; a test that fixes it at 2,880 cannot show that.
What each tool actually is
Grafana k6 v2.3.0
k6 is a single Go binary that runs test scripts written in JavaScript; since v1.0 it also runs .ts files directly. Virtual users (VUs) execute your default function in a loop, and scenarios with executors decide how iterations are scheduled. The docs list six executors: shared-iterations, per-vu-iterations, constant-vus, ramping-vus, constant-arrival-rate and ramping-arrival-rate. The last two are the open model; the rest are closed.
Arrival-rate executors need a pool of free VUs to start iterations. If none is free, k6 counts the miss in dropped_iterations. The docs say drops early in a test usually mean too few VUs were allocated, and drops later usually mean the system is degrading. You can put a threshold on that metric.
Thresholds are pass or fail rules on metrics, for example http_req_duration: ['p(95)<800'] or http_req_failed: ['rate<0.005']. If any threshold fails, k6 exits non-zero (exit code 99 in the drill). abortOnFail with delayAbortEval stops a run early. Checks are assertions on responses but do not change the exit code by themselves; put a threshold on the checks metric if you want them to.
v2.0.0 removed k6 login, k6 pause, k6 resume, k6 scale, k6 status and the externally-controlled executor, moved the Go module path to go.k6.io/k6/v2 (extensions must update imports), and stopped starting the local HTTP API unless you pass --address. v2.3.0 adds --scenario and --once.
Distributed runs: the k6-operator (Apache 2.0) adds a TestRun resource to Kubernetes; parallelism sets the number of runner pods and the operator splits the workload using execution segments. Grafana Cloud k6 is the commercial hosted service.
Licence: AGPL-3.0. Running it internally is usually simple; check with legal before you embed k6 code in a product.
Locust 2.46.7
A locustfile is a normal Python module. Each simulated user is an instance of a User class (usually HttpUser) in its own gevent greenlet, picking @task methods by weight and sleeping for wait_time between them: constant, between, constant_throughput, constant_pacing or poisson.
The key sentence in the Locust docs: wait time can only constrain throughput, not launch new users to reach a target. Locust users are closed by design. You approximate an open workload with many users paced at a fixed interval (arrivals = users / pacing), which holds only while each task finishes within its interval. LoadTestShape changes user count and spawn rate over time through tick().
Distributed runs are built in: a --master process runs the web UI and coordinates, --worker processes run users and report back. Because Python uses one core per process, the docs advise one worker per core; --processes forks them for you (not on Windows). A Locust Kubernetes operator with a LocustTest resource now exists; locust-swarm is marked as no longer maintained.
For CI, Locust exits with code 1 on any failed request (--exit-code-on-error changes it); for latency gates, set environment.process_exit_code in a quitting listener, as the docs show. Console percentiles are labelled approximated.
Licence: MIT. The docs’ hosted option is Microsoft’s Azure Load Testing; Microsoft also sponsors Locust maintenance.
Gatling 3.16.0
Gatling runs on the JVM (64-bit OpenJDK LTS 11 to 25 per the install docs). You write a Simulation in Java, Kotlin or Scala, or use the JavaScript and TypeScript SDK, which needs Node.js 24+ and covers HTTP only. Projects are Maven, Gradle or sbt builds; a standalone bundle with a pre-filled Maven repository exists for firewalled environments, and the drill used it.
The load model is explicit. injectOpen takes arrival-rate steps (constantUsersPerSec, rampUsersPerSec, the stairs helper incrementUsersPerSec); injectClosed takes constantConcurrentUsers and rampConcurrentUsers; the docs say the two cannot be mixed in one profile. An open-model “user” is an arriving journey, not a long-lived thread.
Assertions run after the simulation on global, per-request or per-group statistics; one failure fails the simulation (a failed Maven build, exit code 1, in the drill). Results can be exported in a JUnit-compatible format.
The open-source boundary matters more here. Gatling core is Apache 2.0. The default HTML report module uses Highcharts and Highstock, which Gatling’s licence page says are neither open source nor free for this use; the module is free to use under the Gatling-Highcharts License, with no modification or re-use allowed. Gatling 3.12 dropped the Graphite integration, and its notes say real-time metrics are covered by Gatling Enterprise. Spreading one injection profile across several load generators (“load sharding”) is documented as a Gatling Enterprise behaviour; with the community edition you coordinate separate instances yourself. 3.16 adds a GraphQL SDK for the JVM languages, shared queues, postCheck, counters and the HTTP QUERY method.
Load models and coordinated omission
In a closed test, each virtual user sends a request and waits. If the server freezes for three seconds, each user sends one slow request and then nothing. The requests real users would have sent during the freeze never appear in your percentiles; the load generator has coordinated with the server to avoid measuring the bad period. Gil Tene’s wrk2 README describes this as coordinated omission; k6’s documentation uses the same term when it explains why closed executors let response time reduce the arrival rate.
An open test keeps scheduling arrivals during the freeze. They either wait and record long latencies, or are counted as dropped if the tool has no free capacity. Either way, the bad period shows up.
Concept
Grafana k6
Locust
Gatling
Closed model
constant-vus, ramping-vus, iteration executors
Default: users loop tasks with wait_time
injectClosed: constantConcurrentUsers, rampConcurrentUsers
Open model
constant-arrival-rate, ramping-arrival-rate
Approximation: many users with constant_pacing or constant_throughput
injectOpen: constantUsersPerSec, rampUsersPerSec, incrementUsersPerSec
When the generator cannot keep up
dropped_iterations rises
Achieved rate falls below target; CPU warning in logs
No VU pool to exhaust; watch the generator’s own CPU and memory
Step or stairs profile
ramping-arrival-rate stages
LoadTestShape
incrementUsersPerSec
All three can run either model. What differs is how loudly each tool pushes you towards the model you meant.
Comparison tables
Scripting and authoring
Question
Grafana k6
Locust
Gatling
Script language
JavaScript, TypeScript
Python
Java, Kotlin, Scala, JavaScript, TypeScript
Runtime
Single Go binary with embedded JS engine
Python 3.11+ with gevent
JVM (JS SDK via Node.js CLI)
Response validation
check()
catch_response and response.failure()
.check(...) on requests
Using your own libraries
ES modules; native extensions need Go
Any Python package
Any JVM library; npm for JS SDK
Distributed runs
Question
Grafana k6
Locust
Gatling (community)
Multi-machine
Via k6-operator on Kubernetes
--master / --worker over the network
Not in the open-source edition; Enterprise shards load
Kubernetes operator
k6-operator (TestRun, parallelism)
Locust operator (LocustTest, workers)
None in open source; Enterprise has a private control plane
Commercial hosted
Grafana Cloud k6
Azure Load Testing (Microsoft)
Gatling Enterprise
Metrics, reports and CI gates
Question
Grafana k6
Locust
Gatling
Pass or fail rule
Thresholds in options
quitting hook sets process_exit_code; default exit 1 on any failure
Assertions in setUp
Exit code on breach (drill)
99
1
1 (Maven build failure)
Built-in report
End-of-test summary, --summary-export JSON
Web UI, --csv files
Static HTML report (Highcharts licence)
Streaming metrics
JSON, CSV, OpenTelemetry, Prometheus remote write and other outputs
OpenTelemetry integration, event hooks
Real-time dashboards are an Enterprise feature
Worked example for the Lucknow scenario
Invented constraints: Java results API behind a load balancer, PostgreSQL read replica, Redis cache, and a staging stack sized like production.
Step 1: take the journey mix from logs. Say nine in ten requests are a result lookup by roll number. Test the lookup first; add the landing page and admit card PDF later.
Step 2: choose the open model and a stairs profile. Hold 300 per second for 5 minutes, ramp to 600 and hold for 20 minutes (the real question), then ramp to 900 for 5 minutes to find the break point. In k6 that is a ramping-arrival-rate scenario:
export const options = {
scenarios: {
results_day: {
executor: 'ramping-arrival-rate',
startRate: 100,
timeUnit: '1s',
preAllocatedVUs: 400,
maxVUs: 3000,
stages: [
{ target: 300, duration: '2m' },
{ target: 300, duration: '5m' },
{ target: 600, duration: '2m' },
{ target: 600, duration: '20m' },
{ target: 900, duration: '2m' },
{ target: 900, duration: '5m' },
],
},
},
thresholds: {
'http_req_duration{name:result}': ['p(95)<800'],
http_req_failed: ['rate<0.005'],
dropped_iterations: ['count<100'],
},
};
In Gatling the same stairs are rampUsersPerSec(...).to(...).during(...) and constantUsersPerSec(...).during(...) steps inside injectOpen, with details("result").responseTime().percentile(95.0).lt(800) as the assertion. In Locust, pace each user at one lookup every 5 seconds and set users to rate × 5 (1,500, then 3,000, then 4,500) through a LoadTestShape, and watch the achieved rate, because a user waiting 6 seconds for a response cannot keep a 5-second pace. All three versions were syntax-checked on this box (k6 inspect, locust --list, Maven test-compile), not run at this scale.
Step 3: size the load generators honestly. First confirm against a stub that the generator reaches 900 per second without CPU saturation: zero dropped_iterations in k6, no CPU warning from Locust workers, enough heap and open files for Gatling.
Step 4: record server-side numbers too. Load test output cannot tell you database CPU. Scrape production-style metrics and keep traces for slow requests (see the distributed tracing post). If staging autoscales, note when pods or nodes arrived; the Kubernetes autoscaling post explains why a cold scale-up can cost minutes that results day does not have.
Step 5: read the result as a sentence. “At 600 per second for 20 minutes, p95 was N ms, errors E percent, database CPU peaked at C percent; at 900 per second p95 crossed 800 ms after M minutes.” If you cannot fill it, the test is not finished.
Lucknow recommendation (invented). Use Gatling for the main capacity test, because the backend team owns it in Java and injectOpen makes the model visible in code review. QA keeps Locust for journey tests that reuse Python data helpers. k6 runs a short threshold gate in the GitLab pipeline. If the team cannot maintain three tools, pick the one whose language the service owners write.
Security and hygiene
On 15 October 2026 the GitHub security advisories API returned no published repository advisories for grafana/k6, locustio/locust, gatling/gatling, grafana/k6-operator or locustio/k8s-operator. OSV had nothing for the k6 Go modules or Gatling Maven artefacts, and one old Locust issue: CVE-2020-28364 / GHSA-vqxw-9pg7-v7v9 / PYSEC-2020-60, a stored XSS in the web UI before 1.3.2. Points that matter more than CVE counts:
- Do not expose the Locust web UI or a k6
--addressAPI on a shared network. Bind to loopback or put them behind authentication. - Keep tokens and test passwords out of scripts; use environment variables or a secret store.
- Load tests are traffic. Get written permission, warn on-call and third-party API owners (SMS gateways, payment providers), and never point a test at production without a stop plan.
Decision guide
Situation
Lean towards
Why
Team writes JavaScript or TypeScript and already runs Grafana
Grafana k6
One binary, arrival-rate executors, thresholds as code
Team thinks in Python and needs custom clients or test data logic
Locust
Plain Python, built-in master/worker, MIT licence
JVM team, or you want the open/closed choice explicit in the DSL
Gatling
injectOpen / injectClosed, assertions, rich HTTP DSL
Strict permissive licence policy for every component
Locust, or Gatling core (check the report module)
k6 is AGPL-3.0; Gatling reports use a separate licence
Distributed runs without buying anything
Locust, or k6 with k6-operator
Gatling community has no built-in multi-node sharding
Hard CI gate on p95 and error rate
k6 or Gatling
Thresholds and assertions are first-class; Locust needs a small hook
Lucknow (invented) pick. Gatling open model as the capacity test of record, a short k6 threshold gate on every release, Locust for QA journeys.
Checklist
- Write the capacity sentence: arrival rate, duration, percentile, error budget, server-side limit.
- Decide open or closed from how real users behave when the system slows down.
- Convert “concurrent users” to arrival rate with Little’s law.
- Take the journey mix from production logs.
- Pin versions (k6 v2.3.0 or v1.8.x, Locust 2.46.7, Gatling 3.16.0).
- Prove the generator reaches the target rate against a stub.
- Gate on p95 or p99, error rate, and dropped or delayed arrivals.
- Collect server-side metrics and traces for the same window.
- Confirm licences with legal, including Gatling’s report module.
- Get written approval and inform on-call and third-party owners.
Common mistakes
- Testing an open public system with a fixed number of virtual users and trusting the p95.
- Reporting the average response time instead of p95, p99 and max.
- Ignoring
dropped_iterationsin k6 or the actual achieved rate in Locust. - Spawning all Locust users at the same instant with identical pacing, which creates synchronised bursts instead of steady arrivals.
- Running the load generator on the same machine as the service under test, then blaming the service.
- Using
check()in k6 and assuming failed checks fail the pipeline. - Forgetting that Gatling’s HTML report module is not under Apache 2.0.
- Treating a 5-minute test as proof of 2 hours of stability; caches, connection pools and GC behave differently over time.
- Testing one endpoint in isolation when the real spike also hits login, static files and PDFs.
- Running without permission against an environment that shares a database or an SMS gateway with production.
A drill I ran on 15 October 2026
Shared Linux VM (8 vCPU, 15 GiB RAM), no Docker or Kubernetes, port 8766 left alone. Runs between about 10:20 AM and 10:27 AM IST. Generator and target shared the box over loopback, so the numbers show load model behaviour, not tool speed.
Setup:
- k6 v2.3.0 release tarball; SHA-256
39c3117b6af817592dcd0ce4242105c0a7af10948c2a425306f0be8f7a8a8ab1matchedk6-v2.3.0-checksums.txt. - Locust 2.46.7 via
pip install locust==2.46.7in a Python 3.13.5 virtual environment. - Gatling 3.16.0 standalone bundle (no vendor checksum attached; hashed locally), run offline with
./mvnw -o gatling:teston Eclipse Temurin 21.0.12.1 (SHA-256 matched the Adoptium API value). - Target: a small Python
ThreadingHTTPServeron127.0.0.1:18440.GET /api/resulttakes 15 ms.GET /admin/stall?ms=3000makes every request arriving in the next 3 seconds wait until the stall ends, a stand-in for a GC pause or database failover, triggered about 10 seconds into each 30-second run. - Same gate for every run: p95 under 200 ms, failures under 1 percent.
Results:
Run
Load model
Requests
p95
Max
Exit code
k6, no stall
constant-vus 10, 0.2 s sleep (closed)
1,390
16.82 ms
50.63 ms
0
k6, 3 s stall
same closed model
1,260
17.22 ms
2.82 s
0 (passed)
k6, no stall
constant-arrival-rate 45/s, 20 pre-allocated VUs (open)
1,351
15.97 ms
38.94 ms
0
k6, 3 s stall
same open model
1,293 + 58 dropped iterations
560 ms
3.01 s
99
k6, 3 s stall
open model, 200 pre-allocated VUs
1,351, 0 dropped
1.52 s
3.02 s
99
Locust, 3 s stall
10 users, constant(0.2) (closed)
1,230
20 ms
2,816 ms
0 (passed)
Locust, 3 s stall
200 users, constant_pacing(4.44), all spawned at once
1,400
110 ms
125 ms
0
Locust, 3 s stall
200 users, constant_pacing(4.44), spawn rate 45/s
1,380
1,900 ms
2,883 ms
1
Gatling, no stall
constantUsersPerSec(45) (open)
1,350
17 ms
21 ms
0
Gatling, 3 s stall
same open model
1,350
1,526 ms
3,010 ms
1
What the drill showed:
- Closed models passed a test they should have failed. k6 and Locust with 10 looping users both saw a 2.8-second max but kept p95 near 20 ms, because only about ten requests were in flight during the stall.
- Open models caught the same stall. Gatling at 45 arrivals per second showed p95 1,526 ms and failed its assertion; k6 with enough pre-allocated VUs showed p95 1.52 s and exited with 99.
- Generator capacity changes the story. With only 20 pre-allocated VUs, k6 could not start 58 scheduled iterations during the stall (
vus_maxreached 78), so p95 looked better (560 ms). A threshold ondropped_iterationswould flag this. - Synchronised users can miss a stall. 200 Locust users spawned in one instant with identical pacing arrived in bursts, and the stall fell into a quiet gap (max 125 ms). Spawning at 45 users per second spread arrivals and exposed it (p95 1,900 ms).
- CI gates worked as documented: k6 exit 99 (“thresholds on metrics ‘http_req_duration’ have been crossed”), Locust exit 1 from the
quittinghook, Gatling “95th percentile of response time is less than 200.0 : false (actual : 1526.0)” and a Maven build failure.
What this box could not run: the k6 or Locust Kubernetes operators, Locust workers across machines, Gatling Enterprise, Grafana Cloud k6, Azure Load Testing, the Gatling JavaScript SDK (needs Node.js 24; this box has 20.19.2) and the k6 browser module. The target server was stopped afterwards and port 18440 confirmed closed. Scripts, outputs and notes are under drill44/.
Unlearn
- Unlearn “number of concurrent users” as the default input for public traffic.
- Unlearn trusting a green p95 from a closed test of an open system.
- Unlearn averages as a capacity metric.
- Unlearn the idea that Gatling’s whole download is Apache 2.0 because the core is.
- Unlearn load tests without a written question and a pass or fail rule.
Re-learn
- Re-learn open versus closed workloads and coordinated omission as the first design decision.
- Re-learn Little’s law to convert between users, think time and arrival rate.
- Re-learn the open-model APIs: k6 arrival-rate executors, Gatling
injectOpen, paced Locust users with spread-out spawning. - Re-learn CI gates: k6 thresholds, Locust
process_exit_code, Gatling assertions, plus a check on dropped or delayed arrivals. - Re-learn the licence map: AGPL-3.0 (k6), MIT (Locust), Apache 2.0 plus the Highcharts report licence (Gatling), and where each commercial cloud starts.
Closing
Load testing in 2026 is a measurement design problem before it is a tool problem. Grafana k6 gives you one binary, JS or TS scripts, arrival-rate executors and pipeline-failing thresholds. Locust gives you plain Python and built-in workers, if you remember its users are closed and pace them carefully. Gatling gives JVM teams an explicit open or closed DSL and strict assertions, with multi-node runs on the commercial side. In the drill, open models caught a 3-second stall that closed models waved through. Learn the capacity question you are really asking. Unlearn concurrent users as the default. Re-learn coordinated omission. Practise a stall drill like this one on a laptop. Apply the open model to your next public spike before you run another performance test.
Sources
Grafana k6
- grafana/k6 on GitHub
- k6 v2.3.0 release
- k6 v2.0.0 release
- k6 v1.8.2 release
- k6 v1.0.0 release
- k6 LICENSE (AGPL-3.0)
- k6 open and closed models
- k6 executors
- k6 dropped iterations
- k6 thresholds
- k6 built-in metrics
- k6 versioning and stability guarantees
- k6 running distributed tests
- grafana/k6-operator on GitHub
- Grafana Cloud k6
- Grafana Labs press release on acquiring k6 (17 June 2021)
Locust
- locustio/locust on GitHub
- Locust 2.46.7 release
- Locust LICENSE (MIT)
- Locust on PyPI
- What is Locust?
- Writing a locustfile (wait_time)
- Locust distributed load generation
- Locust running without the web UI (exit codes, CI)
- Locust configuration
- Locust Kubernetes operator docs
- locustio/k8s-operator on GitHub
- Locust hosted load testing
Gatling
- gatling/gatling on GitHub
- Gatling v3.16.0 release
- Gatling LICENSE (Apache 2.0)
- gatling/gatling-js on GitHub
- Gatling JS v3.16.1 release
- Gatling installation
- Gatling injection
- Gatling workload models
- Gatling assertions
- What’s new in Gatling 3.16
- What’s new in Gatling 3.12
- Gatling project licences
- Gatling-Highcharts License
- Gatling core on Maven Central
Concepts and security records
- wrk2 README on coordinated omission
- GHSA-vqxw-9pg7-v7v9 (Locust web UI XSS)
- CVE-2020-28364 on cve.org
- OSV PYSEC-2020-60
- OSV database
- Eclipse Temurin (JDK used in the drill)
Internal
Releted Posts
Continuous profiling in 2026: revisit Grafana Pyroscope, Parca and the OpenTelemetry eBPF profiler before you store another flame graph
Picture a GST invoicing SaaS in Pune with about 1,800 business customers (an invented example, not a real client). Its platform team of six runs about 40 services, mostly Go with a few Java and Python ones, on a 12-node Kubernetes cluster.
Read moreLog storage in 2026: revisit Grafana Loki, VictoriaLogs and OpenObserve before you keep another month of logs
Picture a platform team in Pune that runs a B2B lending stack for NBFCs across Maharashtra and Gujarat (an invented example, not a real client).
Read moreContainer registries in 2026: revisit Harbor, Zot and CNCF Distribution before you push another image
Picture a product engineering team in Bengaluru that runs about forty microservices on Kubernetes for a B2B payments product used by NBFCs across Karnataka and Maharashtra (an invented example, not a real client).
Read more