skip to content

A JMeter run with -Jenv.host=prod.internal -q staging.properties still hits staging. Why?

level: seniorimportance: should knowfreq 27%

answer

  1. Position, not flag type, decides
  2. One pass over the parsed command line
  3. A file merge is just another write
  4. The log lines appear in application order

basics

~10 s

JMeter applies command-line property options in the order they appear. The -q file is read after the -J flag and copies its own env.host over the value the flag had just set.

solid answer

~40 s

`JMeter.initializeProperties` iterates `parser.getArguments()`, and that list preserves the order you typed. `-q`, `-S`, `-D`, `-J`, `-G` and `-L` are all handled in that single pass, so two options touching the same key resolve by **position**, not by flag type. `-q` does a `putAll` from its file into the very same property set `-J` writes with `setProperty`; whichever runs last wins. Moving the `-J` to the right of the `-q` makes the flag win, which is the ordering you want when a file carries an environment's defaults and a flag carries the one value you are overriding for this run. `jmeter.log` settles any doubt: the `Loading additional properties from:` and `Setting JMeter property:` lines appear in the order JMeter applied them. Apache JMeter 6.0.0.

code

bash · 9 lines
bash
# BROKEN: staging.properties is read AFTER the flag, so the file wins
jmeter -n -t checkout.jmx -l run.jtl \
  -Jenv.host=prod.api.internal \
  -q ./config/staging.properties

# FIXED: files first, flags last
jmeter -n -t checkout.jmx -l run.jtl \
  -q ./config/staging.properties \
  -Jenv.host=prod.api.internal

go deeper

for a junior

Remember the practical rule rather than the machinery: put property files first on the command line and single -J overrides last, so the thing you typed to override actually overrides.

for a middle

Explain that JMeter walks the parsed options once, in command-line order, and that -q's putAll and -J's setProperty write into the same map. Position therefore decides, and nothing re-sorts by flag type.

for a senior

Diagnose it from artefacts, not memory: read the interleaved Setting JMeter property and Loading additional properties from lines in jmeter.log. Recognise the danger sign — the run succeeded and produced full results against the wrong system.

for a principal

Make the ordering a team rule rather than a per-job accident, and require the resolved configuration to be evidenced from the run's own log, so an unnoticed override cannot make a whole night's results describe the wrong environment.

The command line is applied left to right, and nothing about a flag's identity gives it priority. That single rule explains the whole surprise. ## One pass, in order After loading the base file, `JMeter.initializeProperties` does this: ```java List<CLOption> clOptions = parser.getArguments(); for (CLOption option : clOptions) { switch (option.getDescriptor().getId()) { case PROPFILE2_OPT -> { ... jmeterProps.putAll(tmp); } // -q case JMETER_PROPERTY -> { ... jmeterProps.setProperty(name, value); } // -J ... } } ``` `CLArgsParser.getArguments()` returns the options in the order the parser added them, which is the order you typed them. `-q`, `-S`, `-D`, `-J`, `-G` and `-L` are all handled in that one pass. So two options that touch the same key resolve by **position**, not by kind. In the failing command, `-Jenv.host=prod.internal` runs first and calls `setProperty`. Then `-q staging.properties` runs and calls `putAll`, and because that file also defines `env.host`, the map entry is overwritten. The plan resolves `env.host` to the staging value and the run goes where you did not intend. ## The fix is positional Put the override to the right of the file it overrides: ``` jmeter -n -t checkout.jmx -l run.jtl \ -q ./config/staging.properties \ -Jenv.host=prod.api.internal ``` Now the file supplies the environment's stable values and the flag lands on top of them. This is the ordering you almost always want, and it is worth making a house rule: **files first, flags last.** It reads the way people expect it to read, and it means a one-off override on the command line always wins. ## Confirming it from the log You do not have to reason about this from the shell history. Every step is recorded, in the order it happened: ``` INFO o.a.j.JMeter: Setting JMeter property: env.host=prod.api.internal INFO o.a.j.JMeter: Loading additional properties from: ./config/staging.properties ``` Reading those two lines top to bottom tells you which write happened last. If the `Loading additional properties from:` line sits below a `Setting JMeter property:` line for a key that file also defines, you have found your answer. ## Related traps in the same loop - **`-q` is repeatable, so several files can shadow each other**, again by position. A common file followed by an environment file behaves as you would hope; the reverse does not. - **`-D` and `-J` cannot lose an ordering race to each other.** They write to different stores, and a `-J` entry simply shadows a system property of the same name, so the conflict is always within one store. - **An empty value on a later `-J` deletes an earlier definition.** `-Jenv.host=staging.internal ... -Jenv.host=` leaves the property unset, logging `Removing JMeter property: env.host`. - **`-p` is not part of this loop at all.** It is handled before the loop starts, so its position on the command line is irrelevant; anything the loop applies necessarily comes after it. ## Why this bites the staging-to-production case specifically The pattern that produces it is exactly the one this leaf exists for: a plan parameterised from outside, a checked-in file per environment, and a pipeline that appends "just one override" to a command line it inherited. The override is appended in the wrong place, the run looks healthy, the results file is full, and every sample went to the wrong host. Nothing fails; the numbers are simply about the wrong system. Behaviour described is Apache JMeter 6.0.0.

  • Does -p obey the same positional rule?
    No. -p is handled before the loop over the command line even begins, because it decides which base property file is loaded in the first place. Its position among the other flags is therefore irrelevant, and anything the loop applies necessarily lands on top of it.
  • Could a later -D have caused the same symptom?
    No. They write to different property stores, and JMeter's set holds the system properties only as its defaults, so a -J entry of that name shadows a -D however late the -D lands. An ordering conflict is always between options writing to the same store — -J against -q, or -q against a later -q.
  • How would you prove from the artefacts which value the run used?
    Read jmeter.log top to bottom. Each -J writes `Setting JMeter property: env.host=...` and each -q writes `Loading additional properties from: <path>`, both at INFO. If the file's line sits below the flag's line for a key that file also defines, the file won.

saying these in an interview costs you the question

  • Says a -J flag always beats a property file
  • Thinks flag type establishes a precedence order
  • Believes JMeter rejects a key defined twice
  • Assumes -q is processed before all -J flags
  • Blames the plan rather than the command line order