Gatling Feeder Strategies: Realistic Data Without the Hall of Mirrors
Identical requests can make a Gatling load test look busy while measuring nothing. Choose a feeder strategy that matches your endpoint's uniqueness needs, and verify it in the simulation log.
25 May 2026, 20:57 UTC

The hall-of-mirrors problem
You point Gatling at your API, inject a few hundred virtual users, and watch the request counter climb. Then you open the logs and notice every request carries the same userId, the same payload, the same everything. The load looks heavy, but the traffic is a hall of mirrors. A feeder is the mechanism that streams test data into each virtual user's session, and if you treat it as a simple file loader rather than a design decision, your results measure your data file more than your system.
The useful takeaway: pick a feeder strategy that matches the uniqueness your endpoints actually require, and verify the choice in the simulation log before you trust the numbers.
Feeder strategies are traffic policies
Gatling feeders read records from sources such as CSV, TSV, JSON, or programmatic collections and expose them as session attributes—named values attached to a virtual user for the duration of its scenario. A strategy decides how those records are handed out. In Gatling 3.x, the common strategies are circular, queue, random, and shuffle.
| Strategy | Reuse behavior | Typical fit | Main risk |
|---|---|---|---|
circular | Repeats records forever | Idempotent reads, lookups | Unrealistic duplicates under long runs |
queue | Each record once, then exhausted | Unique writes, one-time tokens | Scenario stops when data runs out |
random | Random selection forever | Mixed traffic, broad coverage | Duplicates possible, no ordering |
shuffle | Randomized order | Unique sequence without sorting | Exhaustion semantics vary by version |
Because Gatling's engine is asynchronous and reuses connections across virtual users, feeder throughput and parsing cost can become the bottleneck before the network is saturated. A slow or memory-hungry feeder hides the latency you are trying to measure.
Worked example: circular IDs plus a queue of payloads
A practical pattern is to pair a circular feeder for stable identifiers with a queue for payloads that must be unique. The following Scala DSL sketch shows the idea; adjust class names and file paths to your project.
import io.gatling.core.scenario.Simulation
import io.gatling.core.Predef._
import io.gatling.http.Predef._
class DataDrivenExample extends Simulation {
// Circular feeder for read-heavy lookups: repeats safely
val userIdFeeder = csv("user-ids.csv").circular()
// Queue feeder for unique payloads: each record used once
val payloadFeeder = csv("payloads.csv").queue()
val scn = scenario("Data Driven")
.feed(userIdFeeder)
.exec(
http("Get User")
.get("/api/users/#{userId}")
.check(status.is(200))
)
.feed(payloadFeeder)
.exec(
http("Submit Payload")
.post("/api/submit")
.body(StringBody("#{payloadJson}"))
.asJson
.check(status.is(201))
)
setUp(
scn.inject(atOnceUsers(10))
).protocols(http.baseUrl("https://<your-api-host>"))
}
Where to run it: from your project root (the directory containing pom.xml or build.gradle), execute:
./mvnw gatling:test -Dgatling.simulationClass=<fully.qualified.DataDrivenExample>
If you use Gradle, use the equivalent Gatling plugin task. No special system permissions are required beyond local build execution and network access to the target host.
Expected checks and risks:
- Open
results/simulation.logand confirm that#{userId}repeats while#{payloadJson}values are distinct across executions. - Open the HTML report and compare response-time percentiles and error rate against a run that uses a single static payload.
- Risk: queue exhaustion stops the scenario. If your payload file has fewer rows than total planned executions, the simulation will end early unless you handle it.
- Risk: large CSV files are loaded into memory on the load generator. A multi-gigabyte feeder can increase heap pressure and distort results.
Trade-offs: memory, exhaustion, and version drift
Feeders trade realism for resource cost. A circular feeder is cheap and endless, but it manufactures duplicates. A queue feeder preserves uniqueness, yet it turns your data file into a hard limit on test duration. The shuffle strategy sits between them, but its end-of-data behavior is version-sensitive; confirm it against the release notes for your installed Gatling 3.x version rather than assuming it reshuffles automatically.
Large CSV feeders are another constraint. Because Gatling parses and holds the file in memory, a feeder with millions of rows can pressure the heap on the load generator. When that happens, the bottleneck is your test rig, not the system under test. Sharding data across multiple simulations or generating records programmatically are common ways to relieve that pressure, but both add complexity.
Actionable closing
Before your next data-driven run, do three things. First, replace one static body or path parameter with a small CSV feeder and run a short simulation. Second, inspect simulation.log to see exactly which session attributes each virtual user received. Third, compare circular and queue strategies on the same dataset and watch how error rate and percentile latency shift. If the queue empties early or the heap climbs, you have found a data-design limit, not a system limit—and that is a much cheaper problem to fix before a full-scale test.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.