In Gatling, why does a Kotlin simulation have to write incrementUsersPerSec(20.0) while incrementConcurrentUsers(20) is fine as written?
answer
- The two builders take different numeric types
- Rates may be fractional, user counts cannot
- startingFrom is typed like its builder
- Kotlin will not widen an Int literal
basics
~20 sThe 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.
solid answer
~40 sThe two staircase builders differ in argument type because their workload models do: arrivals per second may be fractional, so `incrementUsersPerSec(double)` takes a `Double`, while a count of concurrent users cannot be fractional, so `incrementConcurrentUsers(int)` takes an `Int`. `startingFrom` mirrors that exactly — `startingFrom(double)` on the open chain, `startingFrom(int)` on the closed one. Java and Scala widen an integer literal to a double silently, so `incrementUsersPerSec(20)` compiles there. Kotlin does no implicit numeric widening, so the same line is a type error and must be written `incrementUsersPerSec(20.0)` — and likewise `.startingFrom(20.0)`. Gatling's own Kotlin samples are written with the decimal points for exactly this reason.
code
kotlin · 9 linesval openStairs = incrementUsersPerSec(20.0)
.times(8)
.eachLevelLasting(Duration.ofMinutes(2))
.startingFrom(20.0)
val closedStairs = incrementConcurrentUsers(20)
.times(8)
.eachLevelLasting(Duration.ofMinutes(2))
.startingFrom(20)go deeper
Be ready to say which builder takes a Double and which takes an Int, and to add the decimal point when a Kotlin simulation rejects the literal.
Be ready to explain why the types differ at all: arrival rates may be fractional, held user counts cannot, and startingFrom follows its own chain.
Be ready to spot a snippet copied between SDKs and to name the three things that change across languages while the step names stay identical.
Be ready to decide whether a suite standardises on one authoring language, and what the cost is when samples circulate between two of them.
The staircase builders take different numeric types, and one of the five authoring languages refuses to paper over the difference. It is a two-minute fact that costs an afternoon when a Kotlin simulation is ported from a Java sample. ## Which type goes where | chain | increment argument | `startingFrom` argument | |---|---|---| | `incrementUsersPerSec(...)` (open) | `Double` | `Double` | | `incrementConcurrentUsers(...)` (closed) | `Int` | `Int` | `times(levels)` is an `Int` on both, because a level count cannot be fractional either way. The split is not arbitrary. The open builder's levels are **arrival rates**, and Gatling's injection DSL allows fractional rates throughout — `constantUsersPerSec(0.5)` is a legal half-arrival per second, so a staircase increment of `2.5` is legal too. The closed builder's levels are **counts of users held in the system**, and there is no such thing as half a user, so the API refuses to accept one. ## Why only Kotlin trips The step names and signatures are the same across the JVM SDKs; what differs is what each language will do with a literal that does not match: * **Java** — widens an `int` literal to `double` implicitly, so `incrementUsersPerSec(20)` compiles. * **Scala** — the same widening applies, so `incrementUsersPerSec(20)` compiles; Gatling's own Scala sample even writes `.startingFrom(10)` on the open chain with a comment marking the parameter as a Double. * **Kotlin** — performs **no** implicit numeric widening. `incrementUsersPerSec(20)` is a type mismatch, and so is `.startingFrom(20)` on the open chain. * **JavaScript and TypeScript** — have a single number type, so the distinction is invisible at the call site; a fractional value simply reaches a builder that accepts it. The practical rule for a Kotlin simulation is to write the decimal point on every open-model number and to leave it off every closed-model one: ```kotlin // open: Double everywhere incrementUsersPerSec(20.0) .times(8) .eachLevelLasting(Duration.ofMinutes(2)) .startingFrom(20.0) // closed: Int everywhere incrementConcurrentUsers(20) .times(8) .eachLevelLasting(Duration.ofMinutes(2)) .startingFrom(20) ``` ## The trap this really sets The error itself is harmless — the compiler says the argument types do not match, and you add a `.0`. The real risk is the *other* direction, when someone copies a Kotlin snippet into Java or Scala, hits no error, and concludes the two builders are interchangeable. They are not, and the difference shows up later in a place the compiler cannot help with: a fractional increment is meaningful for the open builder and impossible for the closed one, so a profile that wanted levels 12.5 arrivals per second apart cannot simply be re-expressed in the closed model. ## A related spelling difference in the same chain The duration arguments in the same chain also vary by SDK, and it is worth reading them as a set rather than one at a time: * A **bare number** means seconds in every SDK — `.eachLevelLasting(120)` is two minutes. * **Java and Kotlin** additionally accept a `java.time.Duration`, such as `Duration.ofMinutes(2)`. * **Scala** accepts a duration literal such as `2.minutes`. * **JavaScript and TypeScript** accept an object literal such as `{ amount: 2, unit: "minutes" }`. So a single staircase expression can differ from its neighbour in three ways at once — the numeric literal, the duration spelling and, in Scala only, the registration call `inject` in place of `injectOpen` or `injectClosed`. The **step names themselves never change**, which is why a snippet copied between languages usually looks right and still refuses to build. ## What does not differ It is worth being exact about how narrow this difference is, because the surrounding sameness is what makes it easy to trip over. The **step names are identical** in all five authoring languages: `incrementUsersPerSec`, `incrementConcurrentUsers`, `times`, `eachLevelLasting`, `separatedByRampsLasting` and `startingFrom` are spelled the same everywhere. The **semantics are identical** too — the same level formula, the same optionality, the same ordering constraint. Even the numeric split survives where a language does not enforce it. Gatling's JavaScript and TypeScript sample writes `incrementConcurrentUsers(5)` against `incrementUsersPerSec(5.0)`, mirroring the JVM types although the language has a single number type and would not object either way. That is the right instinct to copy: the distinction is about the workload model, not about the compiler, and writing the literal in the shape the model implies keeps a profile readable when it moves between languages. ## What to check in a review 1. Does the numeric type match the model — `Double` for arrivals per second, `Int` for concurrent users? 2. Does `startingFrom` use the same type as the builder it follows? 3. In Kotlin, is every open-model number written with a decimal point? 4. Is a bare duration number intended as seconds, and does the total run length follow from it?
- Is a fractional increment actually useful, or just permitted?Useful. Arrival rates in Gatling may be fractional throughout, so a staircase can legitimately step by 2.5 arrivals per second, or by a peak divided by a level count that does not divide evenly. The closed builder has no equivalent, because it counts whole users held in the system.
- Do the step names themselves change between the SDKs?No. `incrementUsersPerSec`, `incrementConcurrentUsers`, `times`, `eachLevelLasting`, `separatedByRampsLasting` and `startingFrom` are spelled identically in Java, Kotlin, Scala, JavaScript and TypeScript. What varies is the numeric literal, the duration spelling, and the registration call — Scala's single `inject` rather than `injectOpen` and `injectClosed`.
saying these in an interview costs you the question
- Assuming both staircase builders take the same numeric type
- Expecting Kotlin to widen an Int literal to Double
- Giving startingFrom a type the builder does not take
- Believing a fractional increment works for concurrent users