-
Notifications
You must be signed in to change notification settings - Fork 25.8k
Add parsing from xContent to SearchProfileShardResults and nested classes #22649
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add parsing from xContent to SearchProfileShardResults and nested classes #22649
Conversation
663aa6f to
7cf041a
Compare
javanna
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
left a few minors, thanks for doing this
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What causes this if? differences between the different content types that we support?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe we should rather do this while parsing instead of using parser.map() here?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
And use java.lang.Number?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: can you replace this with the same comment that we had above? I find it more effective and clear
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
bad merge?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
why not using XContentHelper#toXContent here too?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nice, this method could be potentially called in all our tests so that we randomize the human readable flag, just to make sure we don't miss any of those additional fields. I think we haven't and this is the first time we encounter this but tests are the only way to actually verify. Maybe we can do this as a followup?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
++ good point
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
can you look into this when you have time please?
tlrx
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks @cbuescher, I'm sure that it hasn't been fun to do. I left some comments here and there, nothing big.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
And use java.lang.Number?
| } | ||
| } | ||
| } | ||
| Map<String, Long> timings = new HashMap<>(parsedTimings.size()); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can we check that parsedTimings is not null before doing this?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This part has been removed while addressing another comment.
| * A map of breakdown timing for the node | ||
| * @return The total time at this node, inclusive of children | ||
| */ | ||
| private static long getNodeTime(Map<String, Long> timings) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What if timings is null? It doesn't seem to be required in the ctor
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good point, I moved this code from somewhere where it was clear that the map is always there. I will add a check to the ctor.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks! Could we also rename this to getTotalTime or something?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The reason I left the name was to make it clearer where the code was before in this PR (its from AbstractInternalProfileTree). I will rename it to getTotalTime. Also I think the comment is not up to date anymore (it doesn't sum any children) so I will remove that part)
| ensureExpectedToken(XContentParser.Token.START_OBJECT, token, parser::getTokenLocation); | ||
| List<QueryProfileShardResult> queryProfileResults = new ArrayList<>(); | ||
| AggregationProfileShardResult aggProfileShardResult = null; | ||
| String name = null; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could this be renamed to id to avoid confusion?
| } else { | ||
| throwUnknownField(currentFieldName, parser.getTokenLocation()); | ||
| } | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe add another else block with unexpected token type?
| } else { | ||
| throwUnknownField(currentFieldName, parser.getTokenLocation()); | ||
| } | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Same here, shouldn't we throw an exception if the token type is not expected? Or maybe I'm missing something.
| } else { | ||
| throwUnknownField(currentFieldName, parser.getTokenLocation()); | ||
| } | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe also here
| XContentType xcontentType = randomFrom(XContentType.values()); | ||
| XContentBuilder builder = XContentFactory.contentBuilder(xcontentType); | ||
| builder.startObject(); | ||
| builder = shardResult.toXContent(builder, ToXContent.EMPTY_PARAMS); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can't we use XContentHelper.toXContent() here?
92d0e1e to
fabf41b
Compare
fabf41b to
4029cda
Compare
| XContentParser.Token token = parser.currentToken(); | ||
| ensureExpectedToken(XContentParser.Token.START_OBJECT, token, parser::getTokenLocation); | ||
| Map<String, ProfileShardResult> searchProfileResults = new HashMap<>(); | ||
| Map<String, ProfileShardResult> searchProfileResults = new LinkedHashMap<>(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
why do we need ordering here?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This way we preseve the order of the shard keys we encountered during xContent parsing. Without preserving this order, I encountered different xContent rendering outputs for the "shards" section (which is an array) in the roundtrip tests, so the order matters (at least the way assertToXContentEquivalent works so far).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I pushed another commit that fixes the order in which the elements in the "shards" array are written out.
tlrx
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for the hard work! I made many comments but they are all cosmetics or minor. This is getting very close
| * A map of breakdown timing for the node | ||
| * @return The total time at this node, inclusive of children | ||
| */ | ||
| private static long getNodeTime(Map<String, Long> timings) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks! Could we also rename this to getTotalTime or something?
| } else if (DESCRIPTION.match(currentFieldName)) { | ||
| description = parser.text(); | ||
| } else if (NODE_TIME.match(currentFieldName)) { | ||
| // we need to consume this value, but we use the raw nanosecond value |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think the "but we use the raw nanosecond value" part of this comment is misleading. When I read it I thought we used the NODE_TIME_RAW but it doesn't seems to be the case
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, we sum the timings values in ProfileResult ctor now, no need for both of these values, I'll update the comments
| String currentFieldName = null; | ||
| List<ProfileResult> queryProfileResults = new ArrayList<>(); | ||
| long rewriteTime = 0; | ||
| CollectorResult profileCollector = null; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe profileCollector -> collector to reflect the field's name?
| try (XContentParser parser = createParser(xContentType.xContent(), originalBytes)) { | ||
| ensureExpectedToken(XContentParser.Token.START_OBJECT, parser.nextToken(), parser::getTokenLocation); | ||
| ProfileResult parsed = ProfileResult.fromXContent(parser); | ||
| assertEquals(profileResult.getTime(), parsed.getTime()); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can we check the parser in the try/catch block, and the parsed ProfileResult after that?
It's basically moving those two lines:
ProfileResult parsed = ProfileResult.fromXContent(parser);
assertEquals(profileResult.getTime(), parsed.getTime());
after the try block.
| timings.put("key1", 12345L); | ||
| timings.put("key2", 6789L); | ||
| ProfileResult result = new ProfileResult("someType", "some description", timings, children, 123456L); | ||
| children.add(new ProfileResult("child1", "desc1", timings1, Collections.emptyList())); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I suppose that you can use singletonMap("key1", 100L) directly here
| XContentParserUtils.ensureFieldName(parser, parser.nextToken(), AggregationProfileShardResult.AGGREGATIONS); | ||
| XContentParserUtils.ensureExpectedToken(parser.nextToken(), XContentParser.Token.START_ARRAY, parser::getTokenLocation); | ||
| AggregationProfileShardResult parsed = AggregationProfileShardResult.fromXContent(parser); | ||
| assertToXContentEquivalent(builder.bytes(), toXContent(parsed, xcontentType), xcontentType); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can we move this down, after the Token checks?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
done
| ProfileResult profileResult = new ProfileResult("someType", "someDescription", timings, Collections.emptyList()); | ||
| profileResults.add(profileResult); | ||
| AggregationProfileShardResult aggProfileResults = new AggregationProfileShardResult(profileResults); | ||
| XContentBuilder builder = JsonXContent.contentBuilder(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Same here, try with resource block
| CollectorResult collectorResult = createTestItem(1); | ||
| XContentType xcontentType = randomFrom(XContentType.values()); | ||
| XContentBuilder builder = XContentFactory.contentBuilder(xcontentType); | ||
| boolean humanReadable = randomBoolean(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No need to use an intermediate variable for this
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I need it later when comparing to the xContent rendering of the parsed object.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I meant builder.humanReadable(randomBoolean()); here and use builder.humanReadable() later
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah, that doesn't work after my last changes because I got rid of the builder.
| queryProfileResults.add(ProfileResultTests.createTestItem(1)); | ||
| } | ||
| CollectorResult profileCollector = CollectorResultTests.createTestItem(2); | ||
| long rewriteTime = randomNonNegativeLong(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should we use the %100_000 strategy here too?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, good point.
| public void testFromXContent() throws IOException { | ||
| QueryProfileShardResult profileResult = createTestItem(); | ||
| XContentType xcontentType = randomFrom(XContentType.values()); | ||
| XContentBuilder builder = XContentFactory.contentBuilder(xcontentType); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Same here
|
|
||
| for (Map.Entry<String, ProfileShardResult> entry : shardResults.entrySet()) { | ||
| builder.startObject(PROFILE_FIELD).startArray(SHARDS_FIELD); | ||
| TreeSet<String> sortedKeys = new TreeSet<>(shardResults.keySet()); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Are we forced to sort all shard results every time just to be able to check them in tests? Can't we check / sort again in the test instead?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I talked to @javanna about this, the idea was to also make the rendered response reproducibly the same for the same source object. Happy to change this back any time, but the overhead this poses is negligible I think, also its just performed once per response.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
it is not just for tests. Unfortunately this object uses a map to store information that goes into a json array, which is by definition ordered. Either we change the format of the response or we guarantee ordering then. I think this is a good way to guarantee ordering and make the response more human readable too.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
maybe add a comment to explain this @cbuescher ?
|
@tlrx thanks for reviewing, I added a commit that addresses most of your comments and left some answer where I'd like to keep things (or discuss further) |
javanna
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM
tlrx
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM
0ef1ad1 to
1be3216
Compare
…sses (#22649) In preparation for being able to parse SearchResponse from its rest representation for the java rest client, this adds fromXContent to SearchProfileShardResults and its nested classes.
|
Ported to 5.x with c8e027f |
* master: (117 commits) Add missing serialization BWC for disk usage estimates Expose disk usage estimates in nodes stats S3 repository: Deprecate specifying credentials through env vars, sys props, and remove profile files (elastic#22567) Fix Eclipse project generation Fix deprecation logging for lenient booleans Remove @Header we no longer need Make lexer abstract [Docs] Remove outdated info about enabling/disabling doc_values (elastic#22694) Move lexer hacks to EnhancedPainlessLexer Fix incorrect args order passed to createAggregator Improve painless's javadocs Add TestWithDependenciesPlugin to build (elastic#22646) Add parsing from xContent to SearchProfileShardResults and nested classes (elastic#22649) Add unit tests for FiltersAggregator (elastic#22678) Don't register search response listener in transport clients unmute FieldStatsIntegrationIT.testGeoPointNotIndexed, fix already pushed Mute FieldStatsIntegrationIT.testGeoPointNotIndexed, for now Painless: Add augmentation to string for base 64 (elastic#22665) Fix NPE on FieldStats with mixed cluster on version pre/post 5.2 (elastic#22688) Add parsing methods for UpdateResponse (elastic#22586) ...
As a follow up to elastic#22649, this changes the resent tests for parsing parts of search responses to randomly set the humanReadable() flag of the XContentBuilder that is used to render the responses. This should help to test that we can parse back thoses classes if the user specifies `?human=true` in the request url.
As a follow up to #22649, this changes the resent tests for parsing parts of search responses to randomly set the humanReadable() flag of the XContentBuilder that is used to render the responses. This should help to test that we can parse back thoses classes if the user specifies `?human=true` in the request url.
As a follow up to #22649, this changes the resent tests for parsing parts of search responses to randomly set the humanReadable() flag of the XContentBuilder that is used to render the responses. This should help to test that we can parse back thoses classes if the user specifies `?human=true` in the request url.
In preparation for being able to parse SearchResponse from its rest representation
for the java rest client, this adds fromXContent to SearchProfileShardResults and its
nested classes.