Randomize range bounds in Solr's string queries - #76
Open
serhiy-bzhezytskyy wants to merge 1 commit into
Open
Conversation
Query randomization looked for a range by walking the query as an object tree in search of a "range" key. Solr's JSON DSL states the query as a string, for example "total_amount:[5 TO 15}", so the walk reached a leaf node immediately, returned no fields, and every randomized value was discarded. The log still said "Query randomization is enabled", and every client sent the one query written in operations/default.json. Find range terms in the query string, and in each entry of the filter list, since nyc_taxis' distance_amount_facet states its range there. Substitute only the bounds and keep the brackets the query already uses, so an exclusive bound stays exclusive - the same way the object-based path keeps whichever of lt/lte it found. A term that leaves a bound open, "total_amount:[5 TO *]", is left alone. "*" is Solr's spelling of a bound the query does not state, and the object-based path does not randomize a one-sided range either: check_one_of_each_name_present requires one name from each of gte/gt and lte/lt, so such a field is never collected. The skip is applied in the substitution as well as in the extraction, because a term visited by the regexp but absent from fields_and_paths would otherwise consume the value meant for the next term and write it to the wrong field. The bounds are read from the value source by name rather than with a default, so a source that returns unexpected keys raises KeyError instead of silently leaving the query as it was written - the same failure the object-based path gives, and the same silent no-op this commit removes. Checked against a live Solr, not only in unit tests: 450 randomized queries over the five nyc_taxis operations that register a value source, across six seeds, all returned HTTP 200, and every matched document landed in a facet bucket - sum(bucket counts) == numFound throughout. The only queries that matched nothing are trip_distance:[N TO N}, which upstream produces just as often from gte:5, lt:5. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Contributor
Author
|
For sequencing: the two workloads defects this makes reachable are apache/solr-orbit-workloads#20. That one is inert without this PR, and this PR breaks |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Description
--randomization-enabledhad no effect on a Solr workload: every client sent the one query written inoperations/*.jsonfor the whole run, and the log said randomization was on.extract_fields_and_pathswalksparams["body"]["query"]as an object tree looking for arangekey, which is the OpenSearch query DSL. Solr's JSON DSL states the query as a string, so the walk reached a leaf immediately and returned no fields; with no fields no standard value source is called andset_rangenever runs.On
mainat f088380, the same nyc_taxisrangeoperation in both shapes:This finds range terms in the query string and in each entry of the
filterlist, since nyc_taxis'distance_amount_facetstates its range there, and substitutes only the bounds:The brackets the query already states are kept, so an exclusive bound stays exclusive — the same way the object path keeps whichever of
lt/lteit found.Three deliberate details:
A term that leaves a bound open is left alone.
*is Solr's spelling of a bound the query does not state, and the object path does not randomize a one-sided range either:check_one_of_each_name_presentrequires one name from each ofgte/gtandlte/lt, so such a field is never collected.total_amount:[5 TO *]is therefore untouched.The skip is applied in the substitution as well as the extraction. A term visited by the regexp but absent from
fields_and_pathswould otherwise consume the value meant for the next term and write it to the wrong field:total_amount:[* TO 15} AND trip_distance:[1 TO 9}with one value gavetotal_amount:[3 TO 7} AND trip_distance:[1 TO 9}.Bounds are read by name, not with a default. A value source that returns unexpected keys raises
KeyErrorrather than silently leaving the query as written — the same failure the object path gives, and the same silent no-op this PR removes.Measured
Not only unit tests. 450 randomized queries over the five nyc_taxis operations that register a value source, across six seeds, against Solr 10.0.0 with 300,649 documents: all HTTP 200, and every matched document lands in a facet bucket —
sum(bucket counts) == numFoundthroughout, 0 violations. The only queries that match nothing aretrip_distance:[N TO N}, which upstream produces just as often fromgte: 5, lt: 5.I also checked the wider blast radius, since this makes a previously dead path live: an operation with a two-sided range term but no registered value source now fails a run with
SystemSetupErrorfromparams.generate_standard_values_if_absent. Scanning every operations file insolr-orbit-workloads: 5 range terms, all 5 registered, 0 missing;geonameshas none. So no existing workload starts failing.solr-orbit-workloadsnyc_taxis/workload.pyreachable —random_money_valuespasses a float torandom.randrange, which raisesTypeErroron every Python this project supports, so therangeoperation dies immediately. I have those ready and will open them againstsolr-orbit-workloadsalongside this; they should land together with it.Issues Resolved
Fixes #75
Testing
Six tests in
tests/workload/loader_test.py: range terms found in a query string and in thefilterlist, the brackets preserved throughset_range, the wholeget_randomized_valuespath on a string query, an open bound left alone, a skipped term not taking another term's value, and a mis-keyed value source raising.Each of the three details above was mutation-checked — reverting it fails exactly the test that covers it:
extract_solr_range_terms..._leaves_a_bound_open_is_not_randomized:[('total_amount', ('query',))] != []replace()..._does_not_take_another_terms_value: value written to the wrong field.get(name, old)..._omits_a_bound_is_not_silently_ignored:KeyError not raisedruff checkclean; full unit suite 1116 passed / 5 skipped.make itwas not run.