In a Gatling simulation whose scenario feeds from `csv("credentials.csv")` with no strategy method called on it, what happens when more virtual users reach that feed step than the file has rows?
answer
- the default does not recycle
- the whole run stops, not one user
- no assertions, no report, no verdict
- message reads feeder is now empty
basics
~20 sThe default queue strategy hands out each row once, so the first virtual user to find the feeder empty stops the whole load generator. Gatling ends the run as a crash reading 'feeder is now empty', not a failed request.
solid answer
~40 sWith no strategy set the feeder is on `queue`, which consumes: each row goes out exactly once and nothing wraps. The component that owns the feeder serves records one at a time, and when a request arrives with nothing left it tells the controller to stop the load generator with a crash whose message reads `Feeder csv(credentials.csv) ... crashed: feeder is now empty` — on the JVM SDKs it also names the source line of the `feed(...)` step that drew from it. That is a run-level event, not a KO on one request: users still in flight are not allowed to finish, nothing appears in the statistics to explain the stop, and the crash propagates before assertions are evaluated, so the run produces no verdict and no HTML report.
code
java · 8 linesFeederBuilder<String> credentials = csv("credentials.csv");
ScenarioBuilder login = scenario("login")
.feed(credentials)
.exec(http("sign in")
.post("/login")
.formParam("user", "#{username}")
.formParam("pass", "#{password}"));go deeper
Be ready to say that Gatling does not recycle a feeder by default and that running out of records ends the run rather than skipping a user.
Explain the mechanism: the feed step is served centrally, an empty iterator becomes a stop-the-load-generator crash, and nothing is recorded as a failed request.
Show that you compute the records a profile will draw from feed executions rather than user counts, and that you know a crashed run leaves no assertion verdict for a pipeline to read.
Own the policy: which data must stay consuming, how stock size is provisioned and reviewed, and what a suite does when a run cannot be executed at the requested size.
## The default is `queue`, and `queue` consumes `csv("credentials.csv")` with no strategy method called on it is a **queue** feeder: records go out in file order and no record is ever handed out twice. That is deliberate — credentials are exactly the kind of data that must not be shared between two virtual users at the same time — but it makes the file a finite budget. When the rows are gone, they are gone. ## What Gatling actually does when the stock empties The `feed` step does not read the feeder on the virtual user's own thread. Gatling gives each feeder a single component that owns the iterator and serves requests for records one at a time, which is what keeps the stock consistent while thousands of users draw from it. When a request arrives and the iterator has nothing left, that component does not fail the request — it sends the controller a **stop-the-load-generator command with a crash reason**: ``` Feeder csv(credentials.csv) (defined at: MySimulation.<init>(MySimulation.java:31)) crashed: feeder is now empty. ``` Two details of that message are worth knowing: - the name in it is the feeder's own source name, so a simulation with several feeders tells you which file ran dry; - on the JVM SDKs Gatling appends the **call site of the `feed(...)` step itself**, captured by walking the stack out of Gatling's own frames at the moment the DSL builds that step. It is *not* the line where the feeder builder was declared: with `FeederBuilder<String> credentials = csv("credentials.csv")` on one line and `.feed(credentials)` three lines later, the hint names the `.feed(...)` line. The declaration site is captured for one other message only, the "could not locate feeder file" error. In JavaScript and TypeScript the hint is empty either way, because there is no way to recover the original source frame. ## Why this is a run-level event, not a failed request The consequences follow from *where* the stop is raised: 1. The controller treats the reason as a crash and begins stopping everything, cancelling any `maxDuration` timer that was pending. 2. Virtual users already mid-scenario are not given a chance to finish — but that is not what separates a crash from a graceful stop. The controller routes every stop reason through the same branch, and `maxDuration` likewise "forces your run to terminate based on a duration limit, even if some virtual users are still running". In-flight users are cut off either way; the difference is the outcome, and it is point 5 below. 3. The failure is **not** recorded as a KO against a request. No sampler failed; the step never got as far as issuing one. So the run's own statistics contain no trace of the reason it stopped. 4. The `after` hook still runs. 5. The crash propagates out of the run as an exception, so Gatling never reaches the stage where it evaluates assertions and generates the HTML report. **A feeder crash therefore produces no assertion verdict at all** — it is not the "assertions failed" outcome, and a pipeline that only inspects assertion results has nothing to inspect. ## How many records the run really needs The common sizing mistake is to count virtual users. What is consumed is **`feed` step executions**, which is a different number: - a `feed` inside `repeat(10)` takes ten records from one virtual user; - a scenario with two separate `feed` steps takes two records per pass; - in an open model the number of users that start is the arrival rate multiplied by the duration, not the concurrency you see on the report. So a profile of 20 users per second for 30 minutes, each user feeding once, draws 36,000 records — and a 5,000-row credentials file dies about four minutes in. ## The ways out - **Grow the stock** so it outlasts the profile. This is the only fix that keeps the uniqueness guarantee. - **Draw fewer records** — move the `feed` out of a loop, shorten the run, or lower the arrival rate. - **Change strategy** to `random` or `circular`, which never empty. This works only if the data does not have to be unique; on credentials it trades a crash for two users sharing one identity. - `shuffle` does **not** help: it permutes the same finite stock and empties after exactly the same number of `feed` executions. ## Distinguishing it from the other ways a run ends It is worth being able to tell this apart from the endings that look similar in a log: - **`maxDuration` reached** — a graceful stop. Users still in flight are cut off exactly as they are on a crash, but the run itself completes: assertions are evaluated and the report is generated. Nothing is lost. - **An empty feeder** — a crash. Assertions are never reached and no report is produced. - **A stop requested from the scenario code** — graceful, and reported as such. The distinction matters because only the graceful endings leave a verdict behind. A crashed run is not a red run; it is a run with no result, and any automation that consumes Gatling's output has to treat "no result" as a failure rather than as a missing red flag.
- Is the crash attributed to the virtual user that found the feeder empty?No. The feed step is served by a single component that owns the iterator; when it finds nothing left it asks the controller to stop the load generator. Nothing is recorded as a failed request, so the run's own statistics contain no trace of why it ended.
- Two `feed(...)` steps in one scenario — do they share the stock?Only if both were handed the same feeder builder value; Gatling keys the shared feeder on that object's identity. Two separate `csv("credentials.csv")` calls build two independent feeders, each reading the whole file from the top, so the same credentials go out twice and the uniqueness guarantee is lost.
saying these in an interview costs you the question
- Expecting the feeder to wrap back to the first row automatically
- Thinking only the affected virtual user fails rather than the run
- Sizing the file by user count when a loop feeds repeatedly
- Reading a feeder crash as an assertion failure; assertions never ran