skip to content

In k6, how do you stop a parameterised product URL from creating a new name tag value per request?

level: seniorimportance: must knowfreq 55%

answer

  1. name defaults to the request URL
  2. collapse the part that varies
  3. params tags, or a tagged template
  4. url follows whatever name becomes

basics

~20 s

Set the name tag yourself: either tags: { name: 'ProductItem' } in the request params, or k6's http.url tagged template, which renders the name as .../products/${}. k6 then sets the url tag to the same value.

solid answer

~40 s

By default k6 sets both the `name` and the `url` tag of an HTTP sample to the cleaned request URL, so a loop over product ids produces one tag value — and one series family per HTTP metric — per id. Collapse it by naming the operation instead. Either pass a `name` tag in the request params, `http.get(url, { tags: { name: 'ProductItem' } })`, or wrap the template literal in k6's `http.url` helper, which keeps the literal's static parts and replaces each interpolation slot with `${}`, giving `https://shop.test/products/${}`. Either way k6 sets the `url` tag to the same value as `name`, so you are not just moving the problem into the other indexed tag. k6 itself warns once a run passes 100,000 unique time series and points at exactly this fix.

code

javascript · 11 lines
javascript
import http from 'k6/http';

export const options = { vus: 1, iterations: 1 };

export default function () {
  for (let id = 1; id <= 100; id++) {
    http.get(`https://quickpizza.grafana.com/api/json?id=${id}`, {
      tags: { name: 'ProductItem' },
    });
  }
}

go deeper

for a junior

Know that k6 fills the name tag from the request URL by default, and that you can override it by passing tags with a name key in the request's params object.

for a middle

Explain both routes - a manual name tag and the http.url tagged template - and what each produces, including the ${} slot that the template leaves behind.

for a senior

Add the consequence nobody mentions: k6 mirrors your name onto the url tag, and the run warns past 100,000 unique time series with this exact fix in the message.

for a principal

Turn it into a convention: one agreed label per endpoint across the suite, chosen by operation rather than path, so the same request is one series wherever it appears.

## What k6 does by default In k6 v2, every HTTP sample k6 emits carries a `name` tag and a `url` tag, both system tags and both on by default. If you do nothing, k6 sets both to the **cleaned request URL** — the URL you actually requested, with any embedded credentials masked. That is a fine default for a fixed endpoint, and a trap for a parameterised one. ```javascript for (let id = 1; id <= 5000; id++) { http.get(`https://shop.test/products/${id}`); } // name = "https://shop.test/products/1" // name = "https://shop.test/products/2" … and 4,998 more ``` Every distinct tag combination on a metric is a separate time series that k6 keeps in memory for the whole run, so those 5,000 ids become 5,000 series **per HTTP metric** — `http_req_duration`, `http_req_waiting`, `http_reqs` and the rest. The k6 process notices: once a run passes 100,000 unique time series it logs a warning naming the count and the suggested limit, and pointing you at the `name` tag and URL grouping as the remedy. The limit then doubles, so the warning recurs rather than repeats on every flush. Three things are going wrong at once here: - **`name`** has one value per id instead of one value per endpoint, so no single value has enough samples behind it to characterise the endpoint at all. - **`url`** has the same 5,000 values, because with no manual `name` k6 sets both tags to the same cleaned URL. - **Every other tag** on those samples — `status`, `method`, `group`, `scenario` — multiplies against those 5,000, because a time series is the whole tag combination, not one tag. The dimension you wanted was "the product-detail endpoint"; the id was noise. ## Remedy one: set the `name` tag yourself The direct fix is to pass a `name` tag in the request's params object: ```javascript http.get(`https://shop.test/products/${id}`, { tags: { name: 'ProductItem' } }); ``` Now every one of those 5,000 requests emits `name: "ProductItem"`, and one series per metric covers the whole endpoint. The important detail — and the one interviewers actually probe — is what happens to the `url` tag. When you set `name` manually, **k6 sets `url` to the same value**. It does not keep the real URL. That is deliberate: if `url` kept the concrete path you would have moved the explosion from one indexed tag to another and gained nothing. Since k6 v0.41.0 the two tags have always held the same value, and the manual `name` is what both follow. ## Remedy two: the `http.url` tagged template k6 ships a tagged-template helper, `http.url`, that builds the name for you. Wrap the template literal with it and k6 keeps the literal's static parts and replaces each interpolation slot with `${}`: ```javascript http.get(http.url`https://shop.test/products/${id}`); // name = "https://shop.test/products/${}" ``` The request still goes to the real URL; only the tag is templated. This gives you a name that reads like the route it came from, which is the main reason to prefer it over a hand-written label. | Approach | `name` value | `url` value | Best when | | --- | --- | --- | --- | | Nothing | the concrete URL | the concrete URL | the endpoint has no variable part | | `tags: { name: 'ProductItem' }` | `ProductItem` | `ProductItem` | you want a short, stable business label | | `http.url` template | `https://shop.test/products/${}` | same as `name` | you want the route shape preserved | ## What not to reach for - **Do not wrap each request in its own `group()`.** k6's own documentation calls one group per request discouraged: it repeats what the `name` tag already carries and adds a `group_duration` sample per request for no new information. - **Do not disable the `name` tag** via `systemTags` to make the problem go away. You lose the dimension entirely, including for the endpoints where it was useful. - **Do not put the id in a user tag instead.** `tags: { productId: id }` is the same explosion under a different key, and this one is not covered by the `name`/`url` deduplication at all. ## Choosing the label 1. Name the **operation**, not the URL: `ProductItem`, `Checkout`, `SearchProducts`. It survives a path change. 2. Keep one label per endpoint per script, and reuse the same spelling everywhere — a sub-metric selector such as `http_req_duration{name:ProductItem}` matches on the exact string. 3. Prefer `http.url` when the route shape itself is the useful thing to see, and a hand-written label when the route is ugly or the same logical operation is reached by several paths. 4. Check the result: the tag you get is the one in the output, and a typo in a hand-written label quietly splits one endpoint into two.

  • What happens to the url tag when you set a name tag on a k6 request?
    k6 sets `url` to the same value as `name`. Keeping the concrete URL would move the exploding value into a second indexed tag and gain nothing, so since k6 v0.41.0 the two tags always hold the same string.
  • What exactly does http.url put in the name tag?
    The template literal with every interpolation slot replaced by `${}` — for example `https://shop.test/products/${}`. The request still goes to the fully interpolated URL; only the tag is templated.
  • What does k6 log when a run produces too many unique time series?
    Past 100,000 it warns with the current count and the suggested limit, and recommends avoiding high-cardinality tag values or using the `name` tag and URL grouping. It then doubles the threshold, so the warning recurs rather than firing constantly.
  • Would wrapping each request in group() solve the same problem?
    No, and k6's documentation calls one group per request discouraged. The group tag would carry the same varying value, and each call would add a `group_duration` sample duplicating that request's own duration.

k6's name tag is to the request URL what a route pattern is to a concrete address: /products/${} recorded once, rather than /products/8817 recorded five thousand times.

saying these in an interview costs you the question

  • Wraps each parameterised request in its own group instead
  • Thinks the url tag keeps the real URL after name is set
  • Expects k6 to collapse similar URLs automatically
  • Moves the id into a custom tag and calls it fixed
  • Disables the name system tag to silence the warning