-
Notifications
You must be signed in to change notification settings - Fork 2.9k
Core: Fix API breakage introduced by #13191 #13386
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
Conversation
| import org.apache.hadoop.util.Preconditions; | ||
|
|
||
| class ParserContext { | ||
| public class ParserContext { |
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 class is being used in public API methods, therefore it should be public
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.
Yeah technically anyone can use the packaged HttpClient independently make a request on their own and pass through their own ParserContext. So from an API design perspective I agree, we should just make this public to enable people to be able to just build their own if they want. Iceberg implementation can take an internal opinionated approach for whatever client it initializes internally, but it's always available for a user to specify their own context if they so choose.
|
@RussellSpitzer @singhpk234 @amogh-jahagirdar can you guys please review this one? CI should be fixed once #13385 is in |
amogh-jahagirdar
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 catching and fixing this @nastra , in hindsight I agree there's really no reason that we need to break the execute API and we have a simple way to avoid it in this case.
| import org.apache.hadoop.util.Preconditions; | ||
|
|
||
| class ParserContext { | ||
| public class ParserContext { |
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.
Yeah technically anyone can use the packaged HttpClient independently make a request on their own and pass through their own ParserContext. So from an API design perspective I agree, we should just make this public to enable people to be able to just build their own if they want. Iceberg implementation can take an internal opinionated approach for whatever client it initializes internally, but it's always available for a user to specify their own context if they so choose.
| if (null != parserContext) { | ||
| throw new UnsupportedOperationException("Parser context is not supported"); | ||
| } | ||
|
|
||
| return execute(request, responseType, errorHandler, responseHeaders); |
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 is a more elegant way to completely avoid the API breakage.
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.
isn't the execute without parserContext still abstract ? except the parser context being package protected ?
protected abstract <T extends RESTResponse> T execute(
HTTPRequest request,
Class<T> responseType,
Consumer<ErrorResponse> errorHandler,
Consumer<Map<String, String>> responseHeaders);
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 the intention to just have one abstract method for execute ? I agree with the ParserContext being made public
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 the intention to just have one abstract method for execute
@singhpk234 I think it's more in general that when there's an opportunity to avoid public classes being forced to implement something on upgrade we should try do it that way since we can establish an opinionated default implementation; it's not always possible but here I think it is.
So specifically prior to this change client implementations were already forced to implement execute without the parser context, and after #13191 they would have to implement an additional execute. Since there was an easy way to avoid that by just having a non-abstract implementation which still just fails at runtime, it seems worth it to avoid any existing client implementations from having to just override it themselves.
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.
That make sense ! thank you for the explanation @amogh-jahagirdar !
| Consumer<Map<String, String>> responseHeaders); | ||
|
|
||
| protected abstract <T extends RESTResponse> T execute( | ||
| protected <T extends RESTResponse> T execute( |
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.
Will this actually stop a user from being broken on upgrade? If you extend this class and execute is called without the parserContext it still breaks but now you don't know that until runtime?
I may misunderstand the full range of possibilities here, but isn't the internal Iceberg SDK passing through the ParserContext here?
|
|
||
| static class Builder { | ||
| private Map<String, Object> data; | ||
| private final Map<String, Object> data; |
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.
wouldn't we doing a put on a final object then ? as part of add method, i intentionally removed the final 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.
the final applies to the actual map instance and doesn't imply that the map is immutable. Looking at the code again I don't think it actually works currently when the builder's add() method is called because data is an empty immutable map. @singhpk234 shouldn't the data map rather be just an empty mutable map?
- private final Map<String, Object> data;
-
- private Builder() {
- this.data = Collections.emptyMap();
- }
+ private final Map<String, Object> data = Maps.newHashMap();
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.
Yeah good eye, I think we would've caught this when implementing the actual client side changes for scan planning w tests, we wouldn't have any of the expected context passed through but it's good to catch this now.
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 as well, thanks for catching it and fix @nastra !
just have one minor comment on making the map final
singhpk234
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 @nastra really appreciate it !
RussellSpitzer
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.
I approve of this, I just was a little curious about the change.
|
Thanks @nastra , thanks @singhpk234 @RussellSpitzer @stevenzwu for reviewing. I'll go ahead and merge |
#13191 added an API breakage by introducing an abstract method to
BaseHTTPClient. We can actually avoid breaking the API in a similar manner as to how we did it historically in theBaseHTTPClient/RESTClientby throwing an UOE when the new parameter is actually set. Since subclasses already override/implement this new method, everything should work as expected.