Fix aggregate crashing on operations that report null metrics - #1096
Conversation
PR Reviewer Guide 🔍(Review updated until commit aa93387)Here are some key observations to aid the review process:
|
PR Code Suggestions ✨Explore these optional code suggestions:
|
OVI3D0
left a comment
There was a problem hiding this comment.
@serhiy-bzhezytskyy tested and verified this works, LGTM, but looks like there is just a conflict on the test file if you can do a quick rebase
An operation that produced no valid samples reports its metric fields as null.
The geonames workload does this for `optimize`, which comes back with
error_rate 1.0 and a fully null throughput. Aggregating any test run that
contains such an operation fails:
[ERROR] Cannot aggregate. '<' not supported between instances of
'NoneType' and 'NoneType'.
calculate_weighted_average reached min()/max() with None values, and
`value.get(metric_field, 0)` did not help because the key is present with a
null value rather than absent.
Null values are now left out of the min, the max, and the weighted mean
instead of being coerced to 0, and the result is None when no test run
contributed a value, so an unmeasured metric stays distinct from a measured
zero. calculate_rsd has the same problem on a second, independent code path
and returns "NA" when nothing was measured.
The weighted-mean arithmetic moves into a helper because the divisor now
depends on which runs contributed, so it can no longer be a single total
computed up front.
Signed-off-by: Serhiy Bzhezytskyy <me@serhiy-bzhezytskyy.com>
e402752 to
aa93387
Compare
|
Persistent review updated to latest commit aa93387 |
…RSD) Found via live e2e: 'aggregate' of two normal index-only runs crashed with ValueError 'Cannot calculate RSD ... empty list of values'. Root cause was a collision between two prior changes: - the robustness fix pre-filtered None mean values at the call site, and - the opensearch-project#1096 merge made calculate_rsd tolerate None *inside* a list but still raise on a genuinely empty list. Together, an all-None metric (e.g. index-only throughput.mean) produced an empty list that hit the raise. Fix: - build_aggregated_results_dict passes raw v.get('mean') values (still avoids the original KeyError on a missing 'mean' key) and lets calculate_rsd handle None/empty. - calculate_rsd returns 'NA' for an empty/all-None list instead of raising, so one unmeasured metric no longer aborts the whole aggregation. Adds a regression test for empty/all-None/single-value RSD. Signed-off-by: Michael Oviedo <mikeovi@amazon.com>
…RSD) Found via live e2e: 'aggregate' of two normal index-only runs crashed with ValueError 'Cannot calculate RSD ... empty list of values'. Root cause was a collision between two prior changes: - the robustness fix pre-filtered None mean values at the call site, and - the opensearch-project#1096 merge made calculate_rsd tolerate None *inside* a list but still raise on a genuinely empty list. Together, an all-None metric (e.g. index-only throughput.mean) produced an empty list that hit the raise. Fix: - build_aggregated_results_dict passes raw v.get('mean') values (still avoids the original KeyError on a missing 'mean' key) and lets calculate_rsd handle None/empty. - calculate_rsd returns 'NA' for an empty/all-None list instead of raising, so one unmeasured metric no longer aborts the whole aggregation. Adds a regression test for empty/all-None/single-value RSD. Signed-off-by: Michael Oviedo <mikeovi@amazon.com>
…RSD) Found via live e2e: 'aggregate' of two normal index-only runs crashed with ValueError 'Cannot calculate RSD ... empty list of values'. Root cause was a collision between two prior changes: - the robustness fix pre-filtered None mean values at the call site, and - the opensearch-project#1096 merge made calculate_rsd tolerate None *inside* a list but still raise on a genuinely empty list. Together, an all-None metric (e.g. index-only throughput.mean) produced an empty list that hit the raise. Fix: - build_aggregated_results_dict passes raw v.get('mean') values (still avoids the original KeyError on a missing 'mean' key) and lets calculate_rsd handle None/empty. - calculate_rsd returns 'NA' for an empty/all-None list instead of raising, so one unmeasured metric no longer aborts the whole aggregation. Adds a regression test for empty/all-None/single-value RSD. Signed-off-by: Michael Oviedo <mikeovi@amazon.com>
…RSD) Found via live e2e: 'aggregate' of two normal index-only runs crashed with ValueError 'Cannot calculate RSD ... empty list of values'. Root cause was a collision between two prior changes: - the robustness fix pre-filtered None mean values at the call site, and - the opensearch-project#1096 merge made calculate_rsd tolerate None *inside* a list but still raise on a genuinely empty list. Together, an all-None metric (e.g. index-only throughput.mean) produced an empty list that hit the raise. Fix: - build_aggregated_results_dict passes raw v.get('mean') values (still avoids the original KeyError on a missing 'mean' key) and lets calculate_rsd handle None/empty. - calculate_rsd returns 'NA' for an empty/all-None list instead of raising, so one unmeasured metric no longer aborts the whole aggregation. Adds a regression test for empty/all-None/single-value RSD. Signed-off-by: Michael Oviedo <mikeovi@amazon.com>
…RSD) Found via live e2e: 'aggregate' of two normal index-only runs crashed with ValueError 'Cannot calculate RSD ... empty list of values'. Root cause was a collision between two prior changes: - the robustness fix pre-filtered None mean values at the call site, and - the opensearch-project#1096 merge made calculate_rsd tolerate None *inside* a list but still raise on a genuinely empty list. Together, an all-None metric (e.g. index-only throughput.mean) produced an empty list that hit the raise. Fix: - build_aggregated_results_dict passes raw v.get('mean') values (still avoids the original KeyError on a missing 'mean' key) and lets calculate_rsd handle None/empty. - calculate_rsd returns 'NA' for an empty/all-None list instead of raising, so one unmeasured metric no longer aborts the whole aggregation. Adds a regression test for empty/all-None/single-value RSD. Signed-off-by: Michael Oviedo <mikeovi@amazon.com>
Description
calculate_weighted_averagereducedmin/maxwithvalue.get(metric_field, 0). That default only applies when the key is absent, so a key present with valueNonereachedmin()/max()and raisedTypeError: '<' not supported between instances of 'NoneType' and 'NoneType'. The percentile/median branch just below had the same flaw in a different shape —value * iterationsonNone.calculate_rsdis a second, independent site, reached frombuild_aggregated_results_dictwith the per-run mean values (TypeError: can't convert type 'NoneType' to numerator/denominator). Fixing only the first one moves the crash rather than removing it — that is how I found the second, by running against real data after unit tests for the first were already green.The change:
weighted_meanhelper. The divisor now depends on which runs contributed a value, so it can no longer be one total summed up front, and the dict and scalar branches were computing it two different ways;Nonewhen no run contributed a value, rather than substituting0.0would read as "throughput was zero", which is a different claim from "not measured". This matches howaggregate_json_by_keyin the same file already treats nulls;NAfromcalculate_rsdwhen no values remain, as it already does for the single-value case.Issues Resolved
Resolves #1093
Testing
New functionality includes testing
Two new tests in
tests/aggregator_test.py: all-null metric fields, and partially null — a metric with samples in one run but not another, where the valid values must still aggregate, weighted only by the runs that contributed them.Both verified red without the fix, by restoring
main'saggregator.pyunder the new tests rather than by inspection.Full suite:
1424 passed, 5 skipped(baseline onmainis 1422).pylintclean.End-to-end through the real CLI, on test-run files written by OSB's own
TestRun.as_dict():aggregategoes from❌ FAILUREto✅ SUCCESS, and the output is correct rather than merely non-crashing — a healthy operation aggregates tooverall_min 40000.0, mean 42250.0, median 42150.0, overall_max 44500.0withmean_rsd 0.8368, while the null operation reportsnullthroughput andmean_rsd "NA".The
calculate_rsdsite has no dedicated unit test here; it is covered by the end-to-end run, where it was the crash that surfaced once the first site was fixed. I can add a direct one if you'd prefer it in the suite.Notes
Found while using Apache solr-orbit, a Python port of OSB whose
aggregator.pyis byte-identical to this one apart from the import module name, to run a multi-configuration benchmark campaign. The same fix is proposed there as apache/solr-orbit#58. Reported here because the defect is upstream, not port-specific.