Chapter 04Lesson 04~135 minutes

Thread Groups, Virtual Users, Ramp-Up, Loops, and Workload Modeling: Diagnostics, Failure Modes, and Production Practices

When throughput or latency looks wrong, the load model is one of the first layers to inspect. Preserve the run, reconstruct the configured schedule, compare it with achieved load, and only then decide whether the injector, target, or model is responsible.

DiagnosticsInfinite loopSchedulerStartup spikeValidity

Learning objectives

  • Diagnose a thread-count-as-RPS assumption from JMX and JTL evidence.
  • Recognize a ramp that is too aggressive for the injector or target.
  • Catch unbounded/infinite loops before execution and repair them safely.
  • Explain why scheduler duration can be exceeded by an in-flight slow sampler.
  • Separate unlabeled workload classes and controller/sample populations.
  • Prevent experimental Open Model syntax from becoming an undocumented stable dependency.

1. Preserve evidence and keep diagnosis local

Diagnostic reruns remain at http://127.0.0.1:8000 with the Chapter 04 ceilings. Never increase threads to “make the problem obvious.” Preserve JMX, JTL, jmeter.log, run manifest, fixture stats, and generator observations before changing the schedule.

2. Diagnostic sequence

Workload-model diagnostic sequence

Use this diagram as a workload-state model, not as a promise that configured values become achieved values. The prose below explains where feedback from response time, timers, generator limits, and target limits enters.

flowchart TD
E[Preserve JMX + JTL + jmeter.log + run manifest] --> V[Confirm JMeter/Java/tool versions]
V --> W[Reconstruct threads/ramp/loops/duration/timers]
W --> A[Compare configured vs achieved throughput + active threads]
A --> P[Inspect protocol/session/data state]
P --> G[Inspect generator CPU/GC/sockets/network/result cost]
G --> S[Inspect target latency/queues/pools/errors]
S --> X[CI/container/distributed layer if relevant]
X --> F[Smallest schedule/config correction]
F --> R[New bounded rerun]

3. Failure mode: thread count used as an RPS target

A plan is configured with 10 threads because someone wants “10 RPS.” With 100 ms response time and little think time, those users may complete many more than ten requests per second. If response time later grows to one second, the same 10 users may complete far fewer. Nothing in classic Thread Group semantics makes 10 threads equal 10 RPS.

Repair the requirement: decide whether you need 10 concurrent users or a 10-per-second rate. Then choose a matching schedule and verify the achieved result.

4. Failure mode: ramping faster than the injector can sustain

A zero-second ramp intentionally starts all configured threads immediately. At modest scale that can be a useful spike experiment; at large scale it can make the injector spend its first seconds creating threads, opening connections, allocating objects, writing results, and competing for CPU.

If generator CPU/GC spikes, errors appear locally, and achieved workload falls short, do not conclude that the target has reached capacity. Repeat with a workload whose ramp matches the performance question and with confirmed injector headroom.

5. Intentionally broken configuration: infinite loop without lifetime

Do not execute this broken plan. Inspect it in the GUI before a run:

Thread Group
  Threads: 3
  Ramp-up: 1 second
  Loop Count: Infinite / Forever
  Specify Thread lifetime: OFF
  Timer: 0 ms
  Sampler: GET http://127.0.0.1:8000/work?delay_ms=100

There is no deterministic natural stop. Even though the target is local, this violates the chapter's bounded-load contract. The least destructive repair is to choose either a finite loop count or an explicit scheduler lifetime (and appropriate timer), then save a new JMX before execution. Do not rely on “I will remember to press Stop.”

6. Controlled broken expectation: duration does not kill an in-flight sampler

Create a one-thread local plan with:

  • Loop Count: 100
  • Duration: 2 seconds
  • Sampler: /work?delay_ms=500
  • Constant Timer: 400 ms
  • Response timeout: 2000 ms

The expectation “JMeter must exit at exactly 2.000 seconds” is intentionally wrong. The duration is checked between samples. A sample that begins before the boundary may finish afterward. Preserve JTL timestamps and jmeter.log; the correction is conceptual unless your business requirement also demands a different sampler timeout/stop policy.

7. Failure mode: mixed workload classes without stable labels

Two Thread Groups both use a sampler label Request, but one is interactive and one is a background job. The dashboard/JTL aggregate now mixes different schedules and potentially different SLOs. A p95 over the combined label is difficult to interpret.

Rename by stable class intent—for example Interactive — Work and Background — Work—and preserve per-class settings in the run manifest. The network endpoint can be the same while the workload populations remain separate.

8. Failure mode: experimental Open Model taught as immutable syntax

A team copies an Open Model schedule into a long-lived governance document and removes the JMeter version because “it is built in.” That hides the current documentation warning: the component is experimental and may change in future releases.

Repair the baseline by pinning JMeter 5.6.3, preserving schedule text and seed, linking the current component documentation, and defining a stable classic/throughput-timer fallback. Re-validate after every JMeter upgrade.

9. Queueing and saturation: separate causes

Symptom Possible workload/generator cause Possible target cause What to correlate
Throughput below target Too few threads for rate timer; generator CPU/GC Target too slow or throttled Configured schedule + JTL rate + generator/SUT telemetry.
Active threads rise Open arrivals continue while scenarios slow Target queue/service time increases Arrival schedule + active threads + target queues.
Latency rises at startup Synchronized ramp, connection setup, injector pressure Cold caches/JIT/target startup Generator + target telemetry + thread-start timeline.
Run exceeds duration In-flight sample completes after scheduler boundary Slow target response can extend it JTL timestamps + response timeout + target logs.
Errors rise Injector sockets/network/result overhead Target overload/rate limit jmeter.log + JTL errors + both-side resources.

10. Result collection can become part of the workload failure

Heavy GUI listeners and large response retention consume injector resources. Under load, use CLI JTL + jmeter.log and only the fields you need. Do not disable every listener/result field blindly; preserve the evidence necessary to diagnose sample correctness, active threads, timing, and failures.

11. Shortcuts to reject

  • Do not add arbitrary sleeps/retries to force a target RPS without a workload model.
  • Do not blindly increase heap because startup is slow.
  • Do not increase thread count until a throughput timer “hits the number” without checking injector/target constraints.
  • Do not disable TLS/RMI/security controls or use public targets to simplify load generation.
  • Do not delete the failed run after a corrected rerun.
  • Do not treat a JMeter process exit code as proof that a performance threshold passed.

Knowledge check

A 10-thread test achieves 35 samples/second. Is that evidence JMeter ignored the thread count?

What is the safe repair for Infinite Loop Count with no lifetime bound?

Why can a two-second duration test complete after two seconds?

Why are duplicate sampler labels across workload classes a validity problem?

What must you do before blaming the target when a rate-oriented profile misses its configured throughput?

Next lesson

Checkpoint: concurrency profile versus rate-oriented profile

Lesson 5 builds two safe profiles for the same synthetic service, predicts their different control variables, runs them in CLI mode, and explains why configured users and configured rate targets produce different active-thread and throughput evidence.

Official references and version notes

  • Component Reference — current classic Thread Group, Open Model Thread Group, Precise Throughput Timer, and timer semantics.
  • Elements of a Test Plan — sampler ordering, Thread Groups, timer purpose/scope, and listener guidance.
  • Properties Reference — CSV result-save settings including thread-count fields and timestamp behavior.
  • Getting Started — GUI authoring versus CLI load execution and command-line behavior.
  • Best Practices — generator/listener practices for load execution.
  • Apache JMeter downloads — current production release and release-specific Java requirement.
Version and compatibility note

Version-sensitive statements were rechecked against current Apache JMeter primary documentation on 2026-09-04. The production baseline remains Apache JMeter 5.6.3; the release requires Java 8+, while mandatory labs use a Java 17 JDK. The classic Thread Group controls user/thread count, ramp-up, loops, and optional lifetime; with its scheduler, JMeter stops when loops finish or duration/end-time is reached, whichever occurs first, but the check happens between samples and an in-flight sampler waiting for a response is not forcibly ended by that scheduler boundary. The current Open Model Thread Group is explicitly marked experimental and may change; its schedule is evaluated at test start and its schedule end terminates/intercepts active scenario threads unless a tail pause(...) leaves time to finish. The stable mandatory rate-oriented path in this chapter uses the classic Thread Group plus the built-in Precise Throughput Timer. That timer does not create threads and cannot guarantee achieved throughput when too few threads, generator limits, target limits, or other delays prevent the schedule from being served.

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.