Design and execute performance tests that reveal your system's limits before production traffic does. Covers load testing strategies, tool selection, realistic traffic patterns, baseline establishment, bottleneck identification, and the reporting process that turns performance data into engineering decisions.
Performance testing and load testing exist to answer one question: what happens when your system is asked to do more than it was designed for? This page covers the practical workflow of generating traffic, measuring responses, and identifying the exact point where a service stops accepting requests, begins dropping them, or starts returning incorrect data. It matters whenever you ship a new endpoint, scale a cluster, change a database schema, or deploy a feature that could receive more traffic than your staging environment saw. The following sections are organized by the specific task you’re trying to complete, with concrete commands, flags, and error text you can copy today.
wrk is a lightweight, embeddable HTTP benchmarker that runs on Linux, macOS, and WSL. It requires no runtime framework and is available via most package managers (apt-get install wrk, brew install wrk, or download the binary from the official release page).
Exact command and flag breakdown:
wrk -t12 -c400 -d30s -s ./lua/rate_limit.lua http://localhost:8080/api/search
-t12: spawn 12 threads to generate requests. On a multi‑core host, set this to your CPU core count; on a single‑core host, keep it at 1 or 2 to avoid context‑switch overhead.-c400: maintain 400 TCP connections across those threads. This is the primary knob for concurrency.-d30s: run the test for 30 seconds sustained. wrk does not pause between requests unless a Lua script introduces think time.-s ./lua/rate_limit.lua: optional Lua script to vary request patterns, parameter sweeps, or pacing. If you omit -s, wrk round‑robins the supplied URLs at maximum speed.If you don’t need scripting, drop -s and provide the target URL(s) directly:
wrk -t12 -c400 -d30s http://localhost:8080/api/search
When wrk finishes, it prints a summary to stdout containing three fields: latency distribution, socket error rate, and requests per second. The exact format varies by version, but the fields always include a latency percentile breakdown, a Socket errors count, and a Requests/sec rate.
You can identify a breaking point by watching for these conditions in the output:
Socket errors greater than zero. This indicates connection refused, reset by peer, or TCP handshake failure. Even a single socket error under sustained load means the client or server is dropping connections.Requests/sec stopping its growth while -c remains high. This means the server is queuing requests rather than processing them in parallel; the effective throughput has peaked.To catch server‑side errors that wrk may still count as successful responses, run a parallel health‑check loop and watch for HTTP status codes:
while true; do curl -o /dev/null -s -w "%{http_code} %{time_total}\n" http://localhost:8080/api/search; done &
Look for 503, 504, or 429 codes appearing alongside increasing latency. A sudden influx of 503 responses typically means the server’s admission control (rate limiting, circuit breaker, thread‑pool exhaustion) is actively rejecting traffic.
To pinpoint the exact breaking point, increment -c in steps and re‑run the command until one of the signals above changes character.
-c100, run 30 s. Record the Requests/sec, latency percentiles, and Socket errors.-c200, repeat.-c400, -c800, etc.) until either Socket errors > 0 or latency percentiles exceed your acceptable ceiling.The -c value just before the shift is your current breaking point under that traffic pattern.
If you need finer granularity, decrease the step size (-c120, -c140, etc.) around the transition point. Document the exact -c, the observed latency, and the error type in your wiki so the next engineer can compare against future changes.
wrk -t controls thread count, but on a single‑core machine, extra threads add context‑switch overhead and can make performance worse. -c is the concurrency lever; -t just parallelizes generation. A common mistake is blaming -t for poor server response when -c was already saturated.wrk reports Socket errors only for TCP‑level failures. If the server returns 503 Service Unavailable or 429 Too Many Requests, wrk may still count those as successful requests unless you inspect status codes. Always pair wrk with a status‑code‑monitoring loop (the curl while‑loop above) to see what the server actually returns.wrk has no built‑in think‑time parameter. Real users think 200 ms or more between clicks. Without pacing, wrk issues back‑to‑back requests and overstates peak QPS. If pacing is required, write a Lua script with net.sleep(200) and accept that the effective duration -d will cover fewer requests.wrk resolves the target hostname once at startup. If your environment uses dynamic service discovery, load balancers with shifting IPs, or split‑brain DNS, wrk may hit stale addresses, producing spurious Socket errors. Re‑resolve by running wrk from a script that re‑queries DNS, or use a static IP/host entry for the test duration.wrk may appear to work at first, then suddenly start returning 429 responses. This is the server’s admission control, not a wrk bug. Reduce -c or add Authorization headers that match your real clients’ authentication pattern.X-Forwarded-For, terminate TLS, or enforce its own rate limits. wrk sees the proxied response, not the origin server’s raw state. Verify that the proxy is passthrough‑mode for your test, or add headers that mimic your production client so the proxy’s logic is exercised identically.Once you’ve identified the breaking point, record the exact -c value, the observed latency percentiles, and the error type. This becomes a baseline for future regressions. If you change a thread pool size, database connection pool, reverse‑proxy buffer, or any middleware config, re‑run the same wrk command and compare the summary line. Document the result alongside the change that caused it, so the next engineer doesn’t have to rediscover the limit.
This page was rewritten on 18 October 2026. It replaced a templated version that was not specific to its own title. How these pages are written and checked. If anything here is wrong, tell us at [email protected] and we will correct it.
We use cookies for analytics (Google Analytics) and advertising (Google AdSense) to improve your experience and support free content. Privacy Policy