-
Notifications
You must be signed in to change notification settings - Fork 21
[PECOBLR-450] Successfull identification of request type in the execute function #922
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
base: main
Are you sure you want to change the base?
Conversation
… for retry handling
…n the DatabricksHttpClient
* Helper method to set HTTP request configuration for volume operations based on operation type. | ||
* Only works with Thrift client connections; SDK clients use their own retry logic. | ||
*/ | ||
private void setVolumeOperationConfig(HTTPRequestType config) { |
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.
rename to setVolumeOperationRequestType
} | ||
} catch (Exception e) { | ||
// Catch all exceptions (SQLException, NullPointerException, etc.) | ||
// This ensures volume operations continue to work even if HTTP config setup fails |
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.
How will the retry logic work in this case?
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 wont work in this case, the request type will be NOT_SET which will be treated as non idempotent.
|
||
String listFilesSQLQuery = createListQuery(catalog, schema, volume, folder); | ||
|
||
setVolumeOperationConfig(HTTPRequestType.UC_VOLUME_LIST); |
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.
Better move this inside try block of createStatement. This indicates you are update request type just before sending request.
Applies to the entire class
|
||
boolean volumeOperationStatus = false; | ||
|
||
setVolumeOperationConfig(HTTPRequestType.UC_VOLUME_GET_TO); |
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 UC_VOLUME_GET_TO? should this be UC_VOLUME_GET?
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.
Yes
if (isAllowedInputStreamForVolumeOperation) { | ||
responseStream = databricksHttpClient.execute(httpGet); | ||
responseStream = | ||
databricksHttpClient.execute(httpGet, HTTPRequestType.DBFS_VOLUME_EXECUTE_GET); |
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 we can use same request types across UcVolumeClient and DBFSVolumeClient
UC_VOLUME_LIST, | ||
UC_VOLUME_SHOW_VOLUMES, | ||
UC_VOLUME_GET_TO, | ||
UC_VOLUME_PUT_INTO, | ||
UC_VOLUME_REMOVE, | ||
DBFS_VOLUME_EXECUTE_GET, | ||
DBFS_VOLUME_EXECUTE_PUT, | ||
DBFS_VOLUME_EXECUTE_DELETE, |
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.
seems like we are having redundant enums for dbfs and uc volume clients? Please unify
|
||
// enumerates the types of HTTP requests | ||
|
||
public enum RequestIdempotency { |
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.
Do we need 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.
No we dont need this now, altough a similar enum will be needed in Retry Handler. WIll delete this file.
@Override | ||
public CloseableHttpResponse execute(HttpUriRequest request, boolean supportGzipEncoding) | ||
public void setCurrentRequestType(HTTPRequestType currentHTTPRequestType) { | ||
if (this.currentHTTPRequestType == HTTPRequestType.NOT_SET) { |
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 mention in comments, in what case NOT_SET becomes important. Document the UC volumes example we discussed
|
||
TCancelOperationResp cancelOperation(TCancelOperationReq req) throws DatabricksHttpException { | ||
try { | ||
setHttpRequestType(HTTPRequestType.THRIFT_CANCEL_OPERATION); |
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.
redundant setting of request type. you've already set the request type in getThriftResponse
.setDefaultRequestConfig(makeRequestConfig(connectionContext.getSocketTimeout())) | ||
.setRetryHandler(retryHandler) | ||
.addInterceptorFirst(retryHandler); |
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 can't remove this until the new retry mechanism is ready. Please undo this chage
DatabricksHttpRetryHandler retryHandler = | ||
type.equals(HttpClientType.COMMON) | ||
? new DatabricksHttpRetryHandler(connectionContext) | ||
: new UCVolumeHttpRetryHandler(connectionContext); |
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 feel we can rid of HttpClientType now that we have request type.
|
||
@Override | ||
public CloseableHttpResponse execute( | ||
HttpUriRequest request, HTTPRequestType config, boolean supportGzipEncoding) |
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.
maintain consistent naming for params. requestType instead of config
public void setHttpRequestType(HTTPRequestType config) { | ||
this.httpClient.setCurrentRequestType(config); | ||
} |
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.
maintain consistent naming for params.
request.setHeader("Content-Encoding", "gzip"); | ||
} | ||
|
||
currentHTTPRequestType = HTTPRequestType.NOT_SET; |
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 state management feels a bit weird. Why are you updating to NOT_SET 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.
Because in setRequestType we are setting only when its previously NOT_SET
// Execute the request and handle the response | ||
long httpRequestStartTime = System.currentTimeMillis(); | ||
try (CloseableHttpResponse response = httpClient.execute(request)) { | ||
HTTPRequestType currentRequestConfig = httpClient.getCurrentRequestType(); |
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.
rename to currentRequestType
context.fetchAndSetFlagsFromServer(httpClientMock, request); | ||
assertTrue(context.isFeatureEnabled(FEATURE_FLAG_NAME)); | ||
verify(httpClientMock).execute(request); | ||
verify(httpClientMock).execute(eq(request), isA(HTTPRequestType.class)); |
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 we are not specifying the actual HTTPRequestType across tests?
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.
New tests for request Type verification are going to be added anyways, so didn't want to change the existing tests behaviour.
package com.databricks.jdbc.common; | ||
|
||
public enum HTTPRequestType { | ||
NOT_SET, |
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.
HTTPRequestType should be UNKNOWN instead of NOT_SET (I guess the weird nomenclature is because you're actually using RequestType as a state for the http client).
VOLUME_DELETE, | ||
AUTH, | ||
TELEMETRY_PUSH, | ||
OTHER |
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's this used for?
private final IDatabricksConnectionContext connectionContext; | ||
private IdleConnectionEvictor idleConnectionEvictor; | ||
private CloseableHttpAsyncClient asyncClient; | ||
private HTTPRequestType currentHTTPRequestType; |
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.
treating this as a class level property makes this class thread unsafe, this is a request level property and the implementation must be in accordance
public void setCurrentRequestType(HTTPRequestType currentHTTPRequestType) { | ||
/* The NOT_SET check is needed while executing UC volume requests, as they go through Thrift ExecuteStatement, | ||
So, this function will be called 2 times (from UC Volume Client as well as Thrift Client). In order to avoid that conflict, | ||
this check is required. |
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 seems like a hacky way of doing things to bypass the request type being set at the class level
REQUEST_IDEMPOTENCY_MAP.put(HTTPRequestType.VOLUME_PUT, RequestIdempotency.NON_IDEMPOTENT); | ||
REQUEST_IDEMPOTENCY_MAP.put(HTTPRequestType.NOT_SET, RequestIdempotency.NON_IDEMPOTENT); | ||
REQUEST_IDEMPOTENCY_MAP.put(HTTPRequestType.OTHER, RequestIdempotency.NON_IDEMPOTENT); | ||
} |
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.
can we define this mapping as a part of HTTPRequestType enum
throws DatabricksHttpException { | ||
|
||
HttpContext httpContext = new BasicHttpContext(); | ||
initializeRetryState(httpContext); |
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.
why do we maintain the retry counts/duration in the http context? what's the benefit of that?
Thread.currentThread().interrupt(); | ||
throw new RuntimeException("Thread interrupted during retry", e); | ||
} catch (RuntimeException e) { | ||
throwHttpException(e, 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.
throw RuntimeException for InterruptedException and HttpException for RuntimeException? what's the logic here?
|
||
public interface IRetryStrategy { | ||
/* Tells whether a given HTTP status code is retriable or not */ | ||
boolean isStatusCodeRetriable(int statusCode, IDatabricksConnectionContext connectionContext); |
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 should instead just do isRetriable, it should be oblivious to clients on why we chose to retry (status code, etc.), additionally, we should only retry if we have 503 + retry-after header, all those checks should be done by this class, take a look at: https://github.com/databricks/databricks-sdk-java/blob/d1de7b8a9a397fcf34e93120e43d8bc2f459d004/databricks-sdk-java/src/main/java/com/databricks/sdk/core/retry/RetryStrategy.java
|
||
// Last attempt check | ||
if (attempt > MAX_RETRIES) { | ||
return null; |
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.
why do we return null? should we just not return the last response?
HttpStatus.SC_REQUEST_TOO_LONG, | ||
HttpStatus.SC_REQUEST_URI_TOO_LONG, | ||
HttpStatus.SC_UNSUPPORTED_MEDIA_TYPE, | ||
HttpStatus.SC_REQUESTED_RANGE_NOT_SATISFIABLE); |
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.
do we also have non retryable exceptions?
Description
Implemented request identification by explicitly passing the request type (e.g. THRIFT_METADATA, UC_VOLUME_PUT_INTO) (an enum) in the DatabricksHttpClient execute function, for all clients (Thrift, UCVolume, DBFS, Telemetry, AWS)
Testing
The old tests are successfully executing. New tests are yet to be added.