In Gatling's open injection model, what arrival pattern does each of nothingFor, atOnceUsers, rampUsers and stressPeakUsers produce?
answer
- Four steps, each a fixed head-count
- One of them admits nobody at all
- Even spread versus bunched middle
- nothingFor, atOnceUsers, rampUsers, stressPeakUsers
basics
~20 snothingFor admits nobody and shifts the rest of the profile later. atOnceUsers starts a fixed count at one instant. rampUsers spreads a fixed count evenly over a window. stressPeakUsers spreads the same count bunched around that window's middle.
solid answer
~40 sAll four are **open-model injection steps** built from a fixed head-count, and all four are spelled identically in Java, Kotlin, JavaScript, TypeScript and Scala. `nothingFor(d)` injects zero users; it only pushes every later step `d` further down the timeline. `atOnceUsers(n)` schedules all `n` users at the same instant and occupies no time itself, so it does not delay what follows. `rampUsers(n).during(d)` spreads exactly `n` users evenly across `d` — first user at the start, last strictly before the end. `stressPeakUsers(n).during(d)` starts the same `n` over the same `d`, but along a smoothed Heaviside curve, so arrivals are sparse at both ends and densest in the middle. Only the last two take `.during(...)`.
code
java · 26 linesimport io.gatling.javaapi.core.ScenarioBuilder;
import io.gatling.javaapi.core.Simulation;
import io.gatling.javaapi.http.HttpProtocolBuilder;
import java.time.Duration;
import static io.gatling.javaapi.core.CoreDsl.*;
import static io.gatling.javaapi.http.HttpDsl.*;
public class FourStepsSimulation extends Simulation {
private final HttpProtocolBuilder httpProtocol = http.baseUrl("https://example.org");
private final ScenarioBuilder scn = scenario("browse").exec(http("home").get("/"));
{
setUp(
scn.injectOpen(
nothingFor(Duration.ofSeconds(30)),
atOnceUsers(50),
rampUsers(200).during(Duration.ofMinutes(2)),
stressPeakUsers(200).during(Duration.ofMinutes(2))
).protocols(httpProtocol)
);
}
}go deeper
Be ready to name all four steps and say in one line what each puts on the timeline, and to remember that only rampUsers and stressPeakUsers continue with .during.
Be ready to explain that nothingFor adds time but no users while atOnceUsers adds users but no time, and to show the same profile written in both Scala and a Java-API SDK.
Be ready to justify picking one shape over another for a real run, and to spot the quantisation and zero-argument edges before they surprise a scheduled load test.
Be ready to argue which of these shapes a team's standard profiles should be built from, and how you keep injection profiles reviewable rather than hand-tuned per service.
## The family: a fixed head-count, declared once Gatling declares load as a **chain of injection steps** handed to one registering call. The four steps in this family share a single property: each is built from a **fixed number of virtual users**, never from a rate. You state *how many* users and *over how long*, and Gatling computes the instant at which each individual user starts. (What an open workload model is, and when you should prefer one, is performance-testing theory that belongs elsewhere; what follows is only which Gatling builder expresses which arrival shape.) All four are spelled **identically** in Java, Kotlin, JavaScript, TypeScript and Scala. What differs is the registering method — `scn.injectOpen(...)` in the SDKs that sit on the Java API (Java, Kotlin, JavaScript and TypeScript) and plain `scn.inject(...)` in Scala — and how a duration is written, which the table further down spells out. ## What each one puts on the timeline | step | argument | users added | time it occupies | takes `.during(...)` | |---|---|---|---|---| | `nothingFor(d)` | a duration | **0** | `d` | no | | `atOnceUsers(n)` | a count | `n` | **zero** | no | | `rampUsers(n).during(d)` | count, then duration | `n` | `d` | yes | | `stressPeakUsers(n).during(d)` | count, then duration | `n` | `d` | yes | - **`nothingFor(d)`** is the deliberate hole in a profile. It contributes no users whatsoever; its only effect is to push every step declared after it `d` further along the timeline. - **`atOnceUsers(n)`** schedules all `n` users at offset zero inside its own step, and occupies no time of its own. Two consecutive `atOnceUsers(10)` steps therefore give you twenty users at the same instant, not ten and then ten. - **`rampUsers(n).during(d)`** spreads exactly `n` users evenly across `d`, the first at offset zero and the last strictly *before* the end. Gatling's own unit test pins one case: five users over one second land **200 ms apart**, so the *average* spacing is `d / n`, not `d / (n - 1)`. That average is not a guaranteed interval. The window is truncated to whole seconds with `toSeconds`, the head-count is integer-sharded across those seconds and then across each second's milliseconds, so real gaps land on `d / n` only when the count divides cleanly: `rampUsers(3).during(8)` starts its three users at 0 s, 2 s and 5 s — gaps of 2 s and 3 s, not the 2.67 s the formula suggests. - **`stressPeakUsers(n).during(d)`** injects the same `n` users over the same `d`, but places them along a smooth approximation of the Heaviside step function. Arrivals are sparse at both ends and densest in the middle. In Gatling's own test for 100 users over 5 seconds, **67 of them** land in the middle two seconds; an evenly spread ramp would put about 40 there. ## Writing them in each SDK | language | registering call | how a duration is written | |---|---|---| | Java | `injectOpen` | `rampUsers(200).during(Duration.ofMinutes(2))` | | Kotlin | `injectOpen` | `rampUsers(200).during(Duration.ofMinutes(2))` | | JavaScript / TypeScript | `injectOpen` | `rampUsers(200).during({ amount: 2, unit: "minutes" })` | | Scala | `inject` | `rampUsers(200).during(2.minutes)` | A **bare number is a count of seconds**. In Java and Kotlin that is an explicit overload — `during(long durationSeconds)` and `nothingFor(long durationSeconds)`. In Scala it is an implicit conversion in `io.gatling.core.Predef` that turns an `Int` into that many seconds. So `rampUsers(10).during(5)` means five seconds in every SDK. ## What the counts guarantee Because every step in this family carries a fixed count, the profile's totals are simple sums: 1. The **total number of users** a population starts is the sum of the steps' counts, with every `nothingFor` contributing zero. 2. The **length of the injection profile** is the sum of the steps' durations, with every `atOnceUsers` contributing zero. 3. Neither total says anything about when the run *ends* — users still have to finish the scenario they were started into. ## Degenerate arguments Gatling folds away The implementations collapse the boring cases rather than failing on them: - `rampUsers(n).during(0)` behaves exactly like `atOnceUsers(n)`. - `rampUsers(0).during(d)` behaves exactly like `nothingFor(d)`; the window is still consumed. - `stressPeakUsers(n).during(0)` likewise degrades to `atOnceUsers(n)`, and `stressPeakUsers(0).during(d)` to `nothingFor(d)`. - A negative count or a negative duration trips a `require` check and aborts the run before any user starts. One rougher edge: the ramp distributes users **per whole second**, because the window is truncated with `toSeconds`. A ramp window shorter than one second consequently starts nobody at all, while still delaying whatever follows it. ## What is deliberately not in this family `constantUsersPerSec` and `rampUsersPerSec` look similar but are **rate-based**: you supply users per second plus a duration and the head-count falls out of the arithmetic. `constantConcurrentUsers` and `rampConcurrentUsers` are closed-model steps that hold a number of users *inside* the system instead of declaring arrivals. Every step passed to one registering call must be of the same model kind, so a count-based open step cannot share a call with a concurrent-users step.
- Which of these four steps accept a chained .during(...) call, and which are already complete?Only `rampUsers(n)` and `stressPeakUsers(n)` return a builder that needs `.during(d)`. `atOnceUsers(n)` and `nothingFor(d)` return a finished injection step, so `atOnceUsers(500).during(10)` does not compile. `nothingFor` already carries its duration as its single argument.
- In Gatling's Java DSL, what duration does the bare number in nothingFor(120) mean?Seconds — two minutes. `CoreDsl` declares an overload `nothingFor(long durationSeconds)` alongside `nothingFor(Duration)`. Scala reaches the same result through an implicit `Int`-to-seconds conversion in `io.gatling.core.Predef`. Passing a `java.time.Duration` explicitly avoids confusing `nothingFor(2)` with two minutes.
- What does rampUsers(0).during(30) actually do?Exactly what `nothingFor(30)` does: it starts no users but still consumes thirty seconds of the profile, delaying every step declared after it. Gatling folds the zero-count case into the do-nothing step rather than rejecting it, and the same folding applies to `stressPeakUsers(0).during(d)`.
saying these in an interview costs you the question
- Thinking atOnceUsers takes a .during window like rampUsers does
- Believing nothingFor waits for already-started users to finish
- Assuming stressPeakUsers starts more users than an equivalent rampUsers step
- Expecting different step names in Scala than in Java or JavaScript