01 / performance engineering
Locust Performance Lab
Performance testing as code. A self-contained load testing framework that ships its own misbehaving system under test — one command spins up distributed load generation, live Grafana monitoring, automated memory-leak hunting, and an SLA gate that fails the CI build when latency budgets are breached.
SLA gate — perf tests that can fail the build
Latency budgets live in slas.yml, per endpoint. A script parses Locust's CSV,
prints a verdict table into the GitHub job summary, and exits non-zero on any breach.
“A dashboard needs a human — an exit code scales.”
Memory-leak hunter
Samples container memory under constant load, discards warm-up, fits a least-squares trend — and fails only when both growth rate and total growth blow their budgets. Demonstrated against a simulated ~50 KB/request leak:
Gates hardened against a real pipeline
A green suite that lies is worse than a red one. A floating geventhttpclient stopped
URL-encoding raw spaces, so ~29% of /search requests failed on CI and every
nightly run went red — a tooling bug wearing a performance bug's costume. Search terms are now encoded, Locust is
pinned to 2.32.4 to match the Compose image, and the gates learned to tell noise from signal:
- Runs with
--exit-code-on-error 0— the SLA gate is the judge, not Locust's any-failure default (the API injects ~1% payment failures by design) - Survives Locust's
N/Apercentiles, and fails when an SLA'd endpoint is missing from the results instead of silently passing - Skips low-sample endpoints rather than gating on quantization noise; flags 0→X regressions
Load shapes as ~15 lines of code
Stress, spike and soak profiles are LoadTestShape classes selected by one env var —
one reviewed workload model serves every scenario.
class SpikeShape(LoadTestShape):
"""20-user baseline, sudden 10× spike."""
def tick(self):
t = self.get_run_time()
if t < 120: return (20, 5)
if t < 180: return (200, 50) # the spike
if t < 360: return (20, 50) # recovery
return None
Regression detection, run over run
One run tells you where you are; comparing runs tells you where you're heading. Nightly CI diffs endpoint-by-endpoint against the previous nightly's artifact and fails on p95 growth, throughput drops, or worsening error rate:
Checkout is the interesting row. At ~0.3 rps it clears
--min-requests nowhere near, so it is reported and
deliberately not gated — a ±30% p95 swing computed from eight requests is
quantization noise, not evidence.
The gates have their own tests
The scripts that decide pass or fail are themselves production code, so they are tested like it: 25 pytest cases across all four scripts, run in their own CI job before any load is generated. A broken gate can't quietly wave a regression through.
Distributed by default
make up WORKERS=8 — Locust master/worker split in plain Docker Compose,
FastHttpUser for ~10× RPS per generator. The same images spread across extra
hosts by pointing workers at the master.
Live observability
Pre-provisioned Grafana dashboard: throughput and average response time per endpoint, response time plotted against the user ramp, failures/s, and the leak-hunting memory panel — problems visible the moment they start, not in a PDF afterwards.
Error budgets with a rationale
Checkout gets a 5% budget, and the config says why: with ~100 checkout samples in a 2-minute run and ~1% injected gateway timeouts, a 3% budget goes flaky on binomial noise alone. Thresholds you can defend in review.
smoke · baseline · current · stress · spike · soak · leak-test · compare — every scenario is one make target, every gate is an exit code