Skip to content

DRILL-7582: Moved Drillbits REST API communication to the back end layer#1999

Closed
agozhiy wants to merge 1 commit intoapache:masterfrom
agozhiy:DRILL-7582
Closed

DRILL-7582: Moved Drillbits REST API communication to the back end layer#1999
agozhiy wants to merge 1 commit intoapache:masterfrom
agozhiy:DRILL-7582

Conversation

@agozhiy
Copy link
Member

@agozhiy agozhiy commented Feb 26, 2020

DRILL-7582: Moved Drillbits REST API communication to the back end layer

Description

There is a list of Drillbits on the Web UI index page and some statistics, such as Heap Memory Usage, Direct Memory Usage, CPU Usage, Avg Sys Load, Uptime.
Retrieving these stats is implemented by javascript REST API calls from client side. This causes the following problems:

  • Requires all Drillbit hostnames to be be added to client hosts as they are used in the URLs.
  • Won't work with SSL/TLS enabled.
  • Won't work if the Drill cluster is running in isolated environment such as Kubernetes.
  • Won't work if Drill is running in docker and the Web port is remapped (e.g. -p 80:8047)

Now all these HTTP requests are sent by org.apache.http.impl.nio.client.CloseableHttpAsyncClient in back end.

Documentation

Statistics should be displayed in all cases.

Testing

Tested manually with SSL enabled/disabled as well as authentication.
Unit / Functional tests were passed.

* Build an URL of a remote Drillbit endpoint.
*
* @param work {@link WorkManager} instance needed to retrieve the Drillbit address.
* @param request {@link HttpServletRequest} instance needed to to set the URL schema.
Copy link
Member

Choose a reason for hiding this comment

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

Please remove repeated to

Copy link
Member Author

Choose a reason for hiding this comment

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

Done.

Comment on lines 86 to 89
.orElse(null);
if (drillbit == null) {
throw new RuntimeException(String.format("No such drillbit: %s", hostname));
}
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
.orElse(null);
if (drillbit == null) {
throw new RuntimeException(String.format("No such drillbit: %s", hostname));
}
.orElseThrow(() -> new RuntimeException("No such drillbit: " + hostname));

Copy link
Member Author

Choose a reason for hiding this comment

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

Done.

if (drillbit == null) {
throw new RuntimeException(String.format("No such drillbit: %s", hostname));
}
URL url;
Copy link
Member

Choose a reason for hiding this comment

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

No need to declare this variable, the value may be returned in the place where it is assigned.

Copy link
Member Author

Choose a reason for hiding this comment

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

Done.

try {
url = new URL(request.getScheme(), hostname, drillbit.getHttpPort(), path);
} catch (MalformedURLException e) {
throw new RuntimeException(e);
Copy link
Member

Choose a reason for hiding this comment

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

Is there any reason for wrapping the exception into RuntimeException here?

Copy link
Contributor

Choose a reason for hiding this comment

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

More broadly, we have a REST call asking for a table of values. Do we want that request to fail for issues such as this (or for HTTP timeouts or other errors)? My guess is we don't. So, maybe map this kind of exception to a single form of exception which can be a) logged, and b) mapped to a value of "Unavailable" in the UI.

Actually, the malformed URL is in the programmers' control (not the user's.) So, throwing an IllegalStateException might help indicate that this is a "should never occur" kind of error.

Still need to handle network timeouts, etc.

Copy link
Member Author

Choose a reason for hiding this comment

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

Changed to IllegalStateException.

}
URL url;
try {
url = new URL(request.getScheme(), hostname, drillbit.getHttpPort(), path);
Copy link
Member

Choose a reason for hiding this comment

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

Instead of obtaining DrillbitEndpoint and then obtaining its port, may be added additional map call into stream chain right after the filter:

.map(DrillbitEndpoint::getHttpPort)

Copy link
Member Author

Choose a reason for hiding this comment

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

Done.

localHttpClient = httpClient;
if (httpClient == null){
if (drillConfig.getBoolean(ExecConstants.HTTP_ENABLE_SSL)) {
SSLContext sslContext = SSLContexts.custom()
Copy link
Member

Choose a reason for hiding this comment

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

Wouldn't it be better to obtain this instance using SSLConfigBuilder/SSLConfigClient and specify SslContext obtained from here instead of specifying SSLStrategy?

fillQueryCount(i);
}
currentRow.find("#status").text(status_map[key]).css('font-style','').prop('title','');;
currentRow.find("#status").text(status_map[key]).css('font-style', '').prop('title', '');
Copy link
Member

Choose a reason for hiding this comment

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

Something bad with indentation here.

Copy link
Member Author

Choose a reason for hiding this comment

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

Fixed.

.filter(db -> db.getAddress().equals(hostname))
.findAny()
.orElse(null);
if (drillbit == null) {
Copy link
Contributor

Choose a reason for hiding this comment

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

This is a programming invariant check. Maybe use wither assert or Preconditions.

Copy link
Member Author

Choose a reason for hiding this comment

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

Changed to orElseThrow.

try {
url = new URL(request.getScheme(), hostname, drillbit.getHttpPort(), path);
} catch (MalformedURLException e) {
throw new RuntimeException(e);
Copy link
Contributor

Choose a reason for hiding this comment

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

More broadly, we have a REST call asking for a table of values. Do we want that request to fail for issues such as this (or for HTTP timeouts or other errors)? My guess is we don't. So, maybe map this kind of exception to a single form of exception which can be a) logged, and b) mapped to a value of "Unavailable" in the UI.

Actually, the malformed URL is in the programmers' control (not the user's.) So, throwing an IllegalStateException might help indicate that this is a "should never occur" kind of error.

Still need to handle network timeouts, etc.

}

/**
* Send an HTTP request and returns response body as String.
Copy link
Contributor

Choose a reason for hiding this comment

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

Parallel constuctin: "Send ... and return ..." (no "s" on "return").

Copy link
Member Author

Choose a reason for hiding this comment

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

Fixed.

* @throws Exception if unable to create HTTP client or in case of HTTP timeout.
*/
static String doHTTPRequest(HttpRequestBase httpRequest, DrillConfig drillConfig) throws Exception {
CloseableHttpAsyncClient httpClient = getHttpClient(drillConfig);
Copy link
Contributor

Choose a reason for hiding this comment

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

Reasonable timeout? Give Drillbit 2 seconds to respond, else display "Unavailable"?

if (localHttpClient == null) {
synchronized (WebUtils.class) {
localHttpClient = httpClient;
if (httpClient == null){
Copy link
Contributor

Choose a reason for hiding this comment

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

Nit: ){ --> ') {`.

More substantially, maybe separate the create-if-null hokey-pokey with actual creation. Call a method to build the client rather than doing it deeply nested here.

Copy link
Member Author

Choose a reason for hiding this comment

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

Done.

Copy link
Member

@vvysotskyi vvysotskyi left a comment

Choose a reason for hiding this comment

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

Thanks for making changes, +1

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants

Comments