skip to content

While a Gatling closed injection step holds 300 concurrent users, one virtual user finishes its scenario — what does Gatling start in its place?

level: middleimportance: should knowfreq 41%

answer

  1. Replacement, not repetition
  2. New id, new session, first action
  3. No pool is preallocated anywhere
  4. Level is an input, total is an output
  5. Closed populations show no progress bar

basics

~20 s

A brand-new virtual user with a fresh id and an empty session, not the finished one looping again. Gatling preallocates no user pool, so a held count fixes how many run at once, never how many run in total.

solid answer

~40 s

The replacement is a new virtual user, started immediately rather than at the next tick. It takes the next id from a run-wide generator, gets a **fresh session**, and enters the scenario at its first action — so every attribute a check saved, every loop counter and every captured token from the previous user is gone, and a feeder hands the new user its own record. Because users are constructed on demand rather than drawn from a pool, Gatling cannot know how many a closed profile will start: the total depends on how fast the system under test lets scenarios finish. That is why a closed population reports no total user count and the console summary shows only `active` and `done`, with no progress bar.

go deeper

for a junior

Be ready to say that a finished virtual user is replaced by a new one starting the scenario from the beginning, with nothing carried over.

for a middle

Be ready to explain why a closed profile has no knowable total user count, and what that changes in the console output during a run.

for a senior

Be ready to predict feeder consumption over a long hold, and to explain why a slower service means fewer users started at the same concurrency level.

for a principal

Be ready to decide what test data a held-concurrency suite needs when the number of users it will consume is an output of the run rather than an input.

## A finished user is replaced, not restarted While `constantConcurrentUsers(300).during(...)` is in force, Gatling keeps 300 scenarios in flight. When one of them ends, Gatling checks the current target and — if the population is now below it — starts **one new virtual user immediately**, without waiting for the next injection tick. The important word is *new*. The replacement is not the finished user going round again. Gatling takes the next id from a run-wide user-id generator, builds a **fresh session** for it and hands that session to the first action of the scenario. Everything the previous user accumulated is gone. ## What the replacement starts with - A **new user id**, one higher than the last user started anywhere in the run. - An **empty session**: every attribute a check saved with `saveAs`, every counter a loop kept, every value a function wrote is absent. - Its **own feeder record**, because a feeder is consumed per virtual user as the scenario reaches the `feed` step — the new user pulls a record of its own rather than reusing its predecessor's. - The scenario's **first action**, not the point where its predecessor stopped. This is why a long closed-model run can exhaust a finite feeder even though the concurrency level never changes: the level is 300, but the number of users that pass through the scenario keeps climbing for as long as the run lasts. ## There is no preallocated pool Nothing in the closed injection DSL asks you to size a pool of users, because Gatling does not keep one. Users are constructed on demand — at the tick when the target rises, and at the moment another user finishes. The count of users currently in flight is simply *started minus stopped*. The visible consequence is that **Gatling cannot tell you how many users a closed profile will start**. An open profile's total *is* knowable up front, because every open step pins a user count down: the count-based steps (`atOnceUsers`, `rampUsers`, `stressPeakUsers`) state it outright, and the rate-based ones (`constantUsersPerSec`, `rampUsersPerSec`, `incrementUsersPerSec`) derive it from rate × duration. Gatling sums those per-step counts into the profile's total — so the reason is the sum, not a declaration in every step. A closed profile has nothing to sum: its total depends on how fast the system under test lets scenarios finish, which is not known until the run is over. Internally, a closed injection profile reports its total user count as *absent*, and the console summary reacts to that by printing only `active` and `done` counters for the population, with **no progress bar and no "waiting" figure**. A reader used to open-model runs often reports the missing progress bar as a bug. It is not; there is no denominator to compute a percentage against. ## Held concurrency versus total work Two numbers get conflated, and they are not the same: | question | closed-model answer | |---|---| | How many users run at once? | exactly what the step declares, once the level is reached | | How many users run in total? | unknown before the run; it is an output, not an input | | How many iterations does one user run? | one — the scenario, once, then it is replaced | | Who decides the turnover rate? | the system under test, through response times | That last row is the one to carry into an interview. In a closed profile the **level** is yours and the **throughput** belongs to the service: if it slows down, users take longer to finish, fewer are replaced per second, and the run applies less work while still showing exactly 300 concurrent users. ## Reaching the level in the first place The same on-demand construction applies at the start. When a hold at 300 begins from nothing, Gatling does not create 300 users in one burst: the first tick sees 300 missing users and spreads their starts across that one-second window, so the concurrency chart shows a very steep but not vertical rise. It is worth knowing when a run's first second looks different from the rest. ## Common mistakes - Believing a closed profile reuses a fixed set of users that loop the scenario repeatedly. Each iteration is a different virtual user with its own id and session. - Expecting session attributes or a captured token to survive into the replacement user. They do not; whatever the scenario needs it must acquire again. - Sizing a finite feeder against the concurrency level. The level is not the number of users that will be started, and a queue-strategy feeder will run out if the run is long enough. - Reading the missing console progress bar as a defect, rather than as the honest report of a total that cannot be known in advance.

  • Why does Gatling report no total user count for a closed injection profile?
    Because there is nothing to sum. Every open step pins a user count down — the count-based ones (atOnceUsers, rampUsers, stressPeakUsers) state it outright, the rate-based ones (constantUsersPerSec, rampUsersPerSec, incrementUsersPerSec) derive it from rate × duration — so Gatling adds them up into a profile total. A closed step names a level instead, and the total depends on how fast scenarios finish. A closed injection profile therefore reports its total as absent, and the console summary falls back to printing only active and done counters.
  • Can a long closed-model run exhaust a finite feeder even though the level never changes?
    Yes, and it is a common surprise. The level caps how many users exist at once, not how many pass through the scenario. Over a long hold the number of users started keeps climbing, each one consuming its own record, so a queue-strategy feeder can run out mid-run.
  • How does the population reach 300 in the first place?
    At the first tick Gatling sees 300 missing users and spreads their starts across that one-second window rather than creating them in a single burst. The concurrency chart therefore shows a very steep but not vertical rise in the run's first second.

saying these in an interview costs you the question

  • Thinking the same users loop the scenario for the whole run.
  • Expecting a saved token or counter to survive into the replacement user.
  • Sizing a finite feeder against the declared concurrency level.
  • Reading the missing console progress bar as a Gatling defect.