Open vs Closed Injection in Gatling: Choosing the Right Load Model
Choosing between constantUsersPerSec and constantConcurrentUsers changes how latency is measured and how saturation appears. Learn to pick the right model for accurate results.
11 Jun 2026, 10:32 UTC

The Latency Attribution Problem
When configuring a Gatling simulation, the choice of injection profile fundamentally changes how you interpret your results. If you use an open-model profile (like constantUsersPerSec), Gatling spawns users at a fixed rate regardless of how the system responds. If the server slows down, requests pile up. In a closed-model profile (like constantConcurrentUsers), Gatling maintains a fixed number of active users; if the server slows down, the arrival rate of new requests naturally drops.
The danger lies in misattributing latency. In an open model, a spike in response time might not just be server degradation—it could be queuing delay occurring because the load generator is pushing more requests than the system can handle. Conversely, a closed model can hide the true scale of a bottleneck because it applies backpressure to the load generator itself.
Open Profiles: Simulating Arrival Rates
Open injection profiles are designed to mimic real-world traffic, such as a public API or a website, where users arrive independently of one another. The system does not tell the next user to wait because the previous user's request is still processing.
- Behavior: The arrival rate remains constant even if response times increase.
- Saturation Signal: Saturation is often signaled by a divergence between the intended arrival rate and the actual completed requests per second, accompanied by climbing response times.
- Risk: If the system is completely overwhelmed, the load generator may experience resource exhaustion trying to maintain the arrival rate.
Closed Profiles: Simulating Bounded Concurrency
Closed injection profiles are better suited for systems with a limited pool of clients, such as a set of internal microservices or a fixed number of licensed terminal users. Here, a new request is only sent once a previous one has completed.
- Behavior: The number of concurrent users is capped. If the backend slows down, the throughput (requests per second) drops automatically.
- Saturation Signal: Saturation is visible when the throughput plateaus or drops while the number of active users remains at the defined ceiling.
- Risk: It can give a false sense of stability. You might see "stable" response times simply because the closed model is throttling the load, preventing the system from reaching its true breaking point.
Practical Comparison Example
To understand the difference, you can run the same scenario under both models. This allows you to see the "ceiling" of your application.
import io.gatling.core.Predef._
import io.gatling.http.Predef._
class InjectionComparison extends Simulation {
val httpProtocol = http.baseUrl("http://your-test-target.com")
val scn = scenario("Baseline Request")
.exec(http("request_1").get("/"))
// Test 1: Open Model (Arrival Rate)
// Fixed arrival of 20 users/sec for 30 seconds
val openSetup = setUp(
scn.injectOpen(constantUsersPerSec(20) during (30 seconds))
).protocols(httpProtocol)
// Test 2: Closed Model (Concurrency)
// Fixed pool of 50 concurrent users
val closedSetup = setUp(
scn.injectClosed(constantConcurrentUsers(50))
).protocols(httpProtocol)
}Execution: Run this via the Gatling CLI (e.g., ./gatling.sh) from your terminal. Ensure your target environment is isolated to avoid impacting production traffic.
Verification: Compare the HTML reports. In the open model, check if the Users/sec graph remains flat while Response Time climbs. In the closed model, observe if Requests/sec drops as you introduce artificial latency to the server.
Trade-offs and Decision Matrix
| Metric | Open Model (Arrival Rate) | Closed Model (Concurrency) |
|---|---|---|
| Traffic Pattern | Unpredictable/External | Bounded/Internal |
| Throughput | Driven by the generator | Driven by system performance |
| Latency | Includes queuing delay | Represents service time |
| Saturation | Visible as latency spikes | Visible as throughput drop |
A significant limitation of the closed model is that it cannot simulate a "surge" of users who arrive regardless of system state. While Gatling provides pacing and throttling to tweak these behaviors, they do not replace the fundamental logic of the injection profile.
Actionable Closing
- Define your user persona: If your users are independent (web traffic), use
injectOpen. If your users are a fixed pool (worker threads), useinjectClosed. - Cross-validate: Run a baseline with a closed model to find the maximum throughput the system can handle, then use an open model to see how the system behaves when that throughput is exceeded.
- Audit your assertions: Avoid relying solely on average response times in open models; use percentiles (p95, p99) to identify the users suffering from queuing delays.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.