-
Notifications
You must be signed in to change notification settings - Fork 242
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
Track latency, retry and backoff time of GCS JSON API requests #1207
Track latency, retry and backoff time of GCS JSON API requests #1207
Conversation
/gcbrun |
util/src/main/java/com/google/cloud/hadoop/util/GcsJsonApiEvent.java
Outdated
Show resolved
Hide resolved
} | ||
|
||
@VisibleForTesting | ||
GcsJsonApiEvent(@Nonnull HttpRequest request, EventType eventType) { |
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 not just extract the meaningful information from HttpRequest
and wrap it around RequestContext
. Doing that will make it easier to extend it for gRPC as well.
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.
For gRPC we can create another method or refactor this once we add 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.
gRPC implementation is already there are we are even moving towards GA. Ideally, we should add this support for gRPC as well. Least we can do is, any new code added should be extendable to gRPC as well, else we are just be pilling on the tech debt.
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.
gRPC support will come in a separate change.
|
||
private GcsJsonApiEvent(HttpRequest request, EventType eventType, int capacity) { | ||
this(request, eventType); | ||
this.properties = new HashMap<>(capacity); |
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 the purpose of setting capacity? Looking into the usage of it in getBackoffEvent
it seems it will immediately allocate extra capacity as well as rehash 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.
This is done to limit the size of the map. BackOffEvent has two additional properties, hence setting the capacity to 2.
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.
setting this capacity
will not limit the size of hashMap. It's actually not capacity but initialCapacity
https://docs.oracle.com/javase/8/docs/api/java/util/HashMap.html#HashMap-int-
There is no way you will be able to limit the size of hashMap unless ofcourse you customize and override the implementation.
Am I missing something 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.
If you provide the capacity, it will set the capacity to the next power of 2. It will not resize. What you think it will resize?
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 per the documentation When the number of entries in the hash table exceeds the product of the load factor and the current capacity, the hash table is rehashed
. With Default load factor of 0.75 it will rehash as soon as there are two entries in HashMap.
util/src/test/java/com/google/cloud/hadoop/util/RetryHttpInitializerTest.java
Outdated
Show resolved
Hide resolved
} | ||
|
||
@Override | ||
protected RequestTracker getRequestTracker(HttpRequest 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.
Correct me If I am wrong, With this override what we are able to use the same RequestTracker instance against all the gcs requests made in test case i.e. getMetadata, getObject, listObject etc, all the retires of the request are also captured via same tracer but in actual production scenario it will not be the case, every request will have a separate tracker.
If yes, will it even be a valid test then?
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, same tracker is used. Otherwise the test would have failed. Same things is happening in RetryHttpInitializer as well since since initialize is called only once.
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, same tracker will be used across all retries of same request, but if there are multiple requests, say two different request to access object metadata of object1 and object2. TestRetryHttpInitializer
configured here will not be able to provide different tracker but provide the same tracker which will yield weird outcomes at verification step.
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 sync offline.
util/src/test/java/com/google/cloud/hadoop/util/RetryHttpInitializerTest.java
Outdated
Show resolved
Hide resolved
util/src/main/java/com/google/cloud/hadoop/util/RetryHttpInitializer.java
Outdated
Show resolved
Hide resolved
/gcbrun |
/gcbrun |
|
||
private GcsJsonApiEvent(HttpRequest request, EventType eventType, int capacity) { | ||
this(request, eventType); | ||
this.properties = new HashMap<>(capacity); |
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.
setting this capacity
will not limit the size of hashMap. It's actually not capacity but initialCapacity
https://docs.oracle.com/javase/8/docs/api/java/util/HashMap.html#HashMap-int-
There is no way you will be able to limit the size of hashMap unless ofcourse you customize and override the implementation.
Am I missing something here?
} | ||
|
||
@VisibleForTesting | ||
GcsJsonApiEvent(@Nonnull HttpRequest request, EventType eventType) { |
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.
gRPC implementation is already there are we are even moving towards GA. Ideally, we should add this support for gRPC as well. Least we can do is, any new code added should be extendable to gRPC as well, else we are just be pilling on the tech debt.
import com.google.common.flogger.GoogleLogger; | ||
import java.util.concurrent.TimeUnit; | ||
|
||
class RequestTracker { |
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.
similar comment.
postToEventQueue(GcsJsonApiEvent.getExceptionEveent(httpRequest)); | ||
} | ||
|
||
void trackUnsuccessfulResponseHandler(HttpResponse 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.
trackResponse
is called from response interceptor and trackUnsuccessfulResponseHandler
is called from Unsuccessful response handler. I expect the responseInterceptor to be called no matter if response was unsuccessful. Weird.
stopWatch.stop(); | ||
|
||
if (stopWatch.elapsed().toMillis() > LOGGING_THRESHOLD) { | ||
logger.atInfo().atMostEvery(10, TimeUnit.SECONDS).log( |
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, but it's not capturing elapsedTime
correct?
} | ||
|
||
@Override | ||
protected RequestTracker getRequestTracker(HttpRequest 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.
Yeah, same tracker will be used across all retries of same request, but if there are multiple requests, say two different request to access object metadata of object1 and object2. TestRetryHttpInitializer
configured here will not be able to provide different tracker but provide the same tracker which will yield weird outcomes at verification step.
util/src/test/java/com/google/cloud/hadoop/util/TestRequestTracker.java
Outdated
Show resolved
Hide resolved
assertThat(backOffTime).isAtLeast(expectedBackoffTime); | ||
assertThat(backOffTime).isLessThan(expectedBackoffTime + 10); |
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 it varies as range?
How we came up with 10
?
will it cause flakiness?
what if some adds a test where many more retires are done, this verification will still be valid?
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 are unit tests, not actual backoff is happening. The buffer is the potential overhead. I do not expect it to be more than 10 seconds.
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 are unit tests, not actual backoff is happening.
Well, that is not always true, is it? One can mock the transport in unit tests but still use RetryHttpIniitalizer which comes with ExponentialBackOff strategy and hence the actual backoff will will happen.
/gcbrun |
/gcbrun |
1 similar comment
/gcbrun |
/gcbrun |
/gcbrun |
…' into gcs-api-metrics-3-0
/gcbrun |
1 similar comment
/gcbrun |
@@ -166,4 +246,23 @@ private static RetryHttpInitializer createRetryHttpInitializer(Credentials crede | |||
.setReadTimeout(Duration.ofSeconds(5)) | |||
.build()); | |||
} | |||
|
|||
// Helper class which help provide a custom test implementation of RequestTracker |
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 would recommend adding a note on limitation of TestRetryHttpInitializer
, which is, "it can only used against test with only one request because RequestTracker is initialized along with HttpInitializer".
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 an inner class and is an implementation detail. I think it is obvious from the code that this applies for one request per TestRetryHttpInitializer.
1ba5bc2
into
GoogleCloudDataproc:branch-3.0.x
…eCloudDataproc#1207) * Track latency, retry and backoff time of GCS JSON API requests
No description provided.