Controllers: Simple, Loop, Transaction, If, While, Switch, and Throughput: Diagnostics, Failure Modes, and Production Practices
Controller defects alter workload shape before they alter latency charts. Diagnose them by comparing the expected control-flow graph with actual JTL labels and independent target request counts, then isolate generator/control state before touching server capacity.
Learning objectives
- Contain and diagnose an infinite/near-infinite While loop.
- Recognize condition-evaluation cost as generator overhead.
- Correct a Throughput Controller mistaken for a rate controller.
- Avoid child + transaction double counting.
- Find timers/assertions unintentionally inherited through controller scope.
- Simplify deep nesting without deleting business semantics or evidence.
1. Preserve first-failure evidence
http://127.0.0.1:8000, ≤2 threads, and bounded
duration.
Preserve the failing JMX, exact branch variables/data/properties,
JTL, matching jmeter.log, target event log/stats, and
generator CPU/memory before editing controllers.
2. Diagnostic sequence
Logical controllers decide order, repetition, or inclusion. Only executed samplers create target traffic; transaction samples are reporting artifacts rather than extra HTTP requests.
flowchart TD E[Preserve JMX + variables/data + JTL + jmeter.log + target evidence] --> V[Confirm JMeter/Java/tool versions] V --> W[Recompute Thread Group × controller opportunities] W --> C[Inspect If/While/Switch/Throughput state] C --> S[Inspect Timer/Assertion/Processor scope] S --> J[Compare JTL child/transaction labels] J --> T[Compare independent target request counts] T --> G[Inspect generator CPU/GC/listener cost] G --> U[Inspect SUT telemetry/latency/errors] U --> X[Remote/CI/container state if relevant] X --> F[Least destructive controller correction] F --> R[Small bounded rerun]
3. Failure mode: While condition never becomes false
Broken configuration: POLL_MORE=true is initialized but
the extractor is attached to the wrong sampler, so Poll responses
never update it. The While loop keeps generating Poll requests.
Bounded reproduction only: Thread Group duration 4 seconds, one thread, and a 200 ms Constant Timer under Poll. Stop if request count exceeds the predicted safety ceiling.
Evidence: repeated Poll labels, target /poll count
rising, POLL_MORE unchanged. Repair the extractor
scope/default and keep a hard external duration guard.
4. Failure mode: non-idempotent function in While condition
A condition such as a counter function appears to mean “loop three times,” but While evaluates its condition before and after child sampling. The counter can advance twice per cycle and behave unexpectedly.
Use Loop Controller for fixed repetition. Use While for state-driven repetition where a separate state producer sets a stable true/false value.
5. Failure mode: expensive If condition
A large JavaScript expression is evaluated for every user/iteration or every child. Target latency stays stable, but generator CPU rises and achieved RPS falls.
Repair: Interpret Condition as Variable Expression + precomputed boolean/JEXL3/Groovy when appropriate. Measure the same tiny workload before/after; do not increase heap first.
6. Failure mode: Throughput Controller used as “100 RPS”
An engineer sets Throughput=100 in percent mode and expects 100 requests/s. That simply means execute the branch on 100% of controller opportunities. Request rate still depends on threads, response time, timers, loops, and arrival model.
Repair the requirement mapping: Throughput Controller for population/branch frequency; Chapter 7 timer/workload mechanisms for request/arrival rate.
7. Failure mode: transaction and children double-counted
Additional transaction mode produces five HTTP child rows plus one Transaction row. A report sums all six as “requests.” Throughput/volume is overstated by 20% in that example.
Repair the analytics: filter transaction labels for protocol-request counts or use independent target counts. Alternatively use parent mode if the reporting pipeline is transaction-centric and accepts losing separate child CSV rows.
8. Failure mode: parent mode hides child CSV detail unexpectedly
A baseline previously saved child labels; someone enables Generate Parent Sample and the CI parser suddenly sees only transaction rows. The target behavior did not change, but result schema did.
Repair by restoring the intended Transaction mode or updating the reporting contract explicitly. Do not call missing child CSV rows a server regression.
9. Failure mode: timer inherited through controller scope
A 500 ms Constant Timer is placed under a Checkout Simple Controller above both standard and express samplers. Both branches inherit it. If only the final Submit action should include think time, the timer scope is too broad.
Walk ancestors for each sampler and list every in-scope Timer before changing latency interpretation.
10. Failure mode: assertion attached to parent transaction
In Transaction parent mode, assertions attached at the Transaction Controller can apply to both children and parent by default. A response-body assertion intended for one HTTP sampler can therefore fail unrelated samples/transaction result.
Repair with narrow sampler scope or place child samplers inside a Simple Controller and scope the assertion to that intended group.
11. Failure mode: deep nesting obscures the business transaction
A tree with Simple → If → Throughput → Loop → Switch → Transaction → Simple around one request forces reviewers to reverse-engineer five control layers. Complexity increases scope mistakes and makes sample-count math harder.
Flatten layers that do not encode business behavior. Keep one controller per real concern: repetition, branch, transaction, or intentional scope boundary.
12. Causal performance separation
| Symptom | Controller/generator cause | Target cause to distinguish | Evidence |
|---|---|---|---|
| RPS unexpectedly high | extra Loop/While executions | target became faster | JTL label counts + target endpoint counts. |
| RPS unexpectedly low | skipped branches, expensive condition, duration stop | target slowdown | controller opportunities + generator CPU + server latency. |
| Sample count > target requests | Transaction reporting rows | duplicate network traffic | server event count vs JTL labels. |
| Transaction duration changes | include timer/pre-post option changed | endpoint latency change | child elapsed + transaction setting + target timing. |
| Many assertion failures after refactor | scope inheritance changed | target payload regression | tree ancestry + assertion labels/payload. |
13. Troubleshooting shortcuts to reject
- Do not increase thread count to compensate for a branch/loop configuration error.
- Do not add retries or long sleeps to an infinite polling loop.
- Do not increase JVM heap before measuring condition/listener/controller overhead.
- Do not disable assertions/timers globally to make controller counts easier.
- Do not disable TLS/RMI verification or move the experiment to production.
-
Do not delete the failed JTL/
jmeter.log/target evidence after repair.
Knowledge check
What is the first safe response to a While loop that never exits?
Contain it with a hard bounded duration/rate, preserve evidence, then inspect the condition producer and scope rather than increasing load.
Why can JavaScript If conditions reduce achieved load without changing server latency?
Condition evaluation is generator-side CPU work and current JMeter docs warn JavaScript mode can be expensive.
How do you prove Transaction rows are not extra requests?
Compare JTL labels with independent target/server request counts.
Why can a timer appear to affect the wrong branch after controller refactoring?
Hierarchy changed the timer's scope; descendant samplers inherit scoped timers even if the business intent did not.
What is the right repair for fixed 'loop three times' logic written as a While counter?
Use Loop Controller for fixed repetition; reserve While for state-driven continuation.
Official references and version notes
- Component Reference — Logic Controllers — Simple, Loop, Throughput, If, While, Switch, and Transaction Controller semantics.
- Elements of a Test Plan — scope and execution-order rules for samplers, controllers, timers, processors, and assertions.
- Functions and Variables — current JEXL3/Groovy/function/variable behavior used in conditions.
- Best Practices — GUI authoring versus CLI load execution and generator-validity guidance.
- Apache JMeter downloads — current production release and Java requirement.
Version-sensitive behavior was checked against current Apache
JMeter primary documentation on 2026-09-05. The course baseline
remains Apache JMeter 5.6.3 with a Java 17 JDK
for labs and no third-party plugins; JMeter 5.6.3 requires Java
8+. Simple Controller is organizational only. Loop Controller
multiplies its loop count by the enclosing Thread Group iterations
and exposes an index variable named
__jm__<controller-name>__idx. If Controller
should normally use
Interpret Condition as Variable Expression with a
boolean variable or __jexl3/__groovy;
JavaScript condition mode has a potentially large performance
penalty. While Controller evaluates its condition before and after
its children, so non-idempotent functions such as counters in the
condition can produce surprising behavior. Switch Controller
selects one child by numeric index or name and has explicit
fallback semantics. Throughput Controller is intentionally
documented as badly named: it controls branch execution
count/percentage, not request throughput; use a throughput Timer
for rate control. Transaction Controller creates an additional
transaction SampleResult unless Generate Parent Sample is enabled.
In parent mode, child samples do not appear as separate CSV JTL
rows. By default transaction elapsed excludes timers and
pre/post-processor processing; the optional include-duration
setting changes that measurement.
Keep the academy open
Support free, practical DevOps education.
Every lesson is designed to remain readable in a browser, downloadable from GitHub, and usable without a paid learning platform. Contributions help expand and maintain the curriculum.
0x716c4Ab160C4B66F31a28AE2448BfF68fc3a2ef0
Send only Ethereum/ERC-20 compatible assets to this
address.