skip to content

In JMeter's Batch sending mode, which two properties decide when an engine flushes samples?

level: middleimportance: should knowfreq 43%

answer

  1. Two numbers, one count and one clock
  2. Whichever fires first wins
  3. Both reset together, not just the one
  4. A negative value switches one trigger off

basics

~10 s

num_sample_threshold, default 100 samples, and time_threshold, default 60000 milliseconds. Whichever fires first sends the accumulated batch and resets both counters. Setting either to -1 disables that trigger.

solid answer

~50 s

`BatchSampleSender` in Apache JMeter 6.0.0 accumulates `SampleEvent` objects in a list and checks two thresholds on every sample: `num_sample_threshold` (default `100`) and `time_threshold` (default `60000` ms). Whichever condition is met first sends the whole accumulated list to the controller in a single `processBatch` RMI call and resets both the list and the time deadline. Setting one of them to `-1` switches that trigger off, so the other becomes the only rule — `-1` on both would mean nothing is sent until the run ends. Whatever is still in the list is flushed by `testEnded(host)`, so no samples are lost. `StatisticalSampleSender` reads the same two properties, which is worth remembering because the manual's properties-reference page credits only `key_on_threadname` and `time_threshold` to Statistical. Both values are read from the **controller's** properties by default and shipped to the engines.

code

properties · 14 lines
properties
# controller's jmeter.properties - remote batching
mode=StrippedBatch

# send once 100 samples have piled up ...
num_sample_threshold=100
# ... or once 60 seconds have passed, whichever comes first
time_threshold=60000

# -1 switches one trigger off; here, send strictly on the count
#time_threshold=-1

# true (default): the values above are read here and shipped to the engines
# false: each engine uses its own jmeter.properties instead
sample_sender_client_configured=true

go deeper

for a junior

Recall the two property names and their defaults: 100 samples and 60000 milliseconds. Knowing that a batch goes out on whichever limit is reached first is enough at this level.

for a middle

Explain the reset rule and the -1 guard, and say that the end of the run always flushes. Be clear that these thresholds change delivery granularity, not the total volume of results.

for a senior

Demonstrate that you know which node resolves the values and why the shipped manual disagrees with the code. Tune from an observed symptom rather than raising the count because it sounds cheaper.

for a principal

Decide whether engines get local control of their own batching at all. Centralising the thresholds on the controller keeps runs reproducible; devolving them to engines suits heterogeneous fleets but costs you that guarantee.

## The two properties | Property | Default | Meaning | |---|---|---| | `num_sample_threshold` | `100` | send once this many samples have accumulated | | `time_threshold` | `60000` | send once this many milliseconds have elapsed | Both are plain JMeter properties, so they live in `jmeter.properties`, in a file passed with `-q`, or on the command line as `-J`. Neither is a test-plan element, and neither appears in the GUI. ## How the check runs `BatchSampleSender.sampleOccurred` does the following, inside a lock on the sample store: 1. adds the event to the list; 2. if `num_sample_threshold` is not `-1` and the list size has reached it, marks the batch for sending; 3. if `time_threshold` is not `-1`, reads the clock, initialises the deadline on the first sample, and marks the batch for sending once the deadline has passed; 4. if either check fired, clones the list, clears it, and resets the deadline to *now plus the time threshold*; 5. outside the lock, calls `listener.processBatch(...)` once, with the whole clone. The important detail is step 4: **either trigger resets both.** A busy engine that hits 100 samples in two seconds restarts its 60-second clock from that moment, so the time threshold only ever fires on a genuinely quiet engine. ## Turning one off The `-1` guard is in the code but not in the shipped properties file. It gives you two useful shapes: - `num_sample_threshold=-1` — send strictly on a timer, so batch size follows the achieved rate; - `time_threshold=-1` — send strictly on a count, so batch size is fixed and a slow engine may hold samples for a long time. Setting both to `-1` means nothing leaves the engine until `testEnded(host)` flushes it, which turns the sender into an unbounded in-memory buffer. That is the behaviour the removed `Hold` sender had, and it is not a good idea on a long run. ## The end of the run is always a flush `testEnded(String host)` sends whatever remains and then calls `listener.testEnded(host)`, so a partial batch is never dropped. It does mean the last batch can be much larger than the usual `100` if the count trigger was disabled with `num_sample_threshold=-1`, leaving the clock — or, with both off, nothing — as the only bound, and it means a run that ends abruptly on the engine side can lose whatever was still accumulated. ## Which node's numbers actually apply This is the part that catches people out, and the shipped documentation is misleading about it. Both senders read the thresholds **twice**: into `static final` fields, which are resolved on the engine, and into ordinary instance fields, which are resolved on the controller and travel to the engine inside the serialised sender. `readResolve()` then picks between them using `sample_sender_client_configured`, which defaults to `true`: - **`true` (default)** — the **controller's** `num_sample_threshold` and `time_threshold` are used; the engines' own values are ignored. - **`false`** — each engine's own values are used. `sample_sender_client_configured` is itself an instance field, so it too is read on the controller. To hand threshold control to the engines you set that flag **on the controller**. The manual's remote-testing page still says these properties are configured on the server; the shipped code says otherwise, and the code is what runs. ## Where the effort actually goes Raising `num_sample_threshold` reduces the number of RMI round trips, not the number of samples that cross the wire. It trades engine memory (a longer list held between sends) and a burstier arrival pattern on the controller for less per-call overhead. Lowering it does the reverse. If the problem is the sheer volume of results rather than the call overhead, no threshold value fixes it — that is a `mode` decision, not a threshold one.

  • An engine hits 100 samples after two seconds. When does its 60-second timer next expire?
    Sixty seconds after that send, not sixty seconds after the run started. The count trigger resets the time deadline to now plus `time_threshold`, so on a busy engine the timer effectively never fires — it only matters when traffic is sparse.
  • Do these two properties belong on the controller or on the engines?
    On the controller, unless you set `sample_sender_client_configured=false` there. Both senders read the values into instance fields on the controller, ship them inside the serialised sender, and `readResolve` prefers those client values by default.
  • Does raising num_sample_threshold reduce the load on the controller?
    Only the per-call overhead. The same number of `SampleEvent` objects still arrives; they simply come in fewer, larger `processBatch` calls, which also means a longer list held in engine memory and a burstier arrival pattern. Volume is a `mode` problem, not a threshold one.

A delivery van that leaves when it is full or on the hour, whichever comes first — and once it leaves, both the load count and the clock start again from zero.

saying these in an interview costs you the question

  • Thinks only the count trigger resets after a send
  • Believes the time threshold is in seconds, not milliseconds
  • Assumes a partial batch is discarded when the run ends
  • Says raising the count threshold reduces what reaches the controller
  • Configures the thresholds on the engines and expects them to apply
  • Thinks Statistical mode ignores num_sample_threshold