skip to content

In a Gatling simulation, what does `feed(feeder, 2)` put into the virtual user's Session, and what happens if only one record is left?

level: middleimportance: nice to knowfreq 24%

answer

  1. several records, one step, one user
  2. values merge into lists, not numbered keys
  3. a short batch is treated as empty
  4. random can repeat a record inside one call

basics

~20 s

It takes two records in one step and merges them by key, so each source column becomes one Session attribute holding a two-element list that you read by index. If fewer than two records remain, Gatling stops the load generator.

solid answer

~40 s

`feed(feeder, 2)` draws two records from the same shared stock in a single step for one virtual user. The records are merged by key rather than kept separate: every column of the source becomes **one** Session attribute holding a list of the two values — a `List` in Java, Kotlin, JavaScript and TypeScript, a `Seq` in Scala — so a `username` column gives you `username` as a list, not `username1` and `username2`, and you read the elements with `#{username(0)}` and `#{username(1)}`. The count can also be an expression such as `feed(feeder, "#{numberOfRecords}")`. A short batch is not tolerated: if fewer records remain than you asked for, Gatling treats it exactly like an empty feeder and stops the load generator.

code

java · 5 lines
java
ChainBuilder transfer = feed(csv("accounts.csv"), 2)
  .exec(http("transfer")
    .post("/transfers")
    .formParam("from", "#{iban(0)}")
    .formParam("to", "#{iban(1)}"));

go deeper

for a junior

Be ready to say that feeding several records gives you lists under the original column names, and that you index into them rather than looking for suffixed attribute names.

for a middle

Explain the merge-by-key shape, the index syntax that reads it back, and why a batch that cannot be filled completely stops the run instead of shrinking.

for a senior

Show that you know which strategies guarantee distinct records within one batched call, and size a batched feeder to a whole multiple of the batch size.

for a principal

Own when a batched feed is the right modelling choice at all, rather than several feeders or generated values, and what that decision costs in readability.

## One step, several records `feed(feeder, 2)` reaches into the same shared stock and takes **two records in one go**, for one virtual user, in one step. The count may be a literal, an expression that Gatling resolves per user such as `feed(feeder, "#{numberOfRecords}")`, or a function of the Session. A count of zero or less is rejected and stops the load generator. ## What lands in the Session: lists, not numbered keys This is the part people get wrong. Feeding *n* records does **not** create *n* sets of attributes with numeric suffixes. Gatling merges the records by key: for every column in the source you get **one Session attribute holding a list of the n values** — a `List` in Java, Kotlin, JavaScript and TypeScript, a `Seq` in Scala. So a CSV with columns `username,password` fed with `feed(feeder, 2)` leaves the user with: - `username` -> a two-element list - `password` -> a two-element list and **not** `username1`, `username2`. You address the elements by index in the expression language: `#{username(0)}` and `#{username(1)}`. That shape is what makes the feature useful. It is how you drive a request that genuinely needs several rows at once — a bulk upload body, a comparison between two products, a transfer between two accounts — without wiring up several feeders. ## The short-batch rule If fewer than *n* records remain, Gatling does **not** hand back a shorter list and it does not wait. The step requires the full count; anything less is treated exactly like an empty feeder, so the load generator is stopped with the same `feeder is now empty` crash. The records it managed to take before running short are consumed and gone. The practical consequence is a rounding one: with `feed(feeder, 3)` a 100-row file supports 33 calls, not 33 and a bit. Size a batched feed to a whole multiple of the batch size. ## The strategy still decides what you get The count controls *how many* records come back; the strategy still controls *which*: | strategy | what one `feed(feeder, 2)` call yields | |---|---| | `queue` | the next two records in file order, always distinct | | `shuffle` | the next two in the start-up permutation, always distinct | | `random` | two independent draws — **they can be the same record** | | `circular` | the next two in file order, wrapping past the end | The `random` row is the sharp edge. Because that strategy draws with replacement from the whole stock on every call, a single `feed(feeder, 2)` can put the same row into both slots of the list. If your request compares element 0 with element 1, or transfers from one to the other, that is a live defect in the test: the two values must differ and `random` does not promise it. `queue`, `shuffle` and `circular` all walk a cursor, so within one call they always return distinct positions — `circular` only repeats a record if the batch is larger than the file. ## Where the count belongs Two count-less `feed(feeder)` steps and one `feed(feeder, 2)` step are not the same thing. The first gives the user two scalar attributes at two points in the scenario, the second gives it two lists at one point, and only the second guarantees the records were adjacent in the source. Be careful which overload the scalar form is, because `feed(feeder, 1)` is **not** it. Any count greater than zero takes the multi-record path, and that path merges by key unconditionally: a `user` column fed with `feed(feeder, 1)` lands as a **one-element list**, so `#{user}` renders the list (`[alice]` on the Java-family SDKs) rather than `alice`. Only the overload with no count at all hands back the raw record. Choose the batched form when the request needs the records **together**; otherwise feed once with `feed(feeder)` and keep the plain scalar attributes, which read far better in the expression language. ## Reading it back The values are ordinary Session attributes, so everything that works on a list works on them. Inside a request template you index with the expression language — `#{iban(0)}`, `#{iban(1)}` — and in a function that receives the Session you read the attribute as a `List` in Java, Kotlin, JavaScript and TypeScript, or a `Seq` in Scala, and work with it directly. Because the values are lists, the attribute names no longer say how many elements there are. A scenario that feeds two records in one branch and calls the count-less `feed(feeder)` in another leaves the same key holding a list in one case and a scalar in the other, and a template written for one shape will not resolve against the other. Keep the batch size fixed for a given key, or give the batched feeder its own column names. ## When to reach for it Use a batched feed when a single request genuinely needs several records **together**: two accounts for a transfer, a set of line items for a basket, several identifiers for a bulk endpoint. Do not use it as a shortcut for handing a user two values at two different points in its journey — two ordinary count-less `feed(feeder)` steps do that, keep the attributes scalar, and read far better in the request templates.

  • Can `feed(feeder, 2)` hand the same record twice to one virtual user?
    Yes, on `random`. That strategy draws independently from the whole stock on every call, so both slots of the list can land on the same row. `queue`, `shuffle` and `circular` all advance a cursor, so within one call they return distinct positions unless the batch is larger than the source itself.
  • Does the record count have to be a constant?
    No. Gatling accepts an expression string such as `feed(feeder, "#{numberOfRecords}")` or a function of the Session, resolved per virtual user when the step runs. A resolved value of zero or less is rejected and stops the load generator.

saying these in an interview costs you the question

  • Expecting numbered attributes such as username1 and username2
  • Assuming a short batch yields a shorter list instead of crashing
  • Believing random guarantees two different records in one call
  • Thinking two separate feed steps are equivalent to one batched feed