skip to content

Sample Kinds

Splits recorded samples by who declared them: the ones k6 registers at startup and the ones your script constructs. Interviewers probe it because both end up as the same four types.

on this pageshow

explore

questions

9

Which of k6's four metric types does each built-in such as vus, http_reqs or checks use?

level: juniorimportance: must knowfreq 66%

answer

  1. four types, one fixed per metric
  2. counts versus durations versus levels
  3. only two Gauges, only two Rates
  4. vus and vus_max are the Gauges
  5. every _duration built-in is a Trend

basics

~20 s

k6 has four metric types: Counter, Gauge, Rate and Trend. Among the built-ins, vus and vus_max are Gauges, http_reqs and iterations are Counters, checks and http_req_failed are Rates, and every duration such as http_req_duration is a Trend.

solid answer

~30 s

k6 registers every metric as one of **Counter**, **Gauge**, **Rate** or **Trend**, and each built-in's type is fixed by k6 itself. The two Gauges are `vus` and `vus_max`. The Counters are the tallies: `iterations`, `http_reqs`, `dropped_iterations`, `data_sent`, `data_received` and the WebSocket message counts. The two Rates are `checks` and `http_req_failed`. Everything that measures a duration is a Trend — `http_req_duration` and the rest of the `http_req_*` timings, plus `iteration_duration`, `group_duration`, `grpc_req_duration` and the WebSocket timings. The rule of thumb in k6 v2: counts are Counters, durations are Trends, and only four built-ins fall outside that.

go deeper

for a junior

Learn the four type names — Counter, Gauge, Rate, Trend — and be able to place vus, http_reqs, checks and http_req_duration correctly. That mapping alone answers most screening questions.

for a middle

Explain why each built-in got its type: tallies are summed, levels are sampled, pass proportions are rates, and durations need a distribution rather than a single figure.

for a senior

Know the whole roster including the WebSocket and gRPC built-ins, and be able to say instantly which candidate names — http_req_total, checks_failed, vus_active — are not real k6 metrics.

for a principal

Recognise that the fixed type of each built-in is a contract k6 sets for you, so any shared convention your teams build on these names inherits both the name and the type.

## k6's four metric types Every metric in k6 — built-in or otherwise — is one of exactly four types, and the type is fixed when the metric is registered: - **Counter** — sums the values added to it. - **Gauge** — tracks the smallest, the largest and the latest value. - **Rate** — tracks how often a non-zero value occurs. - **Trend** — accumulates many values and computes statistics over them. You choose the type for a metric of your own. For the built-ins you do not: k6 registers each of them at startup with a type already decided, and that decision is what tells you how the numbers behave. ## The built-in roster by type In k6 v2 the built-in metrics and their registered types are: | Built-in metric | Type | What it measures | |---|---|---| | `vus` | Gauge | Virtual users currently active | | `vus_max` | Gauge | Virtual users k6 has initialised | | `iterations` | Counter | Completed iterations of the default function | | `iteration_duration` | Trend | Wall-clock time of one full iteration | | `dropped_iterations` | Counter | Iterations that were not started | | `checks` | Rate | Rate of passing checks | | `group_duration` | Trend | Time spent inside a group | | `http_reqs` | Counter | HTTP requests k6 generated | | `http_req_failed` | Rate | Rate of requests judged failed | | `http_req_duration` and the other `http_req_*` timings | Trend | Per-phase request times | | `data_sent`, `data_received` | Counter | Bytes written and read on the wire | | `ws_sessions`, `ws_msgs_sent`, `ws_msgs_received` | Counter | WebSocket session and message tallies | | `ws_ping`, `ws_session_duration`, `ws_connecting` | Trend | WebSocket timing measurements | | `grpc_req_duration` | Trend | gRPC response time | Three patterns run through that table and are worth holding onto: 1. **Anything that counts occurrences is a Counter** — `iterations`, `http_reqs`, `dropped_iterations`, `data_sent`, `data_received`, and the WebSocket message tallies. 2. **Anything that is a duration is a Trend** — every `http_req_*` timing, plus `iteration_duration`, `group_duration`, `ws_session_duration`, `ws_ping`, `ws_connecting` and `grpc_req_duration`. 3. **Only two built-ins are Rates** (`checks` and `http_req_failed`) and **only two are Gauges** (`vus` and `vus_max`). ## Value units are a second, separate property Beyond the type, k6 registers some built-ins with a value unit. The duration Trends carry a **time** unit and `data_sent`/`data_received` carry a **data** unit, which is why k6 renders one family in milliseconds and the other in bytes. The unit does not change the type: a byte Counter is still a Counter. ## Names that look like built-in metrics but are not Two traps sit next to this roster: - **`checks_total`, `checks_succeeded` and `checks_failed`** appear in k6's end-of-test summary as three display lines derived from the single `checks` Rate. They are presentation names, not metrics k6 registers. - **`http_req_total`, `http_req_looking_up` and `vus_active`** do not exist in k6 at all. The real names are `http_reqs` for the request count and `vus` for the active-VU count, and k6 has no built-in for the DNS lookup phase. Two further name confusions are worth naming explicitly: - **The request counter is `http_reqs`** — plural, with no `_req_` timing prefix — while every timing metric is singular, `http_req_*`. - **There is one built-in named `checks`.** k6 does not mint a separate metric per check name, so no `checks_login` or `checks_status_200` metric exists. ## Where a built-in's type is fixed k6 keeps a single registry of metrics keyed by name, and each entry carries its type. The built-ins are registered into that registry when k6 starts, before your script runs, which is why you never choose their types and why the same name always means the same type across every run. Two rules fall out of the registry's design: 1. **A metric name maps to exactly one type.** Registering a name that already exists with a different type is an error, not a redefinition. 2. **Names obey a fixed shape.** A k6 metric name must start with a letter or an underscore and contain only ASCII letters, digits and underscores, up to 128 characters — which is why every built-in reads as lowercase words joined by underscores. That shape is also a useful sanity check on a half-remembered name: anything with a dot, a dash or a capital letter in it is not a k6 metric name. ## Why the type is the fact to remember The type is what makes a built-in's numbers legible. `vus` being a Gauge is why it reports a current level rather than a running total; `http_reqs` being a Counter is why it only ever grows; `checks` being a Rate is why it is expressed as a proportion; and `http_req_duration` being a Trend is why k6 has a distribution of values for it rather than one number. Learn the roster above and the behaviour of every built-in follows from it.

  • Which two built-in k6 metrics are Rates, and what does each one's rate express?
    `checks` and `http_req_failed`. `checks` expresses the proportion of check evaluations that passed, and `http_req_failed` the proportion of HTTP requests k6 judged to have failed. A Rate in k6 tracks how frequently a non-zero value occurs, so both read as a fraction.
  • Is checks_failed a built-in k6 metric you can find in the metric stream?
    No. k6 registers a single built-in Rate named `checks`. `checks_total`, `checks_succeeded` and `checks_failed` are three display lines the end-of-test summary derives from it, not metrics of their own.
  • Why is data_received a Counter rather than a Trend even though it reports kilobytes?
    Because it tallies bytes, and a tally is summed rather than distributed. k6 registers it as a Counter with a data value unit; the unit governs how the figure is rendered, while the Counter type governs that the values are added up.

saying these in an interview costs you the question

  • Calling http_req_duration a Gauge because it reports a current time
  • Naming the request counter http_req_total instead of http_reqs
  • Treating checks_failed as a registered k6 metric rather than a summary line
  • Saying vus is a Counter because it goes up during a ramp
  • Believing you pick the type of a built-in metric yourself
open as a page

Which four metric classes does k6's k6/metrics module export, and what does each keep?

level: juniorimportance: must knowfreq 76%

basics

~20 s

k6/metrics exports Counter, Gauge, Rate and Trend. Counter sums added values and reports count and rate; Gauge keeps the last value; Rate reports the share of non-zero values; Trend keeps every value, giving avg, min, max, med and p(N).

open as a page

In k6, which three metrics add up to http_req_duration, and what does it leave out?

level: middleimportance: must knowfreq 74%

basics

~10 s

http_req_duration equals http_req_sending plus http_req_waiting plus http_req_receiving. It excludes http_req_blocked, http_req_connecting and http_req_tls_handshaking, so connection setup never appears in it.

open as a page

In k6, what happens if you construct a k6/metrics Trend inside the default function instead of at module scope?

level: middleimportance: must knowfreq 64%

basics

~10 s

Constructing a k6 custom metric outside the init context throws "metrics must be declared in the init context". Only .add() may run inside an iteration; the k6/metrics constructors must sit at module scope.

open as a page

In k6, which k6/metrics types give you p(95) for a checkout step and a pass share for response validation?

level: middleimportance: must knowfreq 70%

basics

~20 s

A Trend for the checkout step and a Rate for the validation. Trend is the only type that stores every value, so only it yields p(95); Rate divides non-zero adds by total adds to give the pass share.

open as a page

In a k6 run, why is iteration_duration usually far larger than http_req_duration?

level: middleimportance: should knowfreq 53%

basics

~20 s

iteration_duration times one whole run of the default function, sampled once per iteration; http_req_duration times one request, sampled once per request. The iteration span also holds sleeps, connection setup and your own JavaScript, none of which http_req_duration contains.

open as a page

In k6, what is the difference between the built-in vus and vus_max metrics?

level: middleimportance: should knowfreq 45%

basics

~20 s

k6 registers vus and vus_max as built-in Gauges, emitted once per second by its scheduler. vus reports the virtual users currently active; vus_max reports the virtual users k6 has initialised, so vus never exceeds vus_max.

open as a page

Why does k6 report http_req_connecting and http_req_tls_handshaking as 0 for most requests in a run?

level: seniorimportance: should knowfreq 49%

basics

~10 s

http_req_connecting and http_req_tls_handshaking are built-in Trends timing the TCP connect and the TLS handshake. A request served by an already-open pooled connection has neither to time, so k6 records 0 on both.

open as a page

In k6, why might a custom metric's .add() call record nothing and leave only a warning in the log?

level: seniorimportance: should knowfreq 52%

basics

~20 s

Because k6's .add() accepts only numbers and booleans. undefined, null, NaN, a non-numeric string or an object is dropped with an "is an invalid value for metric" warning and add() returns false - an error only if options.throw is set.

open as a page