In a Gatling simulation, which building blocks make up a throttle profile, and what does each one do?
answer
- A ceiling laid over an existing run
- Three shapes: ramp, jump, hold
- reachRps in, jumpToRps, holdFor
- Kotlin spells in as during
basics
~10 sThree 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.
solid answer
~40 sGatling's `throttle(...)` takes an ordered list of three step builders. `reachRps(t).in(d)` ramps the ceiling from its current value to `t` requests per second over `d`; `jumpToRps(t)` moves it there immediately and consumes no time; `holdFor(d)` keeps the current ceiling for `d`. Both targets are integers, so a throttle ceiling cannot be fractional. A bare number duration means seconds; Java and Kotlin also take a `java.time.Duration`, Scala a `5.minutes` literal, and the JavaScript/TypeScript SDK an `{ amount, unit }` object. Because `in` is reserved in Kotlin, a Kotlin simulation writes `reachRps(200).during(30)` or backticks the call. You attach the chain either to `setUp`, where it caps the whole run, or to one injected population with `.throttle(...)`.
code
java · 10 linessetUp(
browse.injectOpen(constantUsersPerSec(120).during(Duration.ofMinutes(10))),
checkout.injectOpen(constantUsersPerSec(80).during(Duration.ofMinutes(10)))
).protocols(httpProtocol)
.throttle(
reachRps(200).in(Duration.ofSeconds(30)),
holdFor(Duration.ofMinutes(5)),
jumpToRps(50),
holdFor(Duration.ofMinutes(4))
);go deeper
Be ready to name the three builders and say what each does to the ceiling, and to place the chain either on setUp or on one injected population.
Be ready to explain that the targets are integers, that only reachRps and holdFor consume time, and how the duration argument is spelled in each SDK.
Be ready to say why the Kotlin alias exists, and to point out that a second throttle call on setUp replaces the first rather than merging with it.
Be ready to argue whether a throttle belongs in a team's simulations at all, given that it constrains only HTTP and JMS and bounds the run's duration as a side effect.
Gatling's `throttle(...)` is a **requests-per-second ceiling** laid over a run that some other part of the simulation is already driving. You hand it an ordered list of *throttle step builders*, and together they describe how that ceiling moves over time. There are only three builders, they are statics on the core DSL, and every throttle profile in every SDK is some chain of them. ## The three builders | builder | what it sets the ceiling to | time it consumes | |---|---|---| | `reachRps(target).in(duration)` | ramps linearly from the current ceiling to `target` req/s | `duration` | | `jumpToRps(target)` | moves to `target` req/s immediately | none | | `holdFor(duration)` | keeps whatever the ceiling currently is | `duration` | Two consequences fall straight out of that table: * **The targets are integers.** `reachRps(int)` and `jumpToRps(int)` are declared over `int` in the Java API and over `Int` in the Scala core. A throttle ceiling of `12.5` requests per second is not expressible — unlike an injection rate such as `constantUsersPerSec`, which does take a fraction. * **`jumpToRps` adds no duration.** Only `reachRps` and `holdFor` contribute time. A profile that ends on a bare `jumpToRps(50)` ends at the moment the previous step ended; the jump sets a target that nothing then holds. ## Where you attach the chain The chain goes in one of exactly two places. 1. **On `setUp`** — `setUp(...).throttle(reachRps(200).in(30), holdFor(Duration.ofMinutes(9)))`. This is the run-wide ceiling: every population in the run draws from the same budget. 2. **On one injected population** — `scn.injectOpen(...).throttle(...)`, chained onto the population builder the same way `.protocols(...)` is. That population gets its own ceiling. Both can be present at once, and then a request needs *both* to have room. Note that a second `.throttle(...)` call on `setUp` **replaces** the first rather than adding to it, in the Scala core and in the Java API alike — it is a plain assignment on both sides, so it does not share the merge-versus-replace divergence that `.protocols(...)` has. ## Spelling the duration, per SDK The builder *names* are the same everywhere; the duration argument is not. | SDK | how you write ten seconds | |---|---| | Java, Kotlin | `Duration.ofSeconds(10)`, or the bare number `10` | | Scala | `10.seconds`, or the bare number `10` | | JavaScript, TypeScript | `{ amount: 10, unit: "seconds" }`, or the bare number `10` | A bare number always means **seconds**: the Java API documents its overload as *"the duration in seconds"*, and Gatling's Scala `Predef` carries an implicit conversion from `Int` to a `FiniteDuration` of seconds, so `reachRps(200).in(10)` compiles and means ten seconds in a Scala simulation too. ## The Kotlin collision `in` is a reserved word in Kotlin, so `reachRps(200).in(30)` does not compile there. Gatling ships two ways out, and the Kotlin sample in its own documentation uses the second: * backtick the call — `` reachRps(200).`in`(30) `` * use the alias — `reachRps(200).during(30)` `during` exists only on the object `reachRps(...)` returns; `holdFor` and `jumpToRps` have no `in` to collide with, so they are spelled identically in Kotlin and Java. This is one of three places in the Java API where Kotlin needs an alias; the others are on assertions and on checks, where `is` becomes `shouldBe` and `in` becomes `within`. ## A worked profile ```java setUp( browse.injectOpen(constantUsersPerSec(120).during(Duration.ofMinutes(10))), checkout.injectOpen(constantUsersPerSec(80).during(Duration.ofMinutes(10))) ).protocols(httpProtocol) .throttle( reachRps(200).in(Duration.ofSeconds(30)), holdFor(Duration.ofMinutes(5)), jumpToRps(50), holdFor(Duration.ofMinutes(4)) ); ``` Read left to right, the ceiling climbs from zero to 200 requests per second over thirty seconds, stays at 200 for five minutes, drops instantly to 50, and stays there for four minutes. The two scenarios share that single ceiling — it is one budget for the run, not 200 each. ## What the chain is not The builders describe a **limit**, never a demand. Nothing in `reachRps(200)` asks Gatling to create traffic; the injection profile does that. If the populations only offer 120 requests per second, the ceiling is simply never touched and the run finishes at about 120. The throttle is also the thing that bounds how long the run lasts: Gatling takes the sum of the `reachRps` and `holdFor` durations as a duration limit and stops the run there, exactly as a declared `maxDuration` would. Finally, throttling reaches only two protocols. HTTP request execution and JMS send both route their requests through the throttler; WebSocket, SSE and the other actions do not consult it at all, so a throttle over a WebSocket-only scenario constrains nothing.
- Can a throttle ceiling be fractional, the way an injection rate can?No. `reachRps` and `jumpToRps` are declared over an integer target in both the Scala core and the Java API, so the smallest step you can express is one request per second. Injection rate builders such as `constantUsersPerSec` do accept a fractional rate, which is why the two surfaces are easy to confuse.
- How long does a throttle profile that ends on jumpToRps(50) actually last?Exactly as long as the steps before it. Gatling computes a throttle profile's duration by summing only the `reachRps` ramps and the `holdFor` plateaus; `jumpToRps` is an instantaneous target change that contributes nothing. A trailing jump with no `holdFor` after it therefore changes a ceiling that is never exercised.
saying these in an interview costs you the question
- Believing throttle generates traffic rather than capping it
- Expecting a fractional ceiling; both rps targets are integers
- Writing reachRps(200).in(30) in Kotlin, where in is reserved
- Assuming jumpToRps adds time to the throttle profile