Skip to content
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 methods for UpdateResponse #22586

Merged
merged 7 commits into from
Jan 19, 2017

Conversation

tlrx
Copy link
Member

@tlrx tlrx commented Jan 12, 2017

This commit adds the fromXContent() method to the UpdateResponse class, so that it can be used with the high level rest client.

This commit adds the fromXContent() method to the UpdateResponse class, so that it can be used with the high level rest client.
Copy link
Member

@javanna javanna left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

left a few small comments, LGTM otherwise

@@ -292,8 +292,8 @@ protected static void declareParserFields(ConstructingObjectParser<? extends Doc
objParser.declareString(constructorArg(), new ParseField(_ID));
objParser.declareLong(constructorArg(), new ParseField(_VERSION));
objParser.declareString(constructorArg(), new ParseField(RESULT));
objParser.declareObject(constructorArg(), (p, c) -> ShardInfo.fromXContent(p), new ParseField(_SHARDS));
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

did ShardInfo become a constructor argument?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No - I changed it to optionalConstructorArg() since it can be an argument of the UpdateResponse.

* because most fields are parsed by the parent abstract class {@link DocWriteResponse} and it's
* not easy to parse part of the fields in the parent class and other fields in the children class
* using the usual streamed parsing method.
*/
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think you can remove this comment ;) no need to explain why you are using ObjectParser I think :)

try (XContentBuilder builder = XContentBuilder.builder(xContent)) {
// "get" field contains an embedded version of {@link GetResult} and requires
// to be parsed as if it was a full XContent object.
XContentHelper.copyCurrentStructure(builder.generator(), p);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why are we copying this? because the xContentType can differ between the response and the get section?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

same question

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

because the xContentType can differ between the response and the get section?

No, this has been a crappy hack around the fact that GetResult can be rendered as a ToXContentObject using toXContent() or as a fragment using toXContentEmbedded().

I changed that so that GetResult now has a fromXContentEmbedded() parsing method that can be used in UpdateResponse's object parser.

Note that GetResult.toXContent() delegates all the work to fromXContentEmbedded(). Please let me know what you think about this.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

++ thanks


// We can't use equals() to compare the original and the parsed update response
// because the random update response can contain shard failures with exceptions,
// and those exceptions are not parsed back with the same types.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can we rather generate responses without exceptions and test them like we usually do, and test exceptions separately?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I changed that part to do something slightly different. We can't compare UpdateResponse using equals() because it can have shard failures AND the GetResult object embedded in the update response is a bit different after parsing (it does not have index/type/id/version).

For the shard failures, we already have IndexResponseTests.assertDocWriteResponse() which takes care of checking shard failures, so I reused it.

For the GetResult, I used something similar to the GetResultTests where it generates and returns a Tuple of actual and expected GetResult, and I set to null the index/type/etc values expected for the parsed GetResult.

}

private static void assertUpdateResponse(UpdateResponse expected, Map<String, Object> actual) {
IndexResponseTests.assertDocWriteResponse(expected, actual);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

don't we have to check also the get result part?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, of course, how can I forgot that? It's like running a 100 meters and forget to pass the finish line...

Copy link
Member

@cbuescher cbuescher left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I also took a look but other than the question @javanna already asked I have nothing to add at the moment. Will take another final look if necessary.

@tlrx
Copy link
Member Author

tlrx commented Jan 17, 2017

@javanna @cbuescher I updated the pull request with your comments. I think I addressed all of them. It would be great if you can have another look.

assertNull(parser.nextToken());
}

final UpdateResponse updateResponse = tuple.v2();
Copy link
Member

@javanna javanna Jan 17, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can you call the variable expected or something along those lines?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Renamed to expectedUpdateResponse

@@ -160,4 +213,13 @@ public static GetResult mutateGetResult(GetResult getResult) {
}
return Tuple.tuple(fields, expectedFields);
}

private static BytesReference toXContentEmbedded(GetResult getResult, XContentType xContentType) throws IOException {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should you rather have an anonymous ToXContent that delegates GetResult#toXContentEmbedded and call XContentHelper#toXContent instead?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good idea

Copy link
Member

@javanna javanna left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

long version = actualGetResult.getVersion();
DocWriteResponse.Result result = actualGetResult.isExists() ? DocWriteResponse.Result.UPDATED : DocWriteResponse.Result.NOT_FOUND;

Long seqNo = randomBoolean() ? null : randomNonNegativeLong();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Generating random longs like this tends to generate really large numbers. I added another 50/50 condition in some of my randomized tests that do e.g. a %10000 to get smaller numbers here more often, this uncovered some type conversion issues (integer/long) in the parser. Maybe you could add something like that here too, since smaller numbers are more likely to appear in real responses.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

++ let's add it as a common method somewhere so we remember to do it in other places too?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

While I think a common methods would be great, I'm not sure this distinction (small vs. large longs) is needed very often. In the parsing tests its good to have some more "smaller" values to catch cases where they might be parsed back as int. I wonder how a common method would be called (randomNonNegativeLongButOftenSmall?), which range to sample the small values from (that might vary between tests) and where to put it. But maybe @tlrx has some suggestion.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Here I changed to Long seqNo = randomFrom(randomNonNegativeLong(), (long) randomIntBetween(0, 10_000), null); and I also added a comment.

I'm for having less trivial random methods, I really prefer to repeat stuff and adds comments where it makes sense.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

++ I like that solution here

});

DocWriteResponse.declareParserFields(PARSER);
PARSER.declareObject(UpdateResponse::setGetResult, (p, c) -> GetResult.fromXContentEmbedded(p), new ParseField(GET));
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it might be possible to use the index/type/id information from the already constructed UpdateResponse here to set those missing fields for the GetResult. It looks a bit odd but I think it might work, will need to check that in test though. Or can the index/type/id in the get result be different from the values in the overall response?

Copy link
Member

@cbuescher cbuescher Jan 17, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You can do sth. like this I think instead of directly setting the parsed (incomplete GetResult):

(response, getResult) -> response.setGetResult(new GetResult(response.getIndex(), response.getType(), response.getId(), getResult.getVersion(), getResult.isExists(), getResult.internalSourceRef(), getResult.getFields()))

This way you can use the index/type/id from the parent UpdateResponse together with the rest of the parsed GetResult information. I haven't tested this very thoroughly though because didn't completely understand the test setup yet.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I had the same idea but at that time all the object was parsed in the ConstructingObjectParser. Then I reverted this part and added a comment in the randomUpdateResponse() test method, but now I'm thinking of it again it does not cost a lot and it makes sense. Thanks for raising this point again.

PARSER.declareObject(UpdateResponse::setGetResult, (p, c) -> GetResult.fromXContentEmbedded(p), new ParseField(GET));
}

public static UpdateResponse fromXContent(XContentParser parser) throws IOException {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: IOException is not thrown

// by the toXContentEmbedded method.
GetResult expectedGetResult = new GetResult(null, null, null, -1,
getResults.v2().isExists(), getResults.v2().sourceRef(), getResults.v2().getFields());
expected.setGetResult(expectedGetResult);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why is the expected get result only set to the expected UpdateResponse when seqNo is not null? Should this be behind the if block instead?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good catch, it should be aligned with actualGetResult.isExists() / expectedGetResult.isExists(). I'll change that.


public void testToAndFromXContent() throws IOException {
final XContentType xContentType = randomFrom(XContentType.values());
final Tuple<UpdateResponse, UpdateResponse> tuple = randomUpdateResponse();
Copy link
Member

@cbuescher cbuescher Jan 17, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There is a problem with this test setup that I just found: the xContentType that is used for parsing here is not necessarily the same as the one that is used int randomUpdateResponse(). So the expected values might be off, e.g. if in randomUpdateResponse() SMILE is used and here xContentType is Yaml.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

you are right thanks a lot for catching this

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good catch @cbuescher ! Vielen dank!

ReplicationResponse.ShardInfo shardInfo = RandomObjects.randomShardInfo(random(), true);

actual = new UpdateResponse(shardInfo, shardId, type, id, seqNo, version, result);
actual.setGetResult(actualGetResult);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

same here, seems like the getResult should be set regardless of seqNo being present or not, or do I miss something?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You're right, thanks

@cbuescher
Copy link
Member

@tlrx I left some more comments concerning parsing the GetResult. I think this would simplify the whole test a little bit.

@tlrx
Copy link
Member Author

tlrx commented Jan 19, 2017

@cbuescher @javanna Thanks! I think I addressed all your comments in the last two commits. Are we good to go?

@javanna
Copy link
Member

javanna commented Jan 19, 2017

Are we good to go?

++ on my end

@cbuescher
Copy link
Member

for me also, LGTM

@tlrx tlrx merged commit 833284c into elastic:master Jan 19, 2017
@tlrx tlrx removed the review label Jan 19, 2017
@tlrx
Copy link
Member Author

tlrx commented Jan 19, 2017

Thanks @cbuescher @javanna !

tlrx added a commit that referenced this pull request Jan 19, 2017
This commit adds the fromXContent() method to the UpdateResponse class, so that it can be used with the high level rest client.
@tlrx tlrx deleted the add-parsing-methods-to-update-response branch January 19, 2017 12:08
jasontedor added a commit to jasontedor/elasticsearch that referenced this pull request Jan 20, 2017
* 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)
  ...
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants