Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/gatling/gatling.io-doc/llms.txt

Use this file to discover all available pages before exploring further.

Feeders are how Gatling injects dynamic data into each virtual user’s session. While basic CSV and JSON feeders cover the majority of use cases, large-scale and production-representative simulations often require more sophisticated patterns: grouping records so a single virtual user always receives a coherent set of related data, reading feeder files from AWS S3 to keep sensitive data out of your repository, or fetching credentials at runtime from AWS Secrets Manager. This guide covers all of these advanced patterns with concrete implementation examples.

Grouped Feeder Records

A common challenge arises when your feeder data has a one-to-many relationship — for example, a CSV where multiple URLs belong to the same user, and you want each virtual user to receive only their own URLs.

The Problem

username,url
user1,/account/profile
user1,/account/orders
user2,/settings
user2,/billing
With a standard feeder, user1 might receive /settings and user2 might receive /account/profile — the association between username and URL is lost.

Solution: readRecords with In-Memory Grouping

Use readRecords to load all CSV records into memory at simulation startup, then group them by user before building a custom iterator.
import io.gatling.javaapi.core.*;
import static io.gatling.javaapi.core.CoreDsl.*;

import java.util.*;
import java.util.stream.*;

public class GroupedFeederSimulation extends Simulation {

  // Load all records from the CSV feeder file
  private static final List<Map<String, Object>> ALL_RECORDS =
      csv("data/users_urls.csv").readRecords();

  // Group records by username
  private static final Map<String, List<Map<String, Object>>> GROUPED =
      ALL_RECORDS.stream()
        .collect(Collectors.groupingBy(r -> (String) r.get("username")));

  // Build an iterator that yields one group per virtual user
  private static final Iterator<Map<String, Object>> groupedFeeder =
      GROUPED.entrySet().stream()
        .map(e -> Map.<String, Object>of(
            "username", e.getKey(),
            "urls",     e.getValue().stream()
                          .map(r -> r.get("url"))
                          .collect(Collectors.toList())
        ))
        .collect(Collectors.toList())
        .iterator();

  ScenarioBuilder scn = scenario("Grouped Feeder")
    .feed(groupedFeeder)
    .foreach("#{urls}", "url").on(
      exec(http("Visit URL").get("#{url}"))
    );

  {
    setUp(scn.injectOpen(atOnceUsers(2)))
      .protocols(httpProtocol);
  }
}
readRecords loads the entire file into the JVM heap once, at simulation initialisation. It is ideal for files up to a few hundred thousand rows. For very large datasets, consider streaming from S3 instead (see below).

Feeder Strategies

Gatling provides four built-in strategies for how records are consumed:
StrategyBehaviourBest for
.queue()Records served in order; simulation stops when exhaustedOne-time credential pools
.random()Random record each time, with replacementProduct catalogues, broad data variety
.shuffle()All records shuffled, then served in orderRandomised but fully covered data sets
.circular()Loops back to the first record when the last is reachedLong-running simulations with small data files

AWS S3 Bucket Feeders

When feeder files are large, change frequently, or contain sensitive data, storing them in an AWS S3 bucket is a better choice than committing them to your repository.
S3 feeders work with private locations on Gatling Enterprise Edition and local test execution. They do not work with managed (cloud) locations on Gatling Enterprise Edition.

IAM Configuration

Grant your load generators read access to the S3 bucket via an IAM instance profile policy:
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "AllowFeederRead",
      "Effect": "Allow",
      "Action": "s3:GetObject",
      "Resource": "arn:aws:s3:::my-feeders-bucket/*"
    }
  ]
}
Additionally, the Gatling Enterprise control plane needs permission to pass this profile to load generators:
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Action": ["iam:PassRole"],
      "Effect": "Allow",
      "Resource": ["arn:aws:iam::{Account}:role/{RoleNameWithPath}"]
    }
  ]
}

Add the AWS SDK Dependency

<dependency>
  <groupId>software.amazon.awssdk</groupId>
  <artifactId>s3</artifactId>
  <version>2.27.0</version>
</dependency>

Implementation

The recommended pattern downloads the feeder file from S3 at simulation startup, uses it locally during the run, then deletes the temporary file when the simulation completes.
import io.gatling.javaapi.core.*;
import static io.gatling.javaapi.core.CoreDsl.*;

import software.amazon.awssdk.core.ResponseInputStream;
import software.amazon.awssdk.services.s3.S3Client;
import software.amazon.awssdk.services.s3.model.GetObjectRequest;
import software.amazon.awssdk.services.s3.model.GetObjectResponse;

import java.io.IOException;
import java.nio.file.*;

public class S3FeederSimulation extends Simulation {

  private final String bucketName = "my-feeders-bucket";
  private final String objectKey  = "data/users.csv";
  private final Path   feederFile = loadFeeder();

  private Path loadFeeder() {
    try (S3Client s3 = S3Client.create()) {
      Path tmp = Files.createTempFile("gatling-feeder-", ".csv");
      tmp.toFile().deleteOnExit();
      try (ResponseInputStream<GetObjectResponse> stream = s3.getObject(
          GetObjectRequest.builder().bucket(bucketName).key(objectKey).build())) {
        Files.copy(stream, tmp, StandardCopyOption.REPLACE_EXISTING);
      }
      return tmp;
    } catch (IOException e) {
      throw new RuntimeException(e);
    }
  }

  // Use the downloaded file as a circular feeder
  private final FeederBuilder<String> usersFeeder =
      csv(feederFile.toFile().getAbsolutePath()).circular();

  ScenarioBuilder scn = scenario("S3 Feeder Test")
    .feed(usersFeeder)
    .exec(http("Authenticated Request")
      .get("/api/protected")
      .header("X-User", "#{username}"));

  {
    setUp(scn.injectOpen(rampUsers(100).during(60)))
      .protocols(httpProtocol);
  }
}

AWS Secrets Manager Integration

For credentials and API keys that must never be stored in files, retrieve them securely from AWS Secrets Manager inside the simulation’s initialisation block.
This integration requires Gatling Enterprise Edition with private locations and the AWS SDK for Java 2.x. The secret is retrieved only once per load generator spawn, not per virtual user.

IAM Policy for Secrets Manager

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Action": [
        "secretsmanager:GetSecretValue",
        "secretsmanager:DescribeSecret"
      ],
      "Resource": "arn:aws:secretsmanager:{region}:{account-id}:secret:{secret-name}"
    }
  ]
}
For batch retrieval, add secretsmanager:BatchGetSecretValue to the policy.

Reading a Secret at Simulation Startup

import software.amazon.awssdk.services.secretsmanager.SecretsManagerClient;
import software.amazon.awssdk.services.secretsmanager.model.GetSecretValueRequest;
import software.amazon.awssdk.services.secretsmanager.model.GetSecretValueResponse;

public class SecretsSimulation extends Simulation {

  // Retrieve the secret once during class initialisation
  private static final String API_KEY = fetchSecret("prod/myapp/api-key");

  private static String fetchSecret(String secretName) {
    try (SecretsManagerClient client = SecretsManagerClient.create()) {
      GetSecretValueResponse response = client.getSecretValue(
        GetSecretValueRequest.builder().secretId(secretName).build()
      );
      return response.secretString();
    }
  }

  ScenarioBuilder scn = scenario("Secrets Test")
    .exec(http("Protected Endpoint")
      .get("/api/data")
      .header("Authorization", "Bearer " + API_KEY));

  {
    setUp(scn.injectOpen(atOnceUsers(10)))
      .protocols(httpProtocol);
  }
}

Custom Feeder with DataFaker

When you need synthetic but realistic data — names, emails, addresses — build a custom iterator using the DataFaker library.
import net.datafaker.Faker;
import java.util.*;
import java.util.function.Supplier;
import java.util.stream.Stream;

Faker faker = new Faker();

Iterator<Map<String, Object>> customFeeder =
    Stream.generate((Supplier<Map<String, Object>>) () ->
        Map.of(
          "firstName", faker.name().firstName(),
          "lastName",  faker.name().lastName(),
          "email",     faker.internet().emailAddress()
        )
    ).iterator();

ScenarioBuilder scn = scenario("Custom Feeder")
  .feed(customFeeder)
  .exec(http("Register User")
    .post("/api/users")
    .body(StringBody(
      "{\"firstName\":\"#{firstName}\"," +
      "\"lastName\":\"#{lastName}\"," +
      "\"email\":\"#{email}\"}"
    )).asJson()
    .check(status().is(201)));
Stream.generate creates an infinite lazy stream, so the feeder never exhausts regardless of how many virtual users your simulation injects. This is the recommended approach for generated data.

Build docs developers (and LLMs) love