Articles
k6 Interview Questions: Arrival Rates, Thresholds, and Dropped Iterations
Prepare for k6 interviews by explaining arrival rates, VU allocation, checks, thresholds, dropped iterations, and the limits of a passing latency result.
k6 interview questions become interesting when a test reports fast responses but fails to generate the requested traffic. A dashboard showing a 180 ms p95 does not establish that an API handled 100 arrivals per second if the load generator skipped a third of its scheduled iterations. You need to explain the workload, the observations, and the limits of the conclusion together.
Build an answer around three questions: Did we deliver the intended load? Did the application return the right result? Did those results meet the latency objective?
Evidence boundary: Grafana's official documentation supplies the k6 behavior described below. Workloads, numbers, and diagnostic exercises are original preparation examples, not production benchmarks or verified employer interview scripts. Candidate-reported question records are linked as related practice; they do not establish that an employer uses k6.
Choose an executor from the traffic model
Official behavior: k6 distinguishes open and closed workload models. A closed model waits for an iteration to finish before that VU begins another. Arrival-rate executors start iterations on a schedule, subject to available VUs. When responses slow, a closed model can produce fewer new arrivals because its users remain occupied. Grafana: open and closed models.
An interviewer asks, “We expect 100 checkout attempts each second. Should we use 100 VUs?” Your first answer should be that users and arrival rate are different quantities. One hundred VUs executing a slow, multi-step checkout might start far fewer than 100 checkouts each second. A fast loop might start far more.
Use an arrival-rate model when the question concerns independently arriving work: incoming search requests, payment attempts, or job submissions. A closed model can be appropriate when the question concerns a fixed population of active users who wait before continuing. Neither model is universally more realistic; explain whose behavior the test represents.
Then define the boundary of an iteration. Does it contain one search request, or login, search, and checkout? Does it include retries and realistic processing between requests? Without that definition, “100 per second” remains ambiguous.
Arrival rate counts iterations, not every HTTP request
Official behavior: constant-arrival-rate spaces iteration starts across its configured timeUnit; it does not start the entire second's allocation simultaneously. It continues at the configured rate while VUs are available. The executor already handles pacing, so adding a final sleep merely to regulate arrival rate is unnecessary. Grafana: constant arrival rate.
Consider an original scenario with 20 iterations per second. Each successful iteration sends three requests: create a cart, add an item, and submit checkout. Under that exact path, the workload produces approximately 60 requests per second during steady execution. Early failures, retries, redirects, and conditional branches can change the relationship. Grafana's API load-testing guide also distinguishes request rate from iteration rate. Grafana: API load testing.
A useful interview answer labels both quantities. “I will schedule 20 checkout journeys per second and separately measure HTTP requests and successful checkouts. I will not infer business throughput from the aggregate request counter.”
This matters when a retry improves the success percentage while doubling downstream traffic. The user journey may still complete, but its cost and pressure on the service have changed. Keep the retry policy fixed when comparing two builds, or explain the change explicitly.
Size VU capacity around iteration duration
Official behavior: each running iteration needs a VU. Arrival-rate scenarios therefore require enough allocated VUs to accommodate the time iterations remain active. Grafana recommends pre-allocation and notes that allocating additional VUs during a test can itself consume resources and affect results. Grafana: arrival-rate VU allocation.
For an initial estimate, think in units: arrivals per second multiplied by seconds per iteration gives concurrent iterations. At 100 arrivals per second and roughly 0.2 seconds per iteration, the average occupancy is around 20 VUs under stable conditions. That is an explanatory estimate, not a safe allocation guarantee. Variation, long tails, network waits, and service slowdown require headroom and a trial run.
If iteration duration grows to two seconds, the same target needs roughly 200 concurrently occupied VUs. A test capped at 40 cannot maintain that schedule. The shortage may be a consequence of the application slowing down, even though the immediate reason an iteration is dropped is the absence of a free VU.
Avoid solving every drop by raising maxVUs. Establish the intended workload, observe generator CPU and memory, and choose an allocation the generator can support. Otherwise, you may replace one untrustworthy experiment with another.
A small script with explicit pass conditions
This original teaching script assumes a controlled API whose /health response is status 200 with the exact body ok. Replace that contract with a meaningful business response for your own exercise. The numbers illustrate configuration; they are not recommended production SLOs.
import http from 'k6/http';
import { check } from 'k6';
export const options = {
scenarios: {
probe: {
executor: 'constant-arrival-rate',
rate: 10,
timeUnit: '1s',
duration: '30s',
preAllocatedVUs: 20,
},
},
thresholds: {
http_req_failed: ['rate<0.01'],
http_req_duration: ['p(95)<500'],
checks: ['rate>0.99'],
dropped_iterations: ['count==0'],
},
};
export default function () {
const response = http.get(`${__ENV.BASE_URL}/health`);
check(response, {
'health contract holds': (r) =>
r.status === 200 && r.body === 'ok',
});
}
Set BASE_URL to an environment you are authorized to test. Because maxVUs is omitted, this scenario uses the documented default ceiling equal to preAllocatedVUs. The script deliberately contains one request and one combined correctness check, making the relationship between observations easier to explain.
This is a short configuration exercise, not a capacity study. A health endpoint may bypass authentication, storage, and other expensive paths. Passing it cannot establish checkout capacity. Before a real workload test, verify the script at low load against representative data and validate that intentional failures actually fail the run.
Checks and thresholds answer different questions
Official behavior: a failed k6 check records a failed condition but does not automatically stop execution or fail the overall test. A threshold defines aggregate pass/fail criteria and can produce a nonzero exit status when it fails. Grafana: checks and thresholds.
In the example, the check asks whether a particular response satisfies the health contract. The threshold asks whether the fraction of successful check evaluations exceeds 99%. If an iteration later contains five checks, that aggregate is a percentage of check evaluations, not necessarily the percentage of successful user journeys.
Similarly, an HTTP success classification does not prove business correctness. A status 200 with another tenant's record is a serious failure even if transport and latency metrics look healthy. Name the assertion that distinguishes the correct response from a fast incorrect one.
Define thresholds before reviewing results. Moving the p95 target from 500 to 900 ms after seeing a failure changes the acceptance criterion; it does not fix performance. If the objective changes legitimately, preserve both the old result and the new rationale.
Diagnose dropped iterations with a load ledger
Official behavior: dropped_iterations counts work the executor did not start. In arrival-rate executors, this can happen when there are no free VUs. Iteration-based executors can also drop work when their allowed duration expires, so inspect the executor before interpreting the metric. Grafana: dropped iterations.
Use this original diagnosis ledger to avoid jumping from one counter to a root cause:
| Observation | Plausible explanation | Evidence to collect next |
|---|---|---|
| Drops begin immediately, with stable fast responses | Too little allocated capacity for the full iteration | Full iteration duration, VU occupancy, initialization behavior |
| Drops increase after latency rises | Slower service holds VUs longer | Service queues, downstream latency, active VUs over time |
| Generator CPU is saturated while service utilization stays low | The generator may be limiting delivery | Generator resources, script cost, connection behavior |
| Completed requests are fast but many arrivals were dropped | The observed sample excludes unsent work | Scheduled, started, completed, interrupted, and dropped totals |
| Drops appear only after adding retries | Iterations now occupy VUs longer | Retry counts and iteration duration distribution |
Suppose a simplified accounting interval contains 3,000 scheduled arrivals, 2,700 starts, and 300 drops. If all starts later finish successfully, the responses can still look excellent. The run did not demonstrate successful handling of all 3,000 scheduled arrivals. If some starts are interrupted at shutdown, distinguish those from iterations that never started.
Do not convert the 300 drops into HTTP 500 responses. No corresponding request necessarily reached the application. Report them as missed workload delivery and investigate the cause before making a capacity claim.
Read latency metrics with their denominator
Official behavior: k6 defines http_req_duration from HTTP sending, waiting, and receiving time; connection establishment is represented by separate timings. Iteration duration and HTTP request duration therefore describe different boundaries. Grafana: built-in metrics.
A strong report names the population behind a percentile. Is p95 calculated across all endpoints, a specific operation, or only a filtered subset? A fast high-volume health check can overwhelm the aggregate distribution and hide a slower low-volume checkout. Separate critical operations rather than allowing easy requests to dominate the headline.
Also record the number of observations and the test duration. A brief test may miss cache turnover, token renewal, autoscaling delays, or resource leaks. Warm-up behavior can be relevant evidence, but compare it consistently instead of silently removing inconvenient samples from one build.
When an interviewer asks whether the system supports 100 requests per second, give a bounded conclusion: “Under this request mix, dataset, deployment, and duration, we delivered the target workload with these correctness, latency, and drop results.” That is more useful than a capacity number detached from its experiment.
Before rerunning a failed experiment, write down one proposed explanation and one change that can test it. If you suspect insufficient allocation, a controlled increase in pre-allocated VUs should improve workload delivery without exhausting the generator. If service queues continue growing and response times worsen, more VUs may expose the service limit more clearly rather than solve it. If you change both allocation and application code, the comparison becomes harder to interpret.
A useful result bundle contains the script revision, workload parameters, environment, dataset description, measured arrivals and completions, correctness failures, latency distributions, and generator observations. Keep the original failed run alongside the follow-up. This lets a reviewer assess whether the conclusion follows from the evidence instead of trusting a green CI badge.