Chapter 20Lesson 04~205 minutes

Functions, Custom Properties, Reusable Fragments, and Modular Plans: Diagnostics, Failure Modes, and Production Practices

Modularity failures often happen before the target receives any traffic. A plan can fail because the Include file cannot be found, a Module reference resolves to the wrong duplicate controller, or a missing property silently defaults to one. The correct diagnostic response is to preserve path/property/tree evidence and find the failing layer—not to add retries, threads, heap, or server-side changes.

Path failureHidden defaultsDuplicate fragmentsGlobal propsReproducibility

Learning objectives

  • Diagnose an Include path failure from jmeter.log and command manifest.
  • Recognize hidden __P defaults that alter configured load.
  • Repair duplicate fragment/controller naming.
  • Reject global mutable properties for per-user state.
  • Prevent redundant/circular module dependencies and non-replayable random inputs.
  • Distinguish path/config/generator failures from target latency/saturation.

1. Preserve first-failure evidence

All runnable diagnosis stays on 127.0.0.1:8020, ≤2 threads ×3 work loops. Preserve failed JMX, fragment/property files, cwd/project root/include prefix, command manifest, JTL if created, jmeter.log, generator state and fixture event/stats evidence before repair. Never redirect a broken plan to production “to see if it works there.”

2. Diagnostic sequence

Modular-plan diagnostic sequence

Modularity changes where configuration is defined, not the fundamental JMeter execution model. Properties/functions resolve configuration, controllers assemble reusable elements, and the resulting samplers still execute inside ordinary JMeter threads against the same authorized target.

flowchart TD
E[Preserve JTL / jmeter.log / command manifest / target events] --> V[Confirm JMeter + Java + tool versions]
V --> C[Confirm exact JMX / fragments / properties / CLI + authorized target]
C --> R[Resolve cwd / user.dir / project root / include prefix / filenames]
R --> S[Validate runtime tree scope + unique module names + resolved vars/properties]
S --> P[Inspect per-thread session/data state]
P --> G[Inspect generator JVM / OS / filesystem / network]
G --> T[Inspect SUT telemetry / received event counts]
T --> X[Inspect distributed / CI / container checkout/path state if relevant]
X --> F[Least destructive correction]
F --> M[Small controlled rerun and compare]

3. Intentionally broken example: direct Include run without prefix

main-include.jmx contains:

Include Controller — INC.session.bootstrap.v1
Filename: session-bootstrap.jmx

The fragment actually lives in sibling directory fragments/, not plans/. From an unrelated working directory, deliberately bypass the launcher and omit includecontroller.prefix:

Set-Location "$env:TEMP"

& "$env:JMETER_HOME\bin\jmeter.bat" `
  -n `
  -t "F:\Labs\p20-modular-lab\plans\main-include.jmx" `
  -q "F:\Labs\p20-modular-lab\config\local.properties" `
  -Jrun.id=p20-broken-path `
  -Jvariant=include-broken `
  -l "F:\Labs\p20-modular-lab\results\broken\results.jtl" `
  -j "F:\Labs\p20-modular-lab\results\broken\jmeter.log"

Expected: JMeter cannot resolve the intended external fragment via prefix+Filename and fallback to the JMX launch directory also does not find session-bootstrap.jmx. Preserve the log. The fixture should see no valid session/work sequence for that failed run.

Repair: run through tools/run-local.ps1, which supplies the absolute -Jincludecontroller.prefix=.../fragments/. Keep JMX filename unchanged and rerun the smallest 1-thread profile first.

4. Failure mode: hidden __P default

Broken operational field:

Threads = ${__P(threads)}

If threads is absent, current JMeter returns 1. The run can look successful while generating the wrong load. Repair with an explicit safe default plus resolved-property preflight, or fail the launcher when the required key is missing.

5. Failure mode: function-heavy unreadable fields

Anti-pattern:

${__P(run.id,${__time(YMDHMS)})}-${__threadNum}-${__Random(1,9999)}-${__UUID()}

This hides fallback-to-current-time, thread identity, two kinds of randomness and replay semantics inside one field. Split named values into clear properties/variables/functions, and use deterministic data when replay matters.

6. Failure mode: duplicate fragment/controller names

Module Controller resolves fragments by controller/parent names. Two generic Simple Controller or two identical FRAG.session.bootstrap.v1 paths can make the module reference ambiguous/wrong after reload.

Repair: give each reusable controller a unique hierarchical name and reopen/reselect the Module Controller target. Also give multiple Include Controllers that include the same file distinct controller names, per current JMeter guidance.

7. Failure mode: global mutable property for per-user session

Broken:

${__setProperty(SESSION_ID,${SESSION_ID})}
...
/work?session_id=${__P(SESSION_ID)}

Properties are global to the JMeter process. Thread 2 can overwrite Thread 1's session before Thread 1 sends work. In distributed testing, each engine also has a different “global” property universe.

Repair: keep SESSION_ID in the extractor-created thread-local variable and reference ${SESSION_ID} directly.

8. Failure mode: circular/redundant module structure

An external fragment includes another fragment that eventually points back to the first, or a main plan includes the same setup at two levels. Even where parsing/loading prevents a literal infinite cycle, the design is invalid because runtime ownership and operation counts are no longer obvious.

Maintain an acyclic dependency graph with one owner for each reusable behavior. Flatten redundant wrapper fragments that add no contract.

9. Failure mode: random functions without replay strategy

A failing run used __Random for business data but did not record generated values or a seedable mechanism. The next run cannot reproduce the same input.

Repair: use seeded Random Variable where appropriate, generated/committed deterministic fixtures, or persist the generated values in result/sample variables if exact replay is required. Use random functions only when randomness itself is part of the workload model.

10. Failure mode: “works in GUI” cwd assumptions

GUI launch from the project root can hide path assumptions. A scheduler, CI runner, IDE or shell may start from another directory. Always test a clean-shell invocation from a different cwd using the project launcher before declaring the module portable.

11. Failure mode: putting top-level configuration in included JMX

Cookie Manager or User Defined Variables inside the external included file are not guaranteed to work as expected. Keep shared configuration at the top-level plan and let the fragment accept those already-resolved settings.

12. Causal symptom table

Symptom Likely modular/generator cause Target cause to distinguish Evidence
No target traffic; include error filename/prefix/JMX launch path target outage jmeter.log + command manifest + target zero events.
Run unexpectedly uses 1 thread missing __P property/default server capacity change resolved properties + JTL thread count.
Wrong session appears across users global property/shared state server session corruption vars/props config + target session/thread events.
Different CI behavior checkout/cwd/module/property file missing target regression CI cwd/files/manifest + target event equality.
Throughput lower after modularization extra duplicated runtime invocation/path/log overhead target slowdown JTL sample counts + runtime tree + target service time + generator CPU.
Cannot reproduce failed data unseeded/unrecorded randomness intermittent target bug input evidence/seed/manifest.

13. Security-sensitive boundaries

Property files, launchers and manifests can accidentally capture credentials/environment variables. Include files can execute arbitrary samplers/processes if sourced from an untrusted repository. Treat JMX/fragments/scripts as executable test code. Keep real secrets outside versioned files/logs, validate module provenance, and use only disposable authorized targets.

14. Troubleshooting shortcuts to reject

  • Do not add blanket retries or arbitrary sleeps around include/module errors.
  • Do not increase heap or threads to “push through” path/property failures.
  • Do not disable listeners wholesale without evidence.
  • Do not mutate process-global properties to synchronize virtual users.
  • Do not disable TLS/RMI verification.
  • Do not switch to a production target when a local module fails.
  • Do not grow a recursive module graph to avoid naming a clear boundary.
  • Do not delete failed JTL/jmeter.log/manifest evidence.

Knowledge check

What evidence proves an Include failure happened before the SUT?

Why can __P(threads) create a false-green run?

Why can't properties safely hold per-user SESSION_ID?

How should a random-data failure become reproducible?

What is the least-destructive repair for the broken Include example?

Next lesson

Checkpoint: prove modularity from clean shells

Lesson 5 performs the before/after refactor, predicts runtime/path changes, runs from two working directories, preserves a broken Include log, repairs it, and assembles the production modular-plan contract.

Official references and version notes

Version and compatibility note

Version-sensitive statements were rechecked against current Apache JMeter primary documentation on 2026-09-05. The course baseline remains Apache JMeter 5.6.3 with a Java 17 JDK; JMeter 5.6.3 requires Java 8+. A Module Controller substitutes an already loaded controller/fragment into the active runtime path; fragments referenced by Module Controller need unique names because JMeter uses the controller/parent name path to find them after reload. Include Controller is for external JMX/Test Fragment content. Its Filename field does not support variables/functions. The includecontroller.prefix property can prefix the filename; if prefix+filename cannot be found, JMeter attempts the filename relative to the JMX launch directory. Top-level Cookie Manager/User Defined Variables belong in the main test plan rather than an included JMX because included copies are not guaranteed to work as expected. __P(name,default) is intended for command-line properties; without an explicit default it returns 1. __threadNum is local to its Thread Group and should not be placed in Configuration Elements such as User Defined Variables because those are processed by a separate thread. For reproducible random numeric data, the Random Variable Config Element exposes a seed/per-thread option; the simple __Random function does not expose an equivalent seed parameter.

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.