Your k6 `constant-arrival-rate` scenario sets `rate: 100` but the search endpoint sees 300 requests/s. Why?
answer
- the unit is not the request
- one start runs the whole function
- count the calls per iteration
- redirects add a round trip each
- one scenario per endpoint fixes it
basics
~10 sk6's rate counts iteration starts, not HTTP requests. Each start runs the whole exec function, so an iteration issuing three calls turns rate 100 into about 300 requests per second at the endpoint.
solid answer
~40 sIn k6 v2, `rate` on `constant-arrival-rate` counts **iteration starts**, and one iteration is a full run of the scenario's `exec` function — the `default` export unless named otherwise. If that function makes three HTTP calls, 100 starts per second produce about 300 requests per second at the endpoint; redirects, which k6 follows by default, multiply it further. There are three fixes: make the iteration issue exactly one request so the two numbers coincide, divide the `rate` by the requests per iteration, or split each endpoint into its own entry in the `scenarios` map with its own `exec` function and `rate`. The last is usually clearest, because each rate then stays readable.
code
javascript · 19 linesimport http from 'k6/http';
export const options = {
scenarios: {
search_only: {
executor: 'constant-arrival-rate',
rate: 100,
timeUnit: '1s',
duration: '10m',
preAllocatedVUs: 50,
maxVUs: 300,
exec: 'search',
},
},
};
export function search() {
http.get('https://example.com/search?q=laptop');
}go deeper
Recall that a k6 iteration is one run of the scenario's function, and that rate counts those runs, so the request count depends on what the function does.
Explain the multiplier: requests per second equals rate times the requests one iteration issues, redirects included, and show the arithmetic on a concrete iteration body.
Demonstrate the diagnosis — read the exec function, count calls and redirects, check for a second scenario on the same endpoint — and then pick a fix that stays correct when the script changes.
Argue for a script structure where the load figure cannot drift: one scenario per endpoint with a single-call exec function, so the rate written in the config is the number a reviewer can trust without reading the body.
## What `rate` actually counts In k6 v2 the `rate` key of `constant-arrival-rate` — and the `target` of a `ramping-arrival-rate` stage — counts **iteration starts**, and nothing else. An **iteration** is one top-to-bottom run of the scenario's `exec` function, which is the script's `default` export unless the scenario names another. k6 schedules starts; whatever the function does once started is entirely up to your code. So `rate: 100` is a promise about how often that function is entered, not about how much traffic reaches the endpoint. If the function issues three HTTP calls before it returns, the endpoint sees roughly 300 requests per second while the scenario reports a rate of 100. The gap is not a k6 defect and not a measurement error: it is the multiplier you wrote into the iteration body. | what you wrote in the iteration | `rate: 100, timeUnit: '1s'` yields | |---|---| | one `http.get` to the search endpoint | ~100 requests/s at that endpoint | | a login call, then the search call | ~100 requests/s at each, ~200 total | | three calls in a loop | ~300 requests/s | | one call that follows a redirect | ~200 requests/s, one being the redirected fetch | ## Diagnosing the 100-becomes-300 case When a scenario configured for a flat 100 requests per second lands 300 on the search endpoint, work through the multiplier rather than the executor: 1. **Count the requests one iteration makes.** Read the `exec` function end to end, including anything it calls. Setup work that belongs once per run, not once per iteration, is the usual culprit. 2. **Count the redirects.** A request that follows a redirect is two round trips at the server, and k6 follows redirects by default. 3. **Check for more than one scenario.** The `scenarios` map may hold a second entry hitting the same endpoint. Rates are per scenario, and they add at the target. 4. **Check the request the server is actually counting.** Server-side request counts include things the client may not think of as separate calls, such as a preflight or a retried connection. ## Three ways to pin the number Once you know the multiplier, there are three legitimate fixes, and they say different things about the workload: - **One request per iteration.** Reduce the `exec` function to a single call, so *100 iterations per second* and *100 requests per second* are the same statement. This is the clearest option when the brief is written in requests per second for one endpoint. - **Divide the rate.** Keep a multi-request iteration and set `rate` to the target divided by the requests per iteration — `rate: 33` for three calls. Honest, but fragile: the number silently drifts the moment anyone adds a call to the function. - **Split into named scenarios.** Give each endpoint its own entry in the `scenarios` map, each with its own `exec` function and its own `rate`. Each endpoint's rate then becomes an independent, readable figure, which is usually the best answer when several endpoints are under test at once. The third option is worth spelling out because it is what the `scenarios` map is for. A scenario names an `exec` function, so several scenarios can share one script while each holds its own arrival rate. ## Things this is not - It is **not** the root `rps` option. That is a separate, global request-rate cap on the whole test, not a per-scenario arrival rate, and the two are frequently confused. - It is **not** `preAllocatedVUs` or `maxVUs`. Those size the pool of virtual users available to serve the schedule; neither changes how many starts are planned. - It is **not** specific to the flat executor. `ramping-arrival-rate` stage targets count iteration starts in exactly the same way, so the multiplier applies at every point on a ramp. ## The habit to carry away Before you write a `rate`, decide what one iteration *is*. In k6 the iteration is the unit of load, and a brief expressed in requests per second only translates into a `rate` value once you have fixed how many requests one iteration makes. Two working rules follow: - **Keep the multiplier at one wherever the target is stated per endpoint.** A single-call `exec` function per endpoint, named in the `scenarios` map, makes the configured rate mean what it appears to mean to the next person who reads the file. - **Re-derive the number whenever the iteration body changes.** Any commit that adds a call to an `exec` function has changed the request rate produced by every scenario pointing at it, whether or not the config file was touched.
- How do you hold two k6 endpoints at different request rates in one script?Give each its own entry in the `scenarios` map. Each entry names its own `exec` function and its own `rate` and `timeUnit`, so the two schedules are independent and each rate figure reads as that endpoint's own number rather than a shared budget.
- Does a k6 ramping-arrival-rate stage target count requests or iteration starts?Iteration starts, exactly like `rate` on the flat executor. A stage `target` of 100 means 100 starts per `timeUnit` at the end of that stage, so the same requests-per-iteration multiplier applies at every point along the ramp.
- If one k6 iteration makes three calls, is dividing rate by three a good fix?It works arithmetically — `rate: 33` for a 100 requests/s target — but it is brittle. The divisor is invisible in the config, so the moment someone adds a fourth call the scenario silently offers a different rate. Prefer one request per iteration, or one scenario per endpoint.
saying these in an interview costs you the question
- Insists rate is requests per second regardless of iteration content
- Blames k6 for over-driving instead of counting calls per iteration
- Confuses the scenario rate with the root rps option
- Thinks raising maxVUs would bring the request rate back down
- Forgets that followed redirects add a round trip each