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 injectOpen and injectClosed; 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 --address API 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

  1. Write the capacity sentence: arrival rate, duration, percentile, error budget, server-side limit.
  2. Decide open or closed from how real users behave when the system slows down.
  3. Convert “concurrent users” to arrival rate with Little’s law.
  4. Take the journey mix from production logs.
  5. Pin versions (k6 v2.3.0 or v1.8.x, Locust 2.46.7, Gatling 3.16.0).
  6. Prove the generator reaches the target rate against a stub.
  7. Gate on p95 or p99, error rate, and dropped or delayed arrivals.
  8. Collect server-side metrics and traces for the same window.
  9. Confirm licences with legal, including Gatling’s report module.
  10. Get written approval and inform on-call and third-party owners.

Common mistakes

  1. Testing an open public system with a fixed number of virtual users and trusting the p95.
  2. Reporting the average response time instead of p95, p99 and max.
  3. Ignoring dropped_iterations in k6 or the actual achieved rate in Locust.
  4. Spawning all Locust users at the same instant with identical pacing, which creates synchronised bursts instead of steady arrivals.
  5. Running the load generator on the same machine as the service under test, then blaming the service.
  6. Using check() in k6 and assuming failed checks fail the pipeline.
  7. Forgetting that Gatling’s HTML report module is not under Apache 2.0.
  8. Treating a 5-minute test as proof of 2 hours of stability; caches, connection pools and GC behave differently over time.
  9. Testing one endpoint in isolation when the real spike also hits login, static files and PDFs.
  10. 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 39c3117b6af817592dcd0ce4242105c0a7af10948c2a425306f0be8f7a8a8ab1 matched k6-v2.3.0-checksums.txt.
  • Locust 2.46.7 via pip install locust==2.46.7 in 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:test on Eclipse Temurin 21.0.12.1 (SHA-256 matched the Adoptium API value).
  • Target: a small Python ThreadingHTTPServer on 127.0.0.1:18440. GET /api/result takes 15 ms. GET /admin/stall?ms=3000 makes 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:

  1. 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.
  2. 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.
  3. Generator capacity changes the story. With only 20 pre-allocated VUs, k6 could not start 58 scheduled iterations during the stall (vus_max reached 78), so p95 looked better (560 ms). A threshold on dropped_iterations would flag this.
  4. 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).
  5. CI gates worked as documented: k6 exit 99 (“thresholds on metrics ‘http_req_duration’ have been crossed”), Locust exit 1 from the quitting hook, 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

Locust

Gatling

Concepts and security records

Internal

comments powered by Disqus

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 more

Log 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 more

Container 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