-
Notifications
You must be signed in to change notification settings - Fork 483
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
HDDS-6213. Pluggable OzoneManager request handling hooks #3104
Conversation
@@ -47,6 +47,11 @@ https://maven.apache.org/xsd/maven-4.0.0.xsd"> | |||
<groupId>javax.annotation</groupId> | |||
<artifactId>javax.annotation-api</artifactId> | |||
</dependency> | |||
<dependency> |
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 know this one is weird, but without this, the hdds-interface-client module was not compiling, and it was missing a class from this package once I have added our annotation-processing module. I would be glad if someone can explain what is the reason behind the necessity of this dependency definition after adding the ozone-annotation-processing module.
@errose28 this is a more generic framework for adding request pre and post validations. The fields in place for OM and Client are the same this is an alternative way to define and invoke validations. |
43646a3
to
fe495b2
Compare
<execution> | ||
<id>default-compile</id> | ||
<configuration> | ||
<compilerArgument>-proc:none</compilerArgument> |
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.
Please add a note as to why we need -proc:none
and the sequence of events that should occur when we fresh build the repo.
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 noting this!, I have extended the note into the comment before the plugin definition about the compiler argument specified here.
...ger/src/main/java/org/apache/hadoop/ozone/om/request/validation/RequestFeatureValidator.java
Outdated
Show resolved
Hide resolved
* License for the specific language governing permissions and limitations under | ||
* the License. | ||
*/ | ||
package org.apache.hadoop.ozone.om.request.validation2; |
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.
Is this package for a sample? Not sure why it needs to be there.
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 one is here for a test case.
The bulk of the tests are using the simple validation package, but in that we have validators for all kinds of stuff.
This package is created so that when the validator registry is instantiated, we can provide this package to scan for some of the tests, and we can test for cases when there are no validators for specific conditions, or phases or request types. As the discovery discovers classes with an annotation under a classpath url, I can not test for both, hence I created this package where I have just one specific validation.
Thank you @kerneltime for your notes, I have added answers for you. In the meantime, I was thinking about how to answer to @errose28 beyond the simple yes, to give some more context I have realized a few questions, for which I am trying to come up with good answers and reasoning, but still I feel we should go over them one by one, and discuss it once more. I am wondering what would be a good platform for that discussion. The questions:
To give some short answers:
The idea @kerneltime came up with to perhaps move some generic request processing part into validator methods, made me also think further... The idea to use this with other protocols than just the ozone client protocol, also brings us to some further changes that are necessary to extend the whole API:
Q1 also leads to one more thing, for which I did not do any research, so I just put it here as something we might examine later on: newer client requests condition can be something used on the client side, if there is such an easy way to hook things into the client side of the request, as in that case the client can use this condition to alter requests when talking to older servers... again... this came up just now in my mind, and I am absolutely unsure if we can easily hook on client side, and whether it makes any sense to do this there. Last but not least, there was a main concern from @kerneltime, how to ensure these methods will not linger somewhere in the code, and how we can be sure which methods were running during pre/post process phase for a given query. Also how we can make this easy for devs to realize that there are these validations. |
641712f
to
921f8c6
Compare
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 working on this @fapifta. Overall I think it is very well designed. I haven't had a chance to review the test code yet, but wanted to add some questions/comments for us to discuss before our meeting this afternoon.
* If you plan to use this, please justify why the validation code should not | ||
* be part of the actual request handling code. | ||
*/ | ||
UNCONDITIONAL(r -> true, ctx -> true); |
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 don't think we need these last two right now. Maybe it would be better to remove them to reduce confusion? I can see the unconditional validator being misused since there isn't really a clearly defined use case. I think most newer client handling is going to happen on the client side based on the service info call, so having it here might be misleading for devs working on something with forward compatibility concerns.
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 one is kind of a dilemma for me as well, I have removed these two for now, however I started to look into the possibilities of extending the framework to these use cases on the client side, and what changes might be necessary to use a similar approach there and whether it would be beneficial or not... But in case it will happen we can re-introduce the NEWER_CLIENT_REQUESTS, once we start to think about moving out the authorization or audit for example, we can re-introduce UNCONDITIONAL, and if we change both things, we might forget about them :)
Just to do not loose the tests for these enum values, I have just commented out test code, and the enum values, so we can reintroduce them easier if we want.
} | ||
|
||
boolean shouldApply(OMRequest req, ValidationContext ctx) { | ||
return shouldApplyTo.test(req) && shouldApplyIn.test(ctx); |
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.
Since we are just ANDing the conditions together, would it be easier to have ValidationConditions take just one predicate, and let the predicate's implementation define what conditions to act on? The current implementation seems to frequently require these true
placeholders. I think it would read clearer if ValidationConditions were declared like:
CLUSTER_NEEDS_FINALIZATION(ctx -> ctx.versionManager().needsFinalization()),
OLDER_CLIENT_REQUESTS(r -> r.getVersion() < ClientVersions.CURRENT_VERSION),
NEWER_CLIENT_REQUESTS(r -> r.getVersion() > ClientVersions.CURRENT_VERSION),
UNCONDITIONAL(r -> true);
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.
As predicates are type parametrized, we can not use just one predicate that processes either a context or a request, but you are right, it would be way more better to have a more simpler approach to express the condition.
How about using an abstract method and overriding it on a per enum value basis like the one I added instead of the old way?
...e-manager/src/main/java/org/apache/hadoop/ozone/om/request/validation/ValidatorRegistry.java
Show resolved
Hide resolved
...e-manager/src/main/java/org/apache/hadoop/ozone/om/request/validation/ValidatorRegistry.java
Show resolved
Hide resolved
...annotations/src/main/java/org/apache/ozone/annotations/RequestFeatureValidatorProcessor.java
Show resolved
Hide resolved
...apache/hadoop/ozone/om/request/validation/testvalidatorset1/GeneralValidatorsForTesting.java
Show resolved
Hide resolved
e3ef527
to
d4ac9ce
Compare
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 updates @fapifta. Minor comments on the tests but this looks good overall.
...ager/src/test/java/org/apache/hadoop/ozone/om/request/validation/TestRequestValidations.java
Outdated
Show resolved
Hide resolved
...apache/hadoop/ozone/om/request/validation/testvalidatorset1/GeneralValidatorsForTesting.java
Show resolved
Hide resolved
...ager/src/test/java/org/apache/hadoop/ozone/om/request/validation/TestRequestValidations.java
Outdated
Show resolved
Hide resolved
...ager/src/test/java/org/apache/hadoop/ozone/om/request/validation/TestRequestValidations.java
Outdated
Show resolved
Hide resolved
…such, add initial structure to the registry, and initial lookup, and execution of validator methods.
… checkstyle and rat warnings.
… translator code.
… be a validator. (First draft version surely fails checkstyle and such have to be refactored and cleaned up, commiting for demo purposes.)
…checkstyle and rat.
…oval of server version.
…lem, remove unnecessary extra compile step from annotation processing module.
This reverts commit 585c0a700e310254c49a0d530be4ccfddd714dfc.
…on itself, with that generalize the validations side logic.
Co-authored-by: Ritesh H Shukla <kerneltime@gmail.com>
…dator's packages to be encapsulated within the tests' package.
…invoked during request processing.
40617af
to
ce57565
Compare
ce57565
to
eaac42e
Compare
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 updated @fapifta LGTM.
One of the failing flaky tests is TestOzoneManagerHAMetadataOnly which I tried to run locally, and it ran green, while the other two was ignored earlier, and does not seem to work at all now. Based on this and the +1 from @errose28 I am merging this one to master. |
Sorry for being late here. Why was the new annotation added in a completely separate project in 1. |
Hi @adoroszlai no worries, you may be on time, before we go to far down this road :) Even though we have other annotations, we do not have anything that validates those annotations, and for this annotation, we wanted to ensure some invariants about the methods that are annotated with it, as there are some expectations about the methods where they are used. As such, this module has to be compiled before anything else, as compilation of anything else depends on the jar created from this module. If this module is a child of the main project, then we have a circular dependency as this module would depend on the parent while the parent would depend on the module... In order to solve this I chose to separate the module from the main pom, but made it as a dependency of the main pom with that hook it in onto the build. Certain tools does not hook it in seamlessly like version:set, which is a problem, I admit, I do not have any good resolution for. Dev-support in the main project dir seemed to be a good idea for one reason, this annotation processor is there to help developers to ensure the methods annotated with the annotation are correct, and do not violate the invariants, and if we want this may be optional for the regular dev cycle and it can be just mandatory in the CI level, but that I think would have just complicated things more. I am open to have any better place to build this. Also I had an other idea to have even a separate project like ozone-sdk or such, where we can put similar things, but for just this one use case that seemed to be an overkill. Let's discuss this further at anytime, even offline, or in a new JIRA where we can discuss the proper solution... I have a couple of other items that is related to this and related PRs, and I think there has to be some significant follow up work on this system, as we started to learn more about this while we started to use it in EC upgrade compat problems. |
Thanks @fapifta for the detailed answer. |
What changes were proposed in this pull request?
During developing different new features, the different protocols have to be kept in a stance where the old client can talk to a new server, and a new client also can talk to an old server.
The latter was solved earlier by exposing the server version in the ServiceInfo, but the prior is a hard thing to deal with.
After a few rounds of discussions we came up with an idea to implement a pluggable pre and post validation system, that can help us to either right away reject a specific query from an old client, or if the response contains something that the client can understand, give us the possibility to re-write the regular response to a form that the old client can understand, or at least give the old client a specific error message that says why the request fails.
Similarly, the system has the possibility to adjust the request coming from the old clients if it is possible, so that the request can be served.
Later on we have identified that there is a specific lifecycle phase, when the cluster is not finalized but already upgraded, certain requests in these cases might need to be right away cancelled, based on some request properties, but just until the new feature is not finalized.
With this, we have created 4 conditions in which a validation can be activated (note that one validator can be activated by multiple conditions, and one condition can trigger multiple validators). These conditions are when a newer client posts a request, when an older client posts a request, when the cluster is in the pre-finalized state, and in case of every query a validator might need to run.
Validators are assigned for request types, and request processing phases as well.
Request types are as defined for the Ozone Manager protocol definition, while processing phases for now are pre and post processing.
A post processor can not be a pre-processor at the same time, and for the different phases the validator method signature is different.
Similarly, a validator can belong to only one request type.
Binding happens at initialization time, when the Ozone Manager RPC server starts up.
The validator methods are discovered based on the newly introduced RequestFeatureValidator annotation. Note that this annotation has a processor added as well, which checks requirements against annotated validation methods at compile time.
What is the link to the Apache JIRA
https://issues.apache.org/jira/browse/HDDS-6213
How was this patch tested?
JUnit tests added for the system, OM request handler code change is straightforward enough that is the only part for which I think tests are not necessary, let me know if I am wrong, and the current tests does not prove that the integration will work.