skip to content

Injection Vocabulary

The named step builders that decide when virtual users enter a Gatling run, and what each one declares. Interviewers probe it because the step you choose, not a setting, fixes the run's shape.

on this pageshow

explore

questions

30

In Gatling's open injection model, what arrival pattern does each of nothingFor, atOnceUsers, rampUsers and stressPeakUsers produce?

level: juniorimportance: must knowfreq 70%

answer

  1. Four steps, each a fixed head-count
  2. One of them admits nobody at all
  3. Even spread versus bunched middle
  4. nothingFor, atOnceUsers, rampUsers, stressPeakUsers

basics

~20 s

nothingFor 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 s

All 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 lines
java
import 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

for a junior

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.

for a middle

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.

for a senior

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.

for a principal

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
open as a page

In Gatling's closed injection model, how do you declare 300 concurrent users held for ten minutes and then brought down to 50?

level: juniorimportance: must knowfreq 66%

basics

~20 s

Chain closed injection steps in one call: constantConcurrentUsers(300) for ten minutes, then rampConcurrentUsers(300).to(50) over a wind-down window, then constantConcurrentUsers(50) if that level must be held. Java, Kotlin, JavaScript and TypeScript register them with injectClosed; Scala uses inject.

open as a page

In Gatling's open injection model, what do constantUsersPerSec(20).during(15) and rampUsersPerSec(10).to(20).during(10) each declare, and how many virtual users does each one start?

level: juniorimportance: must knowfreq 72%

basics

~10 s

Both declare an arrival rate in users per second, not a headcount. constantUsersPerSec(20).during(15) starts 300 users, 20 every second. rampUsersPerSec(10).to(20).during(10) climbs linearly from 10 to 20 a second, starting 150.

open as a page

In a Gatling simulation, what does the setUp call do and where must it appear?

level: juniorimportance: must knowfreq 82%

basics

~20 s

setUp registers this simulation's populations, each one a scenario welded to an injection profile, and returns a handle for run-wide settings such as protocols and maxDuration. It must be called exactly once, from the Simulation's constructor.

open as a page

In Gatling, which builders declare a whole stepped load profile in one expression, and what does each call in that chain set?

level: juniorimportance: must knowfreq 48%

basics

~10 s

Gatling has one staircase builder per workload model: incrementUsersPerSec(rateIncrement) and incrementConcurrentUsers(usersIncrement). Both chain .times(levels).eachLevelLasting(duration), plus optional .separatedByRampsLasting(duration) and .startingFrom(x).

open as a page

In a Gatling simulation, how do you declare two minutes in which nobody arrives followed by 500 virtual users starting at the same instant?

level: middleimportance: must knowfreq 52%

basics

~20 s

Chain two open injection steps in one call: nothingFor for the two-minute gap, then atOnceUsers(500). The gap adds no users and only delays what follows; the surge places all 500 users on one instant and costs no time.

open as a page

In a Gatling closed injection profile, what actually brings the number of concurrent users down when a step ramps from 300 to 50?

level: middleimportance: must knowfreq 54%

basics

~20 s

Only virtual users finishing their scenario. Gatling never interrupts a running user to meet a lower target: it withholds the replacement for a finished user while the population is still above the target for the current second, so concurrency falls at whichever is slower — the rate scenarios end, or the declared slope.

open as a page

In a Gatling simulation, why can a throttle set to 200 requests per second finish the run below 200, and what does enabling that throttle do to the scenario's pauses?

level: middleimportance: must knowfreq 50%

basics

~20 s

A throttle only caps, never raises. Gatling cannot exceed what the injection profile and scenario already offer once pauses are off, so an under-supplied run stays below the ceiling. Enabling one also forces the covered populations' pause policy off.

open as a page

In Gatling, why do Java, Kotlin and JavaScript scenarios call injectOpen or injectClosed while Scala scenarios call a single inject?

level: middleimportance: must knowfreq 52%

basics

~20 s

The entry points sit in different binding layers. Scala's inject infers the model from an implicit InjectionProfileFactory chosen by the step type, while the Java API has no implicits and names the choice instead, as injectOpen and injectClosed.

open as a page

In Gatling, how do you make one setUp run two scenarios one after the other rather than at the same time?

level: middleimportance: must knowfreq 56%

basics

~20 s

Chain the second population onto the first with andThen instead of passing both as separate arguments to setUp. Populations listed side by side start together; an andThen child starts only once every user of its parent has terminated.

open as a page

In Gatling, what load does incrementUsersPerSec(20).times(8).eachLevelLasting(Duration.ofMinutes(2)) actually apply when startingFrom is left off?

level: middleimportance: must knowfreq 38%

basics

~20 s

Levels of 0, 20, 40, 60, 80, 100, 120 and 140 arrivals per second, two minutes each. Omitting startingFrom spends the first two minutes injecting nobody and tops out one increment short, at 140 rather than 160.

open as a page

In a Gatling simulation, which building blocks make up a throttle profile, and what does each one do?

level: juniorimportance: should knowfreq 42%

basics

~10 s

Three chain inside throttle(...): reachRps(target).in(duration) ramps the requests-per-second ceiling to a target, jumpToRps(target) steps to it instantly, and holdFor(duration) holds the current value. Declare the chain on setUp or on one injected population.

open as a page

In Gatling, rampUsers(1000).during(60) and stressPeakUsers(1000).during(60) both start 1000 users over 60 seconds — what actually differs?

level: middleimportance: should knowfreq 42%

basics

~20 s

Only the arrival shape. Both start exactly 1000 users and both occupy 60 seconds. The ramp spaces arrivals evenly; the stress peak follows a smoothed Heaviside curve, so arrivals are sparse at both ends and densest in the middle.

open as a page

While a Gatling closed injection step holds 300 concurrent users, one virtual user finishes its scenario — what does Gatling start in its place?

level: middleimportance: should knowfreq 41%

basics

~20 s

A brand-new virtual user with a fresh id and an empty session, not the finished one looping again. Gatling preallocates no user pool, so a held count fixes how many run at once, never how many run in total.

open as a page

In a Gatling simulation, which injection steps declare an arrival rate that climbs from 10 to 100 users per second over five minutes and then holds at 100, and how many virtual users does each part start?

level: middleimportance: should knowfreq 55%

basics

~10 s

Two steps in one injectOpen call: rampUsersPerSec(10).to(100).during(Duration.ofMinutes(5)) then constantUsersPerSec(100).during(...). The ramp averages 55 arrivals a second over 300 seconds and starts 16,500 users; the hold starts 100 for every second it runs.

open as a page

In a Gatling simulation, what does appending .randomized() to constantUsersPerSec(100).during(Duration.ofMinutes(10)) change about the run, and how is it spelled in Scala?

level: middleimportance: should knowfreq 40%

basics

~20 s

It replaces the evenly spread arrival schedule with randomized intervals drawn from a Poisson process at the same average rate. Scala writes it without parentheses, as .randomized; Java, Kotlin and the JavaScript SDK write .randomized().

open as a page

In a Gatling injection profile, when does the next chained injection step begin?

level: middleimportance: should knowfreq 42%

basics

~20 s

The next step begins as soon as every virtual user the current step defines has started, not when those users have finished. A Gatling injection profile is a schedule for launching users, so consecutive steps deliberately overlap.

open as a page

Why does a Gatling staircase written as incrementUsersPerSec(20).times(8).during(Duration.ofMinutes(2)) fail to compile, and how is the duration spelled instead?

level: middleimportance: should knowfreq 30%

basics

~10 s

The stepped-level builders declare no during method. A staircase spells its durations with eachLevelLasting(d) for each level, and optionally separatedByRampsLasting(d) for the ramps between levels.

open as a page

A Gatling simulation calling heavisideUsers(1000).during(20) stopped compiling after an upgrade — what happened to that injection step?

level: seniorimportance: should knowfreq 30%

basics

~10 s

It was renamed. heavisideUsers became stressPeakUsers in Gatling 3.7, kept only as a deprecated alias, and that alias was dropped in 3.11. Substituting stressPeakUsers is the whole fix; the arrival shape is unchanged.

open as a page

A Gatling Java simulation injects only constantConcurrentUsers(300).during(Duration.ofMinutes(10)) — when does that run actually stop?

level: seniorimportance: should knowfreq 37%

basics

~20 s

Not at ten minutes. When the profile's window expires Gatling stops starting replacements, but the run ends only once every user it already started has finished its scenario, so the tail is roughly one scenario iteration long.

open as a page

Gatling's reference says constantUsersPerSec starts users at regular intervals - at what granularity does Gatling actually build that schedule, and when do the gaps stop being equal?

level: seniorimportance: should knowfreq 24%

basics

~20 s

Gatling resolves the rate into one whole head-count, shards it across whole seconds, then spreads each second's share over that second's 1000 millisecond slots. Gaps are exactly equal only when every second carries the same share and it divides 1000.

open as a page

In a Gatling simulation running two scenarios at once, what does a single throttle declared on setUp cap, and how does that differ from calling throttle on each injected population?

level: seniorimportance: should knowfreq 33%

basics

~20 s

A throttle on setUp is one shared ceiling for the whole run: both scenarios draw on the same per-second budget, first come first served, with no guaranteed split. A throttle per injected population gives each scenario its own ceiling.

open as a page

In a Gatling simulation, which run settings attach to setUp itself rather than to one population, and what wins when both are set?

level: seniorimportance: should knowfreq 38%

basics

~20 s

Assertions and maxDuration exist only on the setUp handle; protocols, the pause policy and throttling exist on both setUp and each population; andThen exists only on a population. Where a protocol is set in both places, the population's setting wins.

open as a page

In a Gatling stepped injection profile, what does separatedByRampsLasting add, and why does the presence of startingFrom change the resulting shape?

level: seniorimportance: should knowfreq 26%

basics

~20 s

It inserts a linear ramp between levels instead of a hard jump. With a non-zero startingFrom the shape is level-then-ramp, ending on a level with no trailing ramp; with it omitted or set to zero the shape is ramp-then-level, opening with a ramp out of zero.

open as a page

Your Gatling run must sit at 200 requests per second across two scenarios, so how would you decide between declaring that rate with throttle and declaring it in the injection profile?

level: principalimportance: should knowfreq 30%

basics

~20 s

Decide by what the number is. A rate the test exists to prove belongs in the injection profile, which keeps think time and the request mix intact. A rate that is only a safety limit belongs in throttle.

open as a page

When would you stop expressing a Gatling capacity profile with the incrementUsersPerSec staircase and hand-chain the injection steps instead?

level: principalimportance: should knowfreq 24%

basics

~20 s

When the profile stops being uniform. The staircase gives one increment, one level duration and one ramp duration for the whole run, so unequal levels, an uneven progression or a longer dwell at the top all force explicit alternating steps.

open as a page

In a Gatling run, what happens to requests the throttle ceiling will not admit yet, and what ends a run whose throttle profile is shorter than its injection profile?

level: middleimportance: nice to knowfreq 26%

basics

~20 s

Nothing is dropped. Excess requests are buffered in an unbounded in-memory queue inside the load generator and replayed on the next one-second tick, risking an OutOfMemoryError. The throttle profile's total duration also bounds the run, stopping it like maxDuration.

open as a page

In Gatling, why does a Kotlin simulation have to write incrementUsersPerSec(20.0) while incrementConcurrentUsers(20) is fine as written?

level: middleimportance: nice to knowfreq 20%

basics

~20 s

The open staircase builder takes a Double and the closed one takes an Int, and startingFrom follows the same split. Kotlin performs no implicit widening from Int to Double, so the open builder needs a decimal literal.

open as a page

Your Gatling run must take a service from 300 concurrent users down to 50 at the end — would you express that as a ramp step, a step change, or by ending the population, and how would you decide?

level: principalimportance: nice to knowfreq 25%

basics

~30 s

None of the three interrupts a running user, but they do not come down at the same speed. A ramp keeps replacing finished users against its falling target, so the population follows the declared slope; a step change and the end of the population both shed load at the full completion rate. Ramp when the descent itself is measured or must be gentle; step down when only the lower level matters; end the population when neither is read.

open as a page

Across a Gatling suite, would you declare every steady arrival phase as constantUsersPerSec(rate).during(d) or append .randomized(), and what does Gatling actually let you control about that choice?

level: principalimportance: nice to knowfreq 26%

basics

~20 s

There is no universally right answer, but Gatling fixes the terms: the plain step reruns with an identical arrival schedule, while the randomized one takes its seed from the clock and can never be replayed.

open as a page