-
Notifications
You must be signed in to change notification settings - Fork 25k
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
Fix HLRC parsing of CancelTasks response #47017
Conversation
Pinging @elastic/es-core-features |
Thanks for the submission! Ill review this soon! |
ok finally getting my head above water, will be reviewing today! |
Amazing! |
hey @jesinity I have begun reviewing the code. One thing I would like to point out is that I think the response class hierarchy is too heavy for the client. I would rather see something (this is only the Task/Node Exceptions, but I will be able to provide more info tomorrow) like the following. What you will notice is that there is a response in the test case (i only did this for example purposes). This response contains the 2 lists of exceptions that can be returned, but if you notice, its only a nested map, rather than a full blown client side Exception hierarchy. Since these exceptions are generic due to the fact they are parsed from json, it does not make much sense to build real exceptions from them. They arent going to be throwable in code, so the normal things you would do with them in a java ecosystem dont apply. This being the case, the best you can do is inspect the strings within the class, so it makes sense to just keep it a generic map and let the user do what they will with it. I think there are further things we can do with the other parts of the response, but I need a bit more time to look thru the code. I will provide a better explanation tomorrow (hopefully). It is a day off for my kids and well, kids are crazzzzzzzy sometimes in the house :p One other thing to note is that the TaskInfo can and should be a generic string. A client would never build one of those, because they are always generated from elasticsearch anyway, and the task code should always be using a string instead of a TaskId. This may not be the case for other client methods, but its the right way to go here. More to come soon! Also I want to say thank you very much for the contribution. This is a doozy of a split between client/server, and I am very happy you took it on. We will get thru it together :D There is a newer copy of this code below
|
ok, thanks for your comments, I agree with them, I think a shallow version of Exception is something more handy for the clients to use. |
Yes absolutely. This is why I put the example in a test, to also give u an example for how to do the tests for these classes. Once I get in to the office Ill be doing my best to finish this review and get you to a place where you can begin working on it again. |
I think we might be in a place where eventually we can make concrete client side status classes with a contract but unless you need a particular status but I am perfectly fine just keeping it I think that (along with turning the TaskID into a string) will make the response easy. Also, there is a ton of extra code on the original response in server that is used by the cat api, and we really dont need to port that. I think the response above with the 3 things 1) a client side TaskInfo, 2) a List of task errors, 3) a List of node errors will get us the best, smallest, class. |
10 minutes ago @hub-cap, you are just wrong. instead of the status being bytes, lets also turn it into a nested map. This will give users the ability to inspect it without converting it to something first. So basically in the parser you write, use the same |
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.
ok I think this will be great moving forward. thank you for the effort, I knew it would be a pesky API! :)
Feel free to ask me any Qs you might have regarding the comments!
* @return the response | ||
* @throws IOException in case there is a problem sending the request or parsing back the 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.
I think its best to keep the javadoc in 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.
sorry I did not notice I removed those.
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.
sorry I did not notice I removed those
@@ -135,6 +125,7 @@ public CancelTasksResponse cancel(CancelTasksRequest cancelTasksRequest, Request | |||
*/ | |||
public Cancellable cancelAsync(CancelTasksRequest cancelTasksRequest, RequestOptions options, | |||
ActionListener<CancelTasksResponse> listener) { | |||
|
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.
empty space is not needed
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.
ok
public static final String[] EMPTY_ARRAY = new String[0]; | ||
public static final String[] ALL_ACTIONS = EMPTY_ARRAY; | ||
public static final String[] ALL_NODES = EMPTY_ARRAY; | ||
private String[] nodes = ALL_NODES; |
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 can be simplified. In the HLRC its fine to just hold a list of nodes initialized by List.of()
and we likely dont need those other String arrays above
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.
ok
public static final String[] ALL_NODES = EMPTY_ARRAY; | ||
private String[] nodes = ALL_NODES; | ||
private TimeValue timeout; | ||
private String[] actions = ALL_ACTIONS; |
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 with this above stuff.
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.
ok
private TimeValue timeout; | ||
private String[] actions = ALL_ACTIONS; | ||
private TaskId parentTaskId = TaskId.EMPTY_TASK_ID; | ||
private TaskId taskId = TaskId.EMPTY_TASK_ID; |
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.
These two should be strings instead of a TaskId
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.
consider that the TaskId
is basically a wrapper of nodeId:taskId
where I can appy wildcards.
Removing it is ok for me, but how would we go about passing these parameters and ensuring
- wildcards are applied
- emptiness is guarded
?
Shall I implement the logic in the request itself?
Also the isSet
method is used to check the TaskId is set.
So overall I think it would be better to keep this class, as the burden to create it is not that big.
this.taskGroups.addAll(buildTaskGroups()); | ||
} | ||
|
||
private List<TaskGroup> buildTaskGroups() { |
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 all of this, including the classes you have created for this can be removed. This was only used in the CAT api on the server side, so we can just return the same thing the transport response returns.
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.
they are present in the API, that why I kept them. BTW, here it uses the nodeId
contained in the TaskId
.
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.
You are correct, I missed that this is all in the parent task to the CancelTasksResponse
👍
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 its worth moving them to a "list task response" common class tho like in server side, so we dont have to move them (or duplicate them cuz maybe the author did not see them here).
client/rest-high-level/src/main/java/org/elasticsearch/client/tasks/TaskId.java
Show resolved
Hide resolved
client/rest-high-level/src/main/java/org/elasticsearch/client/tasks/TaskGroup.java
Show resolved
Hide resolved
/** | ||
* Holder of data created out of a {@link CancelTasksResponse} | ||
*/ | ||
public class NodesInfoData { |
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.
Im not sure we need any of this here. This is part of the cat api stuff mentioned above.
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.
Well, these classes hold the tasks themselves (among other things) that is why they are there.
I think that they can be made package
access so that the user does not even know they are there.
import org.elasticsearch.common.xcontent.ObjectParser; | ||
import org.elasticsearch.common.xcontent.XContentParser; | ||
|
||
public class NodeData { |
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.
We might eventually need this for other APIs but I think the TaskInfo
class will suffice for the CancelTaskResponse
I am implementing the changes and I have some comments/questions, see above
so you mean that the status is going to be a nested
it is a bit daunting to implement but I think it is feasible. |
Hey @jesinity I dont want you to work too hard on an implementation there. The built in object parsers have a |
Also, re: the classes I mentioned removing, those are specific to the rest layer, and the rest calls (curl style) will return a different set of items than just the transport client. The transport client is here, Most of that code is used here in the rest layer. |
ok, the following snippet of code now has the edit:
|
I am still working on it, I think by monday I should reissue a merge request |
Awesome news. I will be out for a week starting monday but ill do my best to review it when i see it. |
Hello, I issued a second commit.
|
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 is great work. I think we have a few things to do to update it, and im weary of merging things that have large swaths of code w/o tests. Things like the ElasticsearchException
, which have tons of logic in them, as well as the helpers that build some of the response code do not have tests. While they are potentially exercised in a response test, some of the things like the ElasticsearchException
have so much nested logic that it might be worth testing it on its own. It might be worth you going thru the response classes and asking yourself if these classes are sufficiently exercised in the response test or not.
'}'; | ||
} | ||
|
||
public static class Builder { |
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.
Lets remove this builder since some of the setters on the class itself already return this, and then add return this;
to the methods in the Request that do not have them already.
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 lets just remove the "return this" from the methods in the class and keep the builder, i think that makes more sense
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.
ok
this.taskGroups.addAll(buildTaskGroups()); | ||
} | ||
|
||
private List<TaskGroup> buildTaskGroups() { |
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.
You are correct, I missed that this is all in the parent task to the CancelTasksResponse
👍
this.taskGroups.addAll(buildTaskGroups()); | ||
} | ||
|
||
private List<TaskGroup> buildTaskGroups() { |
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 its worth moving them to a "list task response" common class tho like in server side, so we dont have to move them (or duplicate them cuz maybe the author did not see them here).
client/rest-high-level/src/main/java/org/elasticsearch/client/tasks/NodeData.java
Outdated
Show resolved
Hide resolved
client/rest-high-level/src/main/java/org/elasticsearch/client/tasks/TaskGroup.java
Show resolved
Hide resolved
...h-level/src/test/java/org/elasticsearch/client/documentation/TasksClientDocumentationIT.java
Outdated
Show resolved
Hide resolved
request.setTaskId(new TaskId("nodeId1", 42)); //<1> | ||
request.setActions("cluster:*"); // <2> | ||
request.setNodes("nodeId1", "nodeId2"); // <3> | ||
// request.setTaskId(new org.elasticsearch.client.tasks.TaskId("nodeId1", 42)); //<1> |
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.
any reason this is commented out?
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, I forgot to remove the line as I managed the emptiness through optionals, there is not the concept of "empty task id"
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.
actually the ListTasksResponse contain all data that is CancelTasksResponse. Shall I add this new class in the hierarchy?
...h-level/src/test/java/org/elasticsearch/client/documentation/TasksClientDocumentationIT.java
Outdated
Show resolved
Hide resolved
import java.util.List; | ||
import java.util.Map; | ||
|
||
public class CancelTasksResponseTests extends ESTestCase { |
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 signature of my test was the following,
public class CancelTasksResponseTests extends AbstractResponseTestCase<CancelTasksResponse, CancelTasksResponseTests.CancelTasksResponse> {
and this test needs to also have that signature. Also, please do not just craft one message, you should be using "random" generators like in the other tests. It may require some additional methods to make the inner objects random, and you wont be able to use a nice .equals() method to test equality, but the point of these is to test that a server side response maps properly to a rest 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.
a clarification here:
AbstractResponseTestCase<CancelTasksResponse, CancelTasksResponseTests.CancelTasksResponse>
the first CancelTasksResponse
is the server side object (that also extends toXContent
.
Shall I use this to create an instance that gets marshalled in XCOntent and parsed back?
I assumed you wanted to remove dependencies from the server, but maybe for testing purposes it is useful and more realistic to test the marshalling and unmarshalling of data.
If so, instead of using the CancelTasksResponseTests.CancelTasksResponse
shall I use the real tasks counterpart, that implements all the fromXContent
already (org.elasticsearch.client.tasks.CancelTasksResponse
) ?
Otherwise there are duplications of the logic.
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.
also, shall I remove my test or can I Keep it renaming it?
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 test is not needed as is, but once it extends the AbstractResponseTestCase
it will be good.
I assumed you wanted to remove dependencies from the server, but maybe for testing purposes it is useful and more realistic to test the marshalling and unmarshalling of data.
This is correct, we want to test the actual server object transforming to a client. There are a lot of tests that extend this so you can see the pattern for usage here.
If so, instead of using the CancelTasksResponseTests.CancelTasksResponse shall I use the real tasks counterpart,
Yes, the CancelTaskResponseTests subclass was only to show you how to use it, so it can be viewed as an example, and not code meant to be in the pull request.
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.
ok I followed your boilerplate: actually I needed to create an adapter around the server side CancelTaskResponse
because the default toXContent
marshalls tasks in a different way, not under nodes
but under tasks
client/rest-high-level/src/main/java/org/elasticsearch/client/tasks/TaskId.java
Show resolved
Hide resolved
I added two more commits to address the changes requested. |
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.
Minor nits, Im going to enable tests on this now so any updates will rerun the tests. Great work. Almost to the finish line!!!!
public Map<String, Object> getStatus() { | ||
return status; | ||
} | ||
|
||
void noOpParse(Object s) {} |
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 is this used for?
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 is used down in the parser.
node:id
pair is already being parsed in the NamedObjectParser
identifier, but they are also repeated in the body.
So I parse them in the ObjectParser
but ignore the result:
// already provided in constructor: triggering a no-op parser.declareString(TaskInfo::noOpParse, new ParseField("node")); // already provided in constructor: triggering a no-op parser.declareLong(TaskInfo::noOpParse, new ParseField("id"));
@@ -162,13 +162,11 @@ public void testCancelTasks() throws IOException { | |||
// end::cancel-tasks-request | |||
|
|||
// tag::cancel-tasks-request-filter | |||
// request.setTaskId(new org.elasticsearch.client.tasks.TaskId("nodeId1", 42)); //<1> |
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.
having nothing between these tags will break the docs build.
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.
ok, so is it safe to remove also the comments? the filtering capabilities is managed in the upper call
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 I could to if to add an example to filter by TaskId
, such as
CancelTasksRequest byTaskIdRequest = new org.elasticsearch.client.tasks.CancelTasksRequest.Builder() .withTaskId(new org.elasticsearch.client.tasks.TaskId("myNode",44L)) .build();
import java.util.List; | ||
import java.util.Map; | ||
|
||
public class CancelTasksResponseParsingTests extends ESTestCase { |
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 dont think we need this, given that we have a test that tests parsing of the 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.
ok, I can remove it
import java.io.IOException; | ||
import java.util.List; | ||
|
||
public class ElasticSearchExceptionTests extends AbstractResponseTestCase<org.elasticsearch.ElasticsearchException, |
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.
Elasticsearch
not ElasticSearch
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.
ok! sometimes I camel more than I should
@elasticmachine ok to test |
did changes, I fixed the doc generation. I don't know why there are failing tasks. DO I need to rebase my changes to master? |
7a7aeab
to
c3ae790
Compare
rebased but still I get tasks errors: this time only one fails! The error logs does not help me much |
@elasticmachine run elasticsearch-ci/packaging-sample-matrix |
good the checks have passed! |
Hey there is nothing else you need to do. Ill be doing the merging and backporting in the next day. Great work! |
@elasticmachine update branch |
Neither do I have to rebase and squash the commits? |
No need for any of that. Ive merged new commits so the tests rerun and ill be handling the squash thru the github UI. |
Ive merged this, thank you for your work. Im backporting it now! |
Adds support for proper cancel tasks parsing. Closes #45414
Hi @hub-cap
Some comments:
I created all client side counterparts for all server side objects involved in this exchange.
I did it also for ElasticSearchException and TaskOperationFailure. These are shallow copies of server side counterparts: at first I thought to create some common interfaces in the core module but then I reverted it back.
I did this way in order to cut down dependencies from server side classes, as I saw there are long term requirements to achieve it.
I did not manage to create complex statuses with failures or exceptions, but I went through how the response is generated server side.
I did not touch yet the ListTasksResponse to the other calls of the tasks api are still using old classes.
I did some unit tests I did not commit yet: some parsing tests are enough for the testing purposes?
I left out the parsing of the status field, which format changes very much over responses and is issued only if detailed parameter is issued to server.
I did it because I did not find an easy why doing it without falling back again on server side dependencies.
Status is converted to a raw format wrapping a BytesReference that exposes a Map<String,Object> functionality: I wonder if it is really something worth to have for the client.
I tried to set thing apart but at least a dependency on Lucene
org.apache.lucene.util.BytesRef in org.elasticsearch.common.bytes.BytesArray is unavoidable.
If you want I can add back the dependency on ObjectParserHelper, causing a dependency on server side libraries, or address me some other way around to fix the issue.