While a Gatling closed injection step holds 300 concurrent users, one virtual user finishes its scenario — what does Gatling start in its place?
answer
- Replacement, not repetition
- New id, new session, first action
- No pool is preallocated anywhere
- Level is an input, total is an output
- Closed populations show no progress bar
basics
~20 sA 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 sThe 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
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.
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.
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.
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.