skip to content

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%

answer

  1. A rate per second, not a total
  2. Flat hold, or a straight line
  3. Ramp head-count uses the mean rate
  4. Rates are doubles; fractions are legal

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.

solid answer

~40 s

Both are open-model injection steps, and both name a **rate** rather than a stock of users. `constantUsersPerSec(rate).during(d)` holds one flat arrival rate for the window, so `constantUsersPerSec(20).during(15)` starts 20 users in each of 15 seconds, 300 in all. `rampUsersPerSec(r1).to(r2).during(d)` moves the arrival rate linearly from `r1` to `r2` across the window, so its head-count is the *mean* of the two rates times the duration: `rampUsersPerSec(10).to(20).during(10)` averages 15 a second and starts 150. The rate parameter is a `double` in every SDK, so fractional rates are legal; the head-count Gatling derives from one is always a whole number. You pass these steps to `injectOpen(...)` in Java, Kotlin, JavaScript and TypeScript, and to `inject(...)` in Scala.

code

java · 24 lines
java
import io.gatling.javaapi.core.*;
import io.gatling.javaapi.http.*;

import static io.gatling.javaapi.core.CoreDsl.*;
import static io.gatling.javaapi.http.HttpDsl.*;

import java.time.Duration;

public class RateStepsSimulation extends Simulation {

  private final HttpProtocolBuilder httpProtocol = http.baseUrl("https://example.com");

  private final ScenarioBuilder scn = scenario("rate steps")
    .exec(http("home").get("/"));

  {
    setUp(
      scn.injectOpen(
        constantUsersPerSec(20).during(15),
        rampUsersPerSec(10).to(20).during(Duration.ofSeconds(10))
      )
    ).protocols(httpProtocol);
  }
}

go deeper

for a junior

Be ready to say out loud that the argument is an arrival rate per second, and to multiply it by the window to get the user count.

for a middle

Be ready to derive a ramp's head-count from the mean of its two rates, and to explain why the target rate alone doubles the answer on a ramp from zero.

for a senior

Be ready to review a colleague's profile and catch a bare duration read as minutes, a fractional rate written as an Int in Kotlin, or a ramp sized off one endpoint.

for a principal

Be ready to say which parts of a profile should be expressed as a rate at all, and to defend that choice when someone proposes restating it as a count.

## Two step families, and this is the rate one Gatling's open injection steps split into two families. One names a **stock of users** and spreads it over a window. The other names an **arrival rate** in users per second and holds or moves it. `constantUsersPerSec` and `rampUsersPerSec` are the rate family, and they are the only two open steps whose first argument is a rate. Both are ordinary injection steps: you list them, in order, as arguments to a single registration call. In Java, Kotlin, JavaScript and TypeScript that call is `scn.injectOpen(...)`; in Scala it is `scn.inject(...)`. The step *names* are identical in all five languages — only the entry point differs. ## `constantUsersPerSec(rate).during(d)` This declares a flat arrival rate held for the whole window. `constantUsersPerSec(20).during(15)` says: start 20 new virtual users every second, for 15 seconds. The head-count follows directly. Gatling multiplies the rate by the window in seconds and rounds to a whole number of users: * `constantUsersPerSec(20).during(15)` → 20 × 15 = **300 users** * `constantUsersPerSec(100).during(Duration.ofMinutes(10))` → 100 × 600 = **60,000 users** * `constantUsersPerSec(1).during(5)` → **5 users** Note what the rate does *not* say. It says how many users **start** each second. It says nothing about how many are still running at any moment — that depends entirely on how long your scenario takes, and it is not something the step declares. ## `rampUsersPerSec(r1).to(r2).during(d)` This declares a straight line between two arrival rates. `rampUsersPerSec(10).to(20).during(10)` says: begin at 10 arrivals a second, finish at 20 arrivals a second, and move between them linearly over 10 seconds. The head-count is the **mean** of the two rates times the duration — the area under a straight line, not the sum of its endpoints: | step | mean rate | window | users started | |---|---|---|---| | `rampUsersPerSec(10).to(20).during(10)` | 15/s | 10 s | 150 | | `rampUsersPerSec(2).to(4).during(10)` | 3/s | 10 s | 30 | | `rampUsersPerSec(0).to(100).during(60)` | 50/s | 60 s | 3,000 | | `rampUsersPerSec(10).to(100).during(300)` | 55/s | 300 s | 16,500 | The common mistake is to reach for one endpoint. Using the target rate overstates the count by a factor of two on a ramp from zero; using the start rate understates it just as badly; adding the two rates together doubles it. A ramp whose two rates are equal degenerates to a flat hold. `rampUsersPerSec(100).to(100).during(d)` produces the same even schedule as `constantUsersPerSec(100).during(d)`; Gatling's own unit tests assert exactly that, that a zero-acceleration ramp yields a single repeated interval. ## Fractional rates are legal The rate parameter is a `double`, and Gatling's reference states plainly that rates may be expressed as fractional values. `constantUsersPerSec(0.5).during(Duration.ofMinutes(1))` is a valid way to say *one arrival every two seconds*. The head-count is still a whole number: Gatling rounds `rate × durationInSeconds`. Gatling's own test suite asserts that a rate of `0.4978` held for 100 seconds resolves to 50 users. One language-level consequence: because the parameter is a `double` and Kotlin performs no implicit widening from `Int`, a Kotlin simulation must write `constantUsersPerSec(20.0)` and `rampUsersPerSec(10.0).to(20.0)`. Java, Scala, JavaScript and TypeScript all accept a bare `20`. This is exactly why Gatling's own Kotlin sample carries `.0` on the rate steps but not on the count-based ones. ## Spelling the duration The duration argument differs by SDK even though the step names do not: 1. **Java and Kotlin** take a `java.time.Duration` — `Duration.ofSeconds(15)`, `Duration.ofMinutes(10)`. 2. **Scala** takes a `scala.concurrent.duration` literal — `15.seconds`, `10.minutes`. 3. **JavaScript and TypeScript** take an object literal — `{ amount: 10, unit: "minutes" }`. 4. **All four** also accept a **bare number, which means seconds**. `during(15)` is fifteen seconds everywhere. That last point is the one that bites: `during(10)` is ten *seconds*, not ten minutes, in every SDK. ## Getting it right in review When you read one of these steps, do three things. Read the first argument as a rate per second, never as a total. For a ramp, average the two rates before multiplying. And check the duration unit — a bare number is seconds, so a profile that looks like a ten-minute soak may be a ten-second blip. ## A worked review Take a profile someone submits as *"a gentle warm-up, then a short soak"*: ```java setUp( scn.injectOpen( rampUsersPerSec(0).to(50).during(Duration.ofMinutes(2)), constantUsersPerSec(50).during(120) ) ).protocols(httpProtocol); ``` Work it out step by step. The ramp runs 120 seconds at a mean rate of 25 a second, so it starts **3,000** users — not the 6,000 you get if you reach for the 50. The hold is `during(120)`, a bare number, so it is 120 **seconds**, not 120 minutes: 50 a second for two minutes is another **6,000** users, and the whole "soak" is four minutes long. Both numbers are correct Gatling; neither is what the covering note claimed. That is the value of being able to do this arithmetic in your head during a review.

  • Where do these steps sit relative to each other if you list both in one call?
    They run in the order written, and every step in a single `injectOpen` or `inject` call must belong to the same model kind — you cannot mix these open-model rate steps with the closed-model concurrent-user steps in one call.
  • Does `constantUsersPerSec(20)` mean twenty users are running at once?
    No. It means twenty new virtual users are *started* each second. How many are running concurrently depends on how long one pass through your scenario takes, and the step does not declare it.
  • What is the head-count of `rampUsersPerSec(0).to(100).during(Duration.ofMinutes(1))`?
    3,000. The mean of 0 and 100 is 50 arrivals a second, held over 60 seconds. Reading the target rate instead would give 6,000 — double the truth.

A turnstile counter set to admit twenty people a minute is not the same as a ticket for twenty people. The rate steps set the turnstile; how crowded the hall gets depends on how long each visitor stays.

saying these in an interview costs you the question

  • Reading the first argument as a total user count rather than a rate
  • Summing a ramp's two rates instead of averaging them
  • Multiplying a ramp by its target rate to get the head-count
  • Assuming the rate argument has to be a whole number
  • Treating a bare during(10) as ten minutes rather than ten seconds