Chapter 12Lesson 01~140 minutes

Controllers: Simple, Loop, Transaction, If, While, Switch, and Throughput: Core Concepts and Mental Model

Chapters 7–11 made timing, correctness, correlation, extraction, and data ownership explicit. Chapter 12 controls the shape of the journey itself: which steps execute, how many times they repeat, which branch is selected, and whether several child samples are also represented by a business-transaction sample.

SimpleLoopTransactionIf / While / SwitchThroughput Controller

Learning objectives

  • Explain how logical controllers affect sampler order, branch inclusion, and repetition.
  • Separate branch frequency from request-rate control.
  • Predict sample counts when Thread Group and Loop Controller iterations multiply.
  • Understand Transaction Controller child versus parent result labeling.
  • Choose safe boolean expressions for If/While controllers.
  • Inspect controller scope, result labels, and target request counts before changing a plan.

1. The practical problem: a flat script hides workload intent

A recorded browse → search → search → checkout → upsell sequence might look plausible as a flat list of HTTP requests, but it does not explain why search repeats, when checkout is skipped, which checkout mode is chosen, or whether upsell should occur for every user. Controllers turn those business rules into explicit test-plan structure.

The danger is that controller names can mislead. A Throughput Controller does not produce “100 requests per second.” A Transaction Controller can add a new reporting sample without sending any request. A While Controller can generate effectively unbounded traffic if its condition never becomes false.

Safety boundary: all executable examples use only http://127.0.0.1:8000, maximum 2 threads, bounded loops, synthetic data, and explicit Thread Group duration/stop rules for failure reproductions.

2. Mental model: thread → controller → sampler → result

Controller execution and measurement flow

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
TG[Thread scenario / iteration] --> SC[Simple grouping]
SC --> LC[Loop decision]
LC --> IF[If / While / Switch decision]
IF --> S[Executed sampler sequence]
S --> T[Authorized local target]
S --> TX[Transaction aggregation]
TX --> J[JTL parent/additional transaction labels]
S --> J2[JTL child labels]
T --> SE[Independent server request counts]
TC[Throughput Controller] --> IF
R[Timer / assertion / processor scope] --> S

The thread enters its scenario. Controllers decide which child elements become runnable and how often. Only executed samplers create protocol traffic. A Transaction Controller observes the nested execution and creates a JMeter SampleResult; it does not create another server request. Timers, assertions, processors, and config elements still obey the tree scope around those children.

3. Simple Controller: organization without execution semantics

Simple Controller groups samplers/controllers so a plan can say “Browse phase” or “Checkout phase.” It does not add loops, branches, waits, or samples. Moving identical children into a Simple Controller should not change target request count by itself.

That makes it valuable for controlling scope: a timer or assertion attached to a Simple Controller can intentionally affect only that grouped business phase.

4. Loop Controller multiplies work inside the Thread Group

If Thread Group loop count is 3 and a child Loop Controller count is 2, one child request inside that controller executes 3 × 2 = 6 times per thread. The Loop Controller exposes an index such as ${__jm__SearchLoop__idx}, starting at zero.

Use a nested Loop Controller when one subsection repeats more often than the whole scenario. Use the Thread Group loop for the outer user journey.

5. If Controller: boolean inclusion

If Controller decides whether its children run. The preferred current pattern is to enable Interpret Condition as Variable Expression? and feed a variable containing true/false or a JEXL3/Groovy expression that resolves to true/false.

Example: ${DO_CHECKOUT} where Chapter 11 test data supplied true or false. Avoid JavaScript condition mode in load tests because current JMeter documentation warns of a potentially large performance penalty.

6. While Controller: repeat until the state becomes false

While Controller repeats its children until its condition is the string false (with special blank/LAST semantics). Its condition is evaluated before the child sequence and again after it. That is why non-idempotent functions such as counters should not be placed directly in the condition.

A safer pattern is causal state: a poll sampler extracts POLL_MORE=true/false, and the While condition simply reads ${POLL_MORE}. Add an independent hard duration/iteration guard for failure safety.

7. Switch Controller: select one child

Switch Controller chooses one subordinate element by numeric index or name. Numeric children are zero-based. A non-numeric switch value looks for a child with the same case-sensitive name; if none matches, a child named default is used when present.

Named business branches such as standard and express are often more maintainable than magic numeric indexes because the intent survives reordering.

8. Throughput Controller controls branch execution—not throughput

Throughput Controller has two execution modes:

  • Percent executions: execute the branch for a configured percentage of scenario iterations.
  • Total executions: stop executing the branch after a configured count.

Its Per User setting decides whether the count/percentage is tracked per thread or globally across users. It does not schedule RPS. Chapter 7's Constant/Precise Throughput Timers are rate-oriented components.

9. Transaction Controller changes reporting semantics

A Transaction Controller measures the overall nested business operation and creates a transaction SampleResult. With Generate Parent Sample off, child samples remain normal JTL rows and an additional transaction row is generated after them. With Generate Parent Sample on, the transaction becomes the parent; child samples remain visible in View Results Tree but do not appear as separate CSV JTL rows.

The transaction is successful only if all nested samples are successful. By default its measured time excludes Timer and pre/post-processor processing; the optional include-duration setting changes that interpretation.

10. Transaction rows can make naive sample counting wrong

If a journey sends five HTTP requests and an additional-mode Transaction Controller wraps them, CSV JTL can contain six rows: five child request results plus one transaction result. The target still received only five requests.

Never compute “HTTP request throughput” by counting every JTL row without filtering transaction labels.

11. Controllers also create scope boundaries

A Timer, Assertion, Pre-Processor, or Post-Processor attached above a controller can apply to every sampler under it. A timer placed under a broad Checkout Simple Controller can delay both standard and express branches. A parent-mode Transaction Controller can also complicate assertion scope because assertions attached to it may affect child and parent samples.

Scope review therefore precedes controller refactoring.

12. State to inspect before changing controllers

State Question
Thread/workload Thread count, outer iterations, duration, pacing, achieved load?
Controller state Loop counts, While/If variables, Switch value, Throughput mode/per-user setting?
Variables/data Which per-thread values drive branch decisions?
Protocol/session Does skipping a branch leave required session state unset?
Scope Which timers/assertions/processors/config elements are inherited by child samplers?
Results Which labels are protocol child samples versus transaction reporting samples?
Generator Are condition scripts/large trees/listeners adding CPU/GC cost?
Target How many real requests reached each endpoint independently of JTL transaction rows?
Validity Do configured branch percentages/counts produce the intended business mix?

13. Read-only inspection first

  • Export or screenshot the current controller tree before edits.
  • Multiply Thread Group × nested Loop counts on paper.
  • List every If/While/Switch/Throughput condition/value and expected branch.
  • Mark Transaction Controllers as additional or parent mode.
  • Count predicted child HTTP requests separately from predicted JTL rows.
  • Inspect JTL labels and target/server counts from a tiny baseline run.
  • Record JMeter/Java versions, JTL, matching jmeter.log, and generator CPU/memory.

14. DevOps connection

Controllers encode workload intent in a reviewable form: “search twice,” “checkout only for eligible users,” “poll until ready,” “select one checkout mode,” “show an upsell to a defined population,” and “measure checkout as a business transaction.” That makes CI performance failures attributable to scenario logic rather than opaque request lists.

Knowledge check

Does Simple Controller change sampler execution count?

What is the result of Thread Group loop 3 × Loop Controller loop 2 for one child sampler?

Why should Throughput Controller not be used as an RPS regulator?

Why can parent-mode Transaction Controller make CSV JTL look like only one sample?

What is unsafe about a non-idempotent function inside While condition?

Next lesson

Build the complete local journey

Lesson 2 composes Browse, Search Loop, poll/reset + While, If + Switch checkout, Throughput-controlled upsell, and a Transaction Controller, then compares child counts with transaction reporting.

Official references and version notes

Version and compatibility note

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.

Ethereum / ERC-20
0x716c4Ab160C4B66F31a28AE2448BfF68fc3a2ef0 Send only Ethereum/ERC-20 compatible assets to this address.