Checkpoint Lab — Controllers: Simple, Loop, Transaction, If, While, Switch, and Throughput
The checkpoint turns controller structure into measurable workload evidence. You will run a deterministic browse/search/poll/checkout journey, verify exact child and transaction counts, then introduce one loop/scope defect whose symptom must be explained from JTL labels and independent target counts.
Learning objectives
- Build a complete controller tree with explicit branch/loop semantics.
- Predict target request count and CSV JTL row count before execution.
- Verify branch/loop behavior independently using server events.
- Compare additional transaction mode with parent mode.
- Introduce and diagnose one bounded loop/scope defect.
- Produce a controller evidence packet suitable for CI/baseline review.
1. Assumptions and hard ceilings
| Item | Checkpoint baseline |
|---|---|
| JMeter | Apache JMeter 5.6.3. |
| Java | Java 17 JDK lab baseline; JMeter 5.6.3 requires Java 8+. |
| Plugins | None. |
| Target | http://127.0.0.1:8000 only. |
| Threads | 1 for valid checkpoint; max 2 in optional comparison. |
| Outer iterations | 2 valid; max 10 only for isolated Throughput percentage demo. |
| Search Loop | 2 per outer iteration. |
| While Poll |
2 per outer iteration via response-derived
POLL_MORE.
|
| Checkout | If=true, Switch=standard. |
| Upsell | Throughput Controller Total Executions=1, Per User off. |
| Failure reproduction | 1 thread, duration ≤4 s, 200 ms poll timer. |
2. Start clean and archive the tree
mkdir -p results/checkpoint results/parent results/broken results/repaired
python fixtures/controller_fixture.py --log results/checkpoint/server-events.jsonl
curl --fail --silent http://127.0.0.1:8000/health
curl --fail --silent http://127.0.0.1:8000/reset
Save/export the exact controller tree and resolved variables before running. The evidence packet should show every loop count, branch condition, Switch value, Throughput mode/count, and Transaction option.
3. Valid checkpoint tree
Thread Group — 1 thread × 2 iterations
└── Transaction Controller — Journey Transaction
Parent: OFF
Include timers/pre-post: OFF
├── Simple — Browse Phase
│ └── Browse
├── Loop — SearchLoop ×2
│ └── Search
├── Poll Reset → extractor POLL_MORE=true
├── While — WaitForReady (${POLL_MORE})
│ └── Poll → extractor updates POLL_MORE (true then false)
├── If — Do Checkout (${DO_CHECKOUT}=true)
│ └── Switch (${CHECKOUT_MODE}=standard)
│ ├── standard → Checkout Standard
│ ├── express → Checkout Express
│ └── default → Checkout Default
└── Throughput Controller — Upsell
Total executions=1; Per User=OFF
└── Upsell
4. Required predictions before execution
Prediction A — target requests:
Per outer iteration:
Browse 1
Search 2
Poll Reset 1
Poll 2
Checkout 1
= 7
Two outer iterations = 14
Upsell total-executions branch = 1
Expected target requests = 15
Prediction B — JTL rows, Transaction parent OFF:
15 child protocol rows + 2 transaction rows = 17 CSV rows
Prediction C — Transaction parent ON: target still receives 15 requests, but CSV JTL should contain two parent transaction rows rather than 15 separate child rows.
5. Run the valid checkpoint
jmeter -n -t plans/checkpoint-controllers.jmx -l results/checkpoint/results.jtl -j results/checkpoint/jmeter.log -Jjmeter.save.saveservice.print_field_names=true -Jjmeter.save.saveservice.thread_counts=true
python tools/analyze_jtl_labels.py results/checkpoint/results.jtl
python tools/analyze_server_events.py results/checkpoint/server-events.jsonl
curl --fail --silent http://127.0.0.1:8000/stats
6. Verify valid-run counts
Expected target endpoint counts:
| Endpoint | Expected count |
|---|---|
/browse |
2 |
/search |
4 |
/poll-reset |
2 |
/poll |
4 |
/checkout |
2 |
/upsell |
1 |
| Total | 15 |
Expected additional transaction label count:
Journey Transaction = 2. No transaction label should
appear in the server event log.
7. Verify parent-sample reporting separately
Reset fixture and rerun the same journey with Generate Parent
Sample=ON into results/parent/. Verify:
- target endpoint counts remain 15;
- CSV JTL contains two Journey Transaction parent rows;
- child HTTP results are sub-samples, not separate CSV rows;
- transaction success becomes false if any nested child fails.
8. Deliberate defect — Poll extractor scoped to Poll Reset only
Create a broken copy where the JSON extractor that updates
POLL_MORE after Poll is mistakenly
moved under Poll Reset. Now Poll responses no
longer set false, so While would continue.
Contain the reproduction:
- 1 thread;
- Thread Group duration 4 seconds;
- 200 ms Constant Timer under Poll;
- abort if target Poll count exceeds 20.
Run into results/broken/. Expected: Poll count
materially exceeds the valid count of two per journey while other
child counts may not be reached because the thread remains inside
While.
9. Diagnose the defect from evidence
Required interpretation:
- JTL shows repeated Poll label.
-
Server event log independently confirms repeated
/polltarget requests. -
POLL_MOREnever receives the Poll response's false value because extractor scope is wrong. - The server is responding normally; the generator's controller state is causing excess traffic.
- Thread Group duration prevents an unbounded safety incident but does not repair the business logic.
10. Repair the smallest layer
Restore the JSON extractor as a Post-Processor of Poll with default
false. Keep the same Thread Group/While/fixture
settings and rerun into results/repaired/.
Expected: Poll returns to exactly two executions per journey, the later Checkout/Upsell path executes, and request/JTL counts return to the valid predictions.
11. Optional second defect — wrong Loop Controller count
Change SearchLoop from 2 to 3 for one bounded run. Predict the delta before running:
2 outer iterations × +1 extra Search = +2 target requests and +2 Search JTL rows
This defect is useful for proving multiplicative loop math without involving server errors.
12. Generator/validity observation
Record Task Manager/top CPU/memory during valid and
broken runs. The valid lab is tiny and should leave large injector
headroom. If the generator is unexpectedly saturated, do not use the
result to infer target capacity.
13. Required evidence packet
| Artifact | Required content |
|---|---|
| Controller tree | Simple/Loop/Transaction/If/While/Switch/Throughput structure and options. |
| Resolved branch state | DO_CHECKOUT, CHECKOUT_MODE, POLL_MORE lifecycle. |
| Prediction sheet | Target endpoint counts and expected CSV JTL rows. |
Valid JTL + jmeter.log |
Child and Journey Transaction labels. |
| Server event log/stats | Independent real-request counts by endpoint. |
| Parent-mode run | Two parent transaction CSV rows + unchanged target request counts. |
| Broken run | Repeated Poll labels/events under hard duration guard. |
| Repair run | Restored two Polls/journey and downstream branch execution. |
| Generator observation | CPU/memory headroom and validity note. |
14. Verification checklist
- Target is exactly loopback:8000.
- Valid plan sends 15 target requests and creates 17 CSV rows in additional transaction mode.
- Parent mode changes CSV schema, not target traffic.
- Search loop count and While poll count match predictions.
- Only standard checkout branch executes in the valid plan.
- Upsell executes once total; it is not interpreted as an RPS setting.
- Broken While reproduction is hard-bounded and preserved before repair.
- Timers/assertions/processors are reviewed for inherited controller scope.
15. Validity statement
16. Cleanup
- Stop the local fixture.
- Preserve valid, parent, broken, and repaired evidence until review is complete.
- Remove debug listeners and synthetic variable dumps from later load plans.
- Restore all failure-reproduction hard bounds/configuration to the valid baseline.
- No production target, credential, certificate, RMI engine, paid service, container, database, or system-wide JVM/OS setting was changed.
17. What Chapter 12 adds to the operating model
The performance-testing operating model now has a control-flow contract: outer iteration count, nested loop multiplication, branch conditions and populations, Switch mapping, While termination state/safety bound, Transaction reporting mode, branch-execution controller semantics, scope inheritance, predicted target requests, and expected JTL labels are reviewed before execution.
Chapter 13 moves naturally to Recording Web Traffic, HTTP(S) Test Script Recorder, and Certificate Setup. Recording can create a raw request sequence, but the controller skills from this chapter are what turn that recording into a maintainable business journey instead of replaying every captured request flatly.
Knowledge check
What two numbers must be predicted separately when a Transaction Controller wraps the journey?
Real target request count and JTL result-row count, because transaction samples are reporting artifacts.
Why does parent mode not reduce actual target traffic?
It changes result aggregation/storage, not which nested samplers execute.
What caused the broken Poll loop?
The extractor updating POLL_MORE was scoped to Poll Reset instead of Poll, so the While condition never received false.
Why is Thread Group duration not considered the repair?
It only bounds the failure safely; the causal defect is incorrect state/extractor scope.
How does Chapter 12 prepare for recorded traffic in Chapter 13?
It provides the grouping, branching, repetition, transaction, and scope semantics needed to restructure a flat recording into a valid user journey.
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.