Why do the per-label Transactions/s values in a JMeter dashboard not sum to the Total row?
answer
- Each row divides by its own span
- First start to last end, per label
- Windows overlap, so rates cannot add
- Total drops Transaction Controller samples
basics
~10 sJMeter divides each row's sample count by that label's own span, from its first sample's start to its last sample's end. Rows therefore use different, overlapping denominators, so their rates are not additive.
solid answer
~50 s`Transactions/s` for a row is `count / (lastEndTime - firstStartTime) * 1000`, where both times are that **label's own** first and last sample. Each row is therefore a rate over its own active window, and those windows overlap, differ in length, and are all shorter than or equal to the run. Adding them compares figures with different denominators. The `Total` row is computed the same way over the run-wide span — and it also **drops Transaction Controller samples**, which have rows of their own, so it is not even summing the same sample set. Two practical consequences: a label that only ran for the first minute of a ten-minute run shows a high `Transactions/s`, and a label executed once shows a rate derived from a near-zero span. Read the row as *the pace while that label was active*, and use `Total` for the run.
code
json · 8 lines{
"JR-KO" : { "transaction" : "JR-KO", "sampleCount" : 3,
"throughput" : 0.07922465471254654 },
"JR-OK" : { "transaction" : "JR-OK", "sampleCount" : 252,
"throughput" : 4.187089806430174 },
"Total" : { "transaction" : "Total", "sampleCount" : 255,
"throughput" : 4.2369361136495804 }
}go deeper
Know that Transactions/s is a rate per row and that the run-wide figure is the one on the Total row, not a sum of the rows above it.
Explain the formula: the row's sample count divided by the span from its first sample's start to its last sample's end, expressed per second.
Spot a suspiciously high rate on a low-count row, and know the Total row excludes Transaction Controller samples so it is not even aggregating the same set.
Decide which single throughput figure your teams are allowed to quote from a report, and make sure it is the one computed over a span everyone agrees on.
`Transactions/s` is the column people quote most often and misread most reliably, because it looks like a run-wide rate and is not one. ## How JMeter computes it `StatisticsSummaryData` keeps two clocks per row alongside the counters: - `firstTime`, shrunk to the minimum start time of any sample in the row - `endTime`, grown to the maximum end time of any sample in the row and then: ``` throughput = (total / (double) (endTime - firstTime)) * 1000.0 ``` Both clocks are **per row**. A label that started late or finished early has a shorter span, and its count is divided by that shorter span. ## Why the rows do not add up 1. **Different denominators.** Row A may span 600 seconds and row B 60 seconds. Their rates are per-second figures over different windows; adding them is arithmetic on incompatible units. 2. **Overlapping windows.** Labels in the same plan run concurrently, so their spans overlap. Even if the denominators happened to match, the sum would double-count the wall clock. 3. **A different sample set in Total.** The `Total` row is built with Transaction Controller results excluded, so that a parent and its children are not both counted. Every Transaction Controller still has its own row in the table. Sum the visible rows and you sum parents *and* children. 4. **Rounding.** Every rate column is rendered to two decimals, so even an arithmetically valid sum would not reconcile exactly against the display. ## A worked example From a real report's `statistics.json`, three entries: | transaction | sampleCount | throughput | |---|---|---| | `JR-KO` | 3 | 0.0792 | | `JR-OK` | 252 | 4.1871 | | `Total` | 255 | 4.2369 | The counts add exactly — 3 + 252 = 255. The rates do not: 0.0792 + 4.1871 = 4.2663, against a `Total` of 4.2369. The three failing samples happened to be spread across a slightly narrower window than the run, so their own rate is computed over a shorter span and the sum overshoots. ## Reading the column without being misled - Read a row's `Transactions/s` as **the pace at which that label ran while it was running**, not its share of the run. - Take the run-wide rate from the `Total` row only. - Be suspicious of any row with a very small `#Samples`: a handful of samples clustered together produces a large rate over a tiny span, and a label sampled once has an almost meaningless denominator. - Cross-check against the Transactions Per Second graph on the Throughput page when you need the rate against the clock; that graph is bucketed at the report's granularity, one minute by default, and shows when each label was actually active. - The same value appears as `throughput` in `statistics.json`, at full precision rather than two decimals, which is the better source if you are comparing programmatically. - The table arrives sorted by `Label`, not by rate, so the busiest row is not the top one; click the `Transactions/s` heading before you read anything off the order — the `Total` row sits in its own unsortable body above the rest and stays there whichever heading you click. ## The related network columns `Received` and `Sent` in the Network group are computed with the identical span, as bytes per second divided by 1024. They inherit exactly the same caveat: they are per-row rates over per-row windows and they do not sum to the `Total` row either.
- Why can a rarely-executed label show a higher Transactions/s than a hot one in the same JMeter report?Because its samples were clustered into a short span. Ten samples inside two seconds is five per second, while a thousand samples spread over ten minutes is under two per second. The column is a rate over the label's own active window, so density beats volume.
- Which JMeter dashboard surface shows throughput against the clock rather than as one figure?The Transactions Per Second and Total Transactions Per Second graphs on the Throughput page, plus Hits Per Second. All are bucketed at the report's granularity, 60000 ms by default, so they show when each label was active instead of collapsing it to a single rate.
saying these in an interview costs you the question
- Adds the per-label rates to get the run rate
- Assumes every row divides by the run duration
- Treats a high rate on a tiny row as real load
- Thinks the Total row includes Transaction Controller samples
- Blames a mismatch on rounding alone