skip to content

Across a suite of Gatling simulations, would you declare the waiting policy on each pause call, on the injected population, or on setUp, and how would you decide?

level: principalimportance: nice to knowfreq 28%

answer

  1. one policy, three places to put it
  2. the call beats the population beats setUp
  3. make the run-wide decision visible
  4. shared chains carry durations, not policy

basics

~20 s

Default to setUp, stated explicitly even when it matches the constant default, so one visible line governs the run. Drop to the population only when two user profiles differ; force one call only when that wait is a protocol requirement.

solid answer

~40 s

All three placements work and they override in a fixed order — a `force` argument on a call beats the population's policy, which beats `setUp`'s, which beats the constant default — so the choice is about who can see the decision. I put it on `setUp` by default and write it explicitly, so a reviewer learns what the run did without knowing the framework's default. I move it to the injected population only when one run deliberately holds two user profiles that wait differently. I force an individual `pause` only when that wait is mechanically required — a poll interval, a settle time — because a forced call is invisible to anyone reading `setUp`. And I keep policy out of shared chains, so the simulation that composes them owns the interpretation of their durations.

code

java · 6 lines
java
setUp(
  browse.injectOpen(rampUsers(200).during(Duration.ofMinutes(2))),
  probe.injectOpen(atOnceUsers(1)).disablePauses()
)
  .constantPauses()
  .protocols(httpProtocol);

go deeper

for a junior

Be ready to point at the one line in a simulation that decides how its declared waits are interpreted.

for a middle

Be ready to state the override order — call, population, setUp, default — and to show that a population's policy replaces rather than blends with the run's.

for a senior

Be ready to explain why an unstated wait policy makes two runs quietly incomparable and where you would record the one a run actually used.

for a principal

Be ready to own the convention across a suite: which placement is the default, what a shared chain may declare, and how the decision stays visible a year later.

Gatling lets the same waiting policy be expressed in three different places, and they are not interchangeable in the way a reviewer, or a future maintainer, has to read them. The mechanism is fixed — a `force` argument on a call beats a policy on the population, which beats a policy on `setUp`, which beats the built-in constant default — so the question is entirely one of where you *choose* to put it. ## What each placement buys **On `setUp`.** One line, at the top of the file, that a reviewer reads before anything else. It applies to every population and every chain the simulation composes, including chains pulled in from a shared library. It is the right home for a decision that is a property of *the run*: this run models waiting behaviour, or this run is a flat-out probe with `disablePauses()`. **On the injected population.** Nearly the same methods exist on a population — `disablePauses()`, `constantPauses()`, `exponentialPauses()`, `uniformPauses(...)`, `customPauses(...)` and the generic `pauses(type)`; only `normalPausesWithStdDevDuration(...)` and `normalPausesWithPercentageDuration(...)` are `setUp`-only, and a population reaches that distribution as `pauses(normalPausesWithStdDevDuration(...))`. So one `setUp` can hold a browsing population with realistic waits and a probe population with none. This is the right home when the decision is a property of *the user profile* rather than of the run, and it is the only placement that lets two profiles differ inside a single run whose results you want to read together. **On the individual `pause(...)` call.** A `force` argument overrides everything for exactly one wait. It is precise and it is nearly invisible: nobody reading `setUp` will learn that one wait somewhere in a two-hundred-line chain opts out. Reserve it for waits that are not think time at all — a poll interval, a settle time before a read-after-write check — where the value is part of the protocol, not part of the user model. ## How I would decide 1. **Default to `setUp`, and put it there even when it agrees with the default.** Writing `constantPauses()` explicitly costs one line and turns an implicit assumption into something a reviewer can see and a diff can change. A reader should never have to know the framework default to know what the run did. 2. **Move it to the population only when two profiles in one run genuinely differ.** If they differ, they belong in one run so their results share a clock; if they do not, splitting the policy across populations is noise. 3. **Use a `force` argument only for waits that are mechanically required.** If you find yourself forcing more than one or two per simulation, the run-wide policy is wrong and you are patching it call by call. 4. **Do not let a shared chain library declare a policy.** Chains meant for reuse should carry durations and nothing else, so that the simulation composing them owns the interpretation. A library chain that forces its own pause type will surprise every simulation that reuses it. 5. **Pin it, whatever you choose.** Two runs whose waiting policy differs are not comparable, and the policy is invisible in the report — so an unstated policy is a difference nobody will spot when they compare last week's numbers with today's. ## Two traps this decision walks into * **There is no configuration key for it.** Nothing in `gatling.conf` sets the pause type; it exists in the simulation's code or not at all. A per-environment switch therefore has to be built by hand in the simulation, and once it is, the run's behaviour depends on something the report does not show. If you build one, make the run description say which way it was set. * **"Disable all waiting" is not one switch.** The pause type is consulted only when Gatling builds a `pause` action. `disablePauses()` leaves every `pace` interval and every `rendezVous` barrier exactly where they were. A simulation that leans on `pace` to hold a rate does not become a flat-out probe when you disable its pauses — it keeps its pacing and loses only the think time, which is a third behaviour that nobody asked for and that the report will not distinguish from either intended one. ## The shape I would land on | decision | where it lives | |---|---| | does this run model waiting at all | `setUp`, always written explicitly | | do two user profiles wait differently | the injected populations | | is this particular wait a protocol requirement | a `force` argument on that call | | does a reusable chain need a policy | it does not — the composing simulation owns it | None of this is enforced by the framework: every one of these placements compiles and runs. The value of choosing one deliberately is that a year later the answer to "why did this run produce that number" is one line at the top of the file rather than a search through the chains.

  • Why write constantPauses() when constant is already the default?
    Because a reader should not need to know the framework's default to know what the run did. Writing it makes the assumption visible in review, gives a diff something to change when it stops holding, and removes the ambiguity between a deliberate choice and an omission.
  • Where would you record which policy a given run used?
    In the run description passed to the launcher, because nothing in the report shows it. The statistics table records response times, not the waiting policy that shaped the arrival pattern, so two runs with different policies look comparable and are not.
  • Should a shared chain library ever force its own pause type?
    Almost never. A chain meant for reuse should carry durations and let the composing simulation interpret them; a forced type inside a library chain silently overrides whatever the composing setUp declared, and the surprise lands far from where it was written.

saying these in an interview costs you the question

  • Relying on the framework default instead of stating it.
  • Forcing a pause type call by call to patch a wrong policy.
  • Letting a shared chain library declare a wait policy.
  • Treating disablePauses as removing pace and rendezVous too.