skip to content

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%

answer

  1. One expression versus explicit steps
  2. The helper buys compactness with uniformity
  3. Unequal levels force hand-chained steps
  4. A staircase is itself a chainable step

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.

solid answer

~40 s

The staircase builder buys one thing: a whole capacity profile as a single parameterised expression, which is far easier to read, review and drive from configuration than fifteen hand-written steps. It buys that with uniformity. The increment is constant, every level lasts the same time, and every ramp lasts the same time. So I keep the helper while the profile is genuinely a linear progression of equal-length levels, and I hand-chain `constantUsersPerSec` and `rampUsersPerSec` steps the moment it is not — a longer hold at the top level, a geometric rather than linear progression, or a different ramp into the highest transition. The other honest trigger is readability: a staircase whose arithmetic a reviewer cannot follow to the intended peak is worse than the explicit steps it replaced, however compact it looks.

code

java · 9 lines
java
setUp(
  scn.injectOpen(
    incrementUsersPerSec(20)
      .times(8)
      .eachLevelLasting(Duration.ofMinutes(2))
      .startingFrom(20),
    constantUsersPerSec(160).during(Duration.ofMinutes(30))
  )
);

go deeper

for a junior

Be ready to say that the staircase helper produces equal levels at a constant increment, and that anything uneven has to be written as separate steps.

for a middle

Be ready to list what the helper fixes for the whole run — increment, level duration, ramp duration, linear shape — and to name what each costs you.

for a senior

Be ready to describe the middle ground you would actually ship: a uniform staircase for the ascent with an explicit step appended for the exception.

for a principal

Be ready to set the convention: how a profile's intended peak is stated in source, when the team may hand-chain, and why uniform arithmetic beats hand-kept invariants.

This is a choice about how a load profile is **expressed**, not about what the profile should be. What the levels ought to be, and what a capacity run tells you when it finishes, are questions for whoever owns capacity planning; the question here is which Gatling construct states the answer most honestly. ## What the helper is actually worth One expression replaces an alternating run of holds and ramps. For eight levels with ramps and a starting level that is fifteen hand-written steps — eight holds and seven ramps — collapsed into five lines. That matters for three concrete reasons: * **It is parameterisable.** The increment, the level count and the two durations are four values. Drive them from configuration and one simulation covers a smoke profile and a full capacity profile without a second code path. * **It cannot drift.** Hand-written alternating steps have to agree with each other — the end rate of each ramp must equal the rate of the level after it. That invariant is easy to break in an edit and invisible in review. The helper derives every rate from one formula, so it cannot be inconsistent with itself. * **It reads as intent.** `incrementUsersPerSec(20).times(8)` says "a capacity staircase" in a way sixteen steps do not. ## What it costs Everything the helper derives, it derives uniformly: | quantity | the helper | hand-chained steps | |---|---|---| | progression between levels | one constant increment, linear only | any progression at all | | level duration | one value, every level | per level | | ramp duration | one value, every gap | per gap, or none in places | | ramp shape | linear | linear, or any step you can name | | randomised arrival spacing | not available on a staircase | `randomized()` on each constant or ramp step | That last row is a real limit rather than an inconvenience: `randomized()` is declared on the constant-rate and ramp-rate steps only, so a staircase's levels always inject at regular intervals. A profile that needs irregular spacing has to be written out. ## Where I draw the line I keep the helper when all of these hold: 1. the levels form a linear progression; 2. every level holds for the same time; 3. the transitions are all the same, whether that is instant or one fixed ramp; 4. a reviewer can follow the arithmetic from the source to the intended peak. I hand-chain as soon as one of them fails. In practice the first to fail is usually the second: a capacity run often wants a short dwell on the low levels and a much longer hold at the top, and the helper simply cannot express that. The usual middle ground is worth naming — keep the staircase for the ascent and **append** a separate step after it, since the finished staircase is an ordinary injection step and can be chained with others of the same model kind: ```java setUp( scn.injectOpen( incrementUsersPerSec(20) .times(8) .eachLevelLasting(Duration.ofMinutes(2)) .startingFrom(20), constantUsersPerSec(160).during(Duration.ofMinutes(30)) ) ); ``` That keeps the uniform part uniform and states the exception explicitly, which is usually more honest than either extreme. ## The failure mode to guard against The helper's compactness hides arithmetic, and hidden arithmetic is where capacity profiles go wrong. Two specific traps are worth a standing convention: * Omitting `startingFrom` on an unramped staircase costs the entire top level and spends the first level injecting nobody — while leaving the run length unchanged, so nothing looks wrong. * A bare-number duration means **seconds**, so `.eachLevelLasting(10)` is ten seconds, and a capacity run that should take twenty minutes finishes in eighty seconds. Both are invisible in a diff and obvious in the run's own arrival-rate output. My rule is that any staircase states its intended peak in the source — a named constant, with the increment derived from it — so that a reviewer checks one number rather than reconstructing eight. ## The constraint that is not negotiable Whichever way the profile is expressed, every step in one injection call must be of the **same** workload model kind. A staircase of arrival rates cannot be followed by a hold of concurrent users in the same call; that is a modelling decision made before any of this, and the DSL will not let it be fudged afterwards.

  • How would you drive a staircase from configuration rather than hard-coding it?
    Read the four values — increment, level count, level duration and ramp duration — from system properties or the simulation's own constants, and derive the increment from a stated peak divided by the level count. One simulation then serves a short smoke profile and a full capacity profile with no second code path and no duplicated arithmetic.
  • If the profile has to change mid-run, does a staircase still work?
    A staircase describes an ascent declared before the run starts; it has no mechanism to react to anything observed during the run. Gatling's injection profiles are fixed at registration, so a profile that must respond to the system's behaviour is not a staircase problem at all.
  • Is there a reason to prefer explicit steps even when the profile is uniform?
    Occasionally, yes — when the team reading the simulation is not fluent in the helper and the explicit steps document the profile better for them. That is a legitimate readability argument, but it should be a deliberate team decision rather than a default, because hand-written steps can drift out of agreement with each other.

saying these in an interview costs you the question

  • Forcing an uneven profile into a uniform helper
  • Assuming a staircase can vary its level durations
  • Treating a staircase as the only way to ramp
  • Chaining a staircase with steps of the other model