In a Gatling scenario, a token-refresh request intermittently returns 401 — how do you retry just that step, and what do the failed attempts do to the run's request statistics?
answer
- Wrap the flaky step, not the scenario
- The number is attempts, not extra retries
- Failed attempts are still recorded
- tryMax(1) is exitBlockOnFail
- Status resets between attempts
basics
~20 sWrap the refresh step in tryMax(n): any failure inside restarts the wrapped chain, up to n attempts in total. Every attempt is recorded, so the failed ones still appear as KO requests in the run's statistics.
solid answer
~40 sPut `tryMax(3)` around the refresh call alone, not around the scenario. A `401` is already a failure without extra work, because every Gatling HTTP request carries a default status check accepting 2xx and 304 unless you declare a status check of your own. When the block fails, Gatling abandons the rest of the wrapped chain and restarts it from the top, resetting the user's status between attempts, so a user whose third try works continues normally. The number is total attempts: `tryMax(1)` never retries and is literally how `exitBlockOnFail` is implemented. What it does not do is clean the numbers — every attempt is logged, failing ones included, so a twice-retried refresh adds three requests and two KOs to the run.
code
java · 8 linesScenarioBuilder scn = scenario("authenticated browse")
.tryMax(3).on(
http("refresh token").post("/oauth/token")
)
.exitHereIfFailed()
.exec(
http("orders").get("/orders")
);go deeper
Be ready to name tryMax as the retry block and to place it around a single request rather than the whole scenario.
Explain that the argument is total attempts, that the rest of the attempt is skipped on failure, and that the user's status resets between attempts.
Show that you know retries change the run's numbers: extra requests and KOs that no injection profile asked for, and a report someone will misread unless you say so.
Own the policy: which failures a suite is allowed to retry, where the retry lives, and how a recovered failure is reported so the run stays honest.
## Wrap the flaky step, not the scenario `tryMax(times)` is Gatling's retry block. It wraps a chain; if anything inside that chain fails, the virtual user abandons the rest of the wrapped chain and starts it again from the top, up to a maximum number of attempts. For a token refresh that intermittently answers `401`, the block goes around **that request alone**: ```java tryMax(3).on( http("refresh token").post("/oauth/token") ); ``` Wrapping the whole scenario instead would replay every step that already worked, repeat side-effecting calls such as an order submission, and add requests to the run that no part of your load model asked for. ## What counts as a failure inside the block Two things mark the block failed, and `tryMax` reacts to both: 1. **A failed check.** Every Gatling HTTP request carries a default status check unless you declare a status check of your own; that default accepts **2xx and 304**. A `401` therefore fails without you writing anything. The corollary is the trap: if you add your own status check that tolerates `401`, the response becomes a success and `tryMax` never fires. 2. **A technical error** — a connection failure, a read timeout, a request that could not be built. ## How many attempts `tryMax(n)` really makes The counter starts at 0 when the user enters the block. Each failure increments it, and the block retries while it is still failed **and** the counter is below `n`. So the number is **total attempts**, not retries on top of a first try. | call | attempts if every one fails | equivalent | |---|---|---| | `tryMax(1)` | 1 | exactly `exitBlockOnFail` | | `tryMax(3)` | 3 — the first try plus two retries | — | | `exitBlockOnFail()` | 1 | implemented as `tryMax(1)` | `exitBlockOnFail` is not merely similar to `tryMax(1)`; in the source it *is* `tryMax(1)`. It is the block you use when you want a failure to skip the rest of the wrapped chain without ever retrying it. ## The rest of the attempt is skipped The moment a step inside the block fails, Gatling unwinds straight back to the `tryMax` — the actions that came after the failing one in that attempt do **not** run. That is what makes the block useful around a login-then-use sequence: a failed login does not proceed to the call that needs the token. Between attempts Gatling **resets the user's status to success**, so a user whose third attempt works leaves the block in a normal state and the rest of the scenario runs as usual. ## What it does to your numbers This is the half candidates forget, and Gatling's reference says it plainly: *all requests performed in failing iterations will be logged, including the failing one.* - A refresh that fails twice and succeeds on the third attempt contributes **three** requests to the run, two of them **KO**. - Your request count is higher than the traffic your injection profile describes, and your error count is higher than the number of users who actually suffered. - Retrying does not clean the statistics; it only rescues the virtual user. So the honest description of a retried run is: the users completed, and the run recorded the failures anyway. If the report's error column is the thing someone is going to read, say out loud that some of those KOs were recovered. ## After the retries are exhausted If every attempt fails, the block exits **failed**, and that failure propagates outward to the user's session. That is what makes the next line work: ```java tryMax(3).on( http("refresh token").post("/oauth/token") ).exitHereIfFailed(); ``` `exitHereIfFailed` ends the scenario for a user whose session is currently in a failed state — which, after the block above, is exactly the users whose retries ran out. Users who recovered sail straight past it, because their status was reset. ## Traps - **Reading `tryMax(3)` as "three retries".** It is three attempts. - **Retrying non-idempotent work.** A retried payment is two payments. - **Wrapping the scenario instead of the step.** You replay successes and inflate every count. - **Expecting retries to erase the KOs.** They are logged, always. - **Assuming a `401` needs a custom check to be a failure.** The default status check already rejects it — and adding a permissive one is what would hide it.
- How many times does tryMax(1) run the chain it wraps, and what is it equivalent to?Once, with no retry. It is exactly `exitBlockOnFail`, which Gatling implements as `tryMax(1)`. Both are the block you reach for when a failure should skip the rest of the wrapped chain but not repeat it.
- What happens to the actions after the failing one inside a tryMax block?They are skipped. As soon as a step is marked failed, the virtual user unwinds straight back to the tryMax, so the remainder of that attempt never runs. That is what makes the block safe around a login-then-use pair: a failed login does not proceed to the call that needed the token.
- Why is wrapping a whole Gatling scenario in tryMax worse than wrapping one step?A retry replays everything in the block, including the requests that already succeeded and any step with a side effect. Your request count and your error count both grow, the load you apply stops matching the profile you declared, and a non-idempotent call such as an order submission happens twice.
saying these in an interview costs you the question
- Reading tryMax(3) as three retries after a first attempt
- Wrapping the whole scenario instead of the flaky step
- Believing a successful retry removes the earlier KOs
- Retrying non-idempotent work such as a payment
- Assuming a 401 needs a custom check before Gatling calls it a failure