How to Read a k6 Performance Report
A complete, line-by-line explanation of every core metric, percentile, and threshold—with a realistic report example and practical bottleneck-diagnosis tips.
Introduction
A k6 report packs latency distributions, throughput, concurrency, error rates, and pass/fail verdicts into one summary. Misreading it is easy—and dangerous. A team can ship a broken release by looking only at average response time or overlooking a rising http_req_failed rate.
This guide explains the report zone by zone, shows how to read percentiles, and provides a disciplined way to decide whether a system is ready for real-world traffic.
What is k6?
k6 is an open-source, developer-centric load testing tool from Grafana Labs. JavaScript test scripts simulate virtual users (VUs) hitting an application concurrently while k6 measures how the system behaves.
- Validate response times as concurrency grows.
- Find capacity limits before real users do.
- Catch regressions in CI/CD.
- Verify SLOs and SLAs using objective measurements.
The structure of a k6 summary report
When you run k6 run script.js, the final output has four useful zones: the execution header, functional checks, the metrics table, and thresholds.
Header describes execution mode, script, scenario, VUs, and duration. Checks are custom assertions. The metrics table contains timing, count, rate, and gauge data. Thresholds are the authoritative automated verdict.
Key metrics explained
http_reqs
Total HTTP requests and requests/second. This is your throughput indicator; a plateau as VUs rise can signal saturation.
http_req_duration
Total request time: sending + waiting + receiving. Read its percentiles, not only its average.
http_req_waiting
Time to first byte (TTFB), usually the largest component. High waiting with low send/receive time points to backend processing.
http_req_blocked
Time waiting for a free client connection slot. High values can indicate connection-pool exhaustion.
http_req_connecting
TCP connection setup time, excluding TLS. High values suggest network or connection overhead.
http_req_tls_handshaking
HTTPS handshake time. Slow values can indicate poor reuse or an overloaded TLS terminator.
http_req_sending
Time to send request data; typically relevant for large uploads.
http_req_receiving
Time to receive the response. Large bodies and slow delivery increase this value.
http_req_failed
Percentage and count of failed requests. Read it alongside latency: a service can be fast because it fails fast.
checks
Pass rate of custom assertions. A 200 response can still fail a body or business-logic check.
iteration_duration
Time for a complete VU loop, including all requests, sleeps, and script processing.
iterations · vus
Completed loop count and current/max virtual users. Compare concurrency with throughput to assess scaling.
data_sent · data_received
Total network bytes transferred; useful for bandwidth planning and finding oversized payloads.
group_duration
Duration of each group() block, useful for locating the slowest step in a user journey.
Percentiles: p50, p90, p95, p99
Never trust the average alone. Two systems can average 200ms while one has a painful long tail. Percentiles expose the experience of slower users.
At 10,000 requests per minute, a 3-second p99 still means roughly 100 requests per minute are that slow. “Rare” does not mean irrelevant at scale.
Thresholds and pass/fail criteria
Thresholds turn measurements into an automated CI/CD decision. Define latency, error-rate, and functional criteria together:
export const options = {
thresholds: {
http_req_duration: ['p(95)<500'],
http_req_failed: ['rate<0.01'],
checks: ['rate>0.99'],
},
};Use a baseline before setting targets, keep safety margin below external SLAs, add tagged thresholds for critical endpoints, and consider abortOnFail for costly tests. A failed threshold makes the overall run fail—even if other metrics look healthy.
Metric types
Example report walkthrough
- Check thresholds first. The displayed checkmarks are authoritative; verify the configured expression and measured value together.
- Check errors. Pair
http_req_failedwithchecks; protocol success does not guarantee correct content. - Inspect latency. Compare p95/p99 against the SLO, then break duration into waiting, sending, receiving, blocked, and connection time.
- Check load and throughput. Compare
http_reqs/swith VU levels and look for a plateau. - Separate warm-up from steady state. Final summaries combine the whole run, so use time-series output for precise windows.
Important: Never infer pass/fail from a metric value alone. The threshold expression, tags, abort settings, and actual ✓/✗ output are the source of truth.
Common pitfalls
- Trusting averages over p95/p99.
- Ignoring
http_req_failedbecause latency is low. - Mixing ramp-up measurements with steady-state results.
- Confusing checks with HTTP request failures.
- Treating a single maximum as typical behavior.
- Comparing runs with different VU counts, durations, or load profiles.
- Overlooking blocked and connection time, which may indicate a client or network bottleneck.
Exporting and visualizing
- JSON:
k6 run --out json=results.json script.jsfor raw measurements. - HTML: use a community reporter such as k6-reporter for shareable reports.
- Grafana: stream to InfluxDB or Prometheus for dashboards and capacity-knee analysis.
- k6 Cloud: get hosted visualization, history, and geographic load distribution.
- CI/CD: rely on k6’s non-zero exit code when thresholds fail.
Actionable checklist
- Check thresholds first.
- Read p95/p99, not averages alone.
- Pair latency with error rate.
- Use checks for business and response correctness.
- Break duration down to localize bottlenecks.
- Judge steady state separately from ramp-up.
- Export results to track trends across releases.
FAQ
95% of measured requests completed faster than the stated value; 5% were slower.
Duration covers sending, waiting, and receiving. Waiting is the server-response/TTFB portion. Their difference helps locate network versus backend time.
Request failure is protocol-level; checks are custom assertions. A 200 response can pass the former and fail the latter.
Use a percentile—typically p95 or p99—because averages hide tail latency.
No comments:
Post a Comment